diff --git a/bootloader/SOURCES b/bootloader/SOURCES
index 3e17338..9448515 100644
--- a/bootloader/SOURCES
+++ b/bootloader/SOURCES
@@ -69,4 +69,7 @@ main-pp.c
 show_logo.c
 #elif defined(MPIO_HD200) || defined(MPIO_HD300)
 mpio_hd200_hd300.c
+#elif defined(RK27_GENERIC)
+rk27xx.c
+show_logo.c
 #endif
diff --git a/bootloader/rk27xx.c b/bootloader/rk27xx.c
new file mode 100644
index 0000000..37778db
--- /dev/null
+++ b/bootloader/rk27xx.c
@@ -0,0 +1,45 @@
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "inttypes.h"
+#include "string.h"
+#include "cpu.h"
+#include "system.h"
+#include "lcd.h"
+#include "kernel.h"
+#include "thread.h"
+#include "backlight.h"
+#include "backlight-target.h"
+#include "font.h"
+#include "common.h"
+#include "version.h"
+
+extern int show_logo( void );
+void main(void)
+{
+
+    _backlight_init();
+
+    system_init();
+    kernel_init();
+    enable_irq();
+
+    lcd_init_device();
+    _backlight_on();
+    font_init();
+    lcd_setfont(FONT_SYSFIXED);
+
+    show_logo();
+    sleep(HZ*2);
+
+    while(1)
+    {
+        reset_screen();
+        printf("GPIOA: 0x%0x", GPIO_PADR);
+        printf("GPIOB: 0x%0x", GPIO_PBDR);
+        printf("GPIOC: 0x%0x", GPIO_PCDR);
+        printf("GPIOD: 0x%0x", GPIO_PDDR);
+        sleep(HZ/10);
+    }
+}
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 6d4ef9f..eee0c86 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -210,6 +210,9 @@ target/arm/ata-nand-telechips.c
 #if (CONFIG_STORAGE & STORAGE_NAND) && (CONFIG_NAND == NAND_SAMSUNG)
 target/arm/s5l8700/ata-nand-s5l8700.c
 #endif
+#if (CONFIG_STORAGE & STORAGE_NAND) && (CONFIG_NAND == NAND_RK27XX)
+target/arm/rk27xx/ata-nand-rk27xx.c
+#endif
 #if (CONFIG_STORAGE & STORAGE_ATA) && !defined(IPOD_6G)
 drivers/ata.c
 #endif
@@ -473,6 +476,8 @@ target/arm/i2c-telechips.c
 target/arm/s5l8700/i2c-s5l8700.c
 #elif CONFIG_I2C == I2C_S5L8702
 target/arm/s5l8702/i2c-s5l8702.c
+#elif CONFIG_I2C == I2C_RK27XX
+target/arm/rk27xx/i2c-rk27xx.c
 #endif
 
 #if CONFIG_CPU == PNX0101
@@ -562,6 +567,8 @@ target/arm/s5l8700/crt0.S
 target/arm/s5l8702/crt0.S
 #elif CONFIG_CPU==IMX233
 target/arm/imx233/crt0.S
+#elif CONFIG_CPU==RK27XX
+target/arm/rk27xx/crt0.S
 #elif defined(CPU_ARM)
 target/arm/crt0.S
 #endif /* defined(CPU_*) */
@@ -1605,6 +1612,23 @@ target/arm/s5l8702/ipod6g/adc-ipod6g.c
 #endif
 
 #ifndef SIMULATOR
+#if CONFIG_CPU == RK27XX
+target/arm/rk27xx/kernel-rk27xx.c
+target/arm/rk27xx/system-rk27xx.c
+target/arm/rk27xx/timer-rk27xx.c
+target/arm/rk27xx/backlight-rk27xx.c
+target/arm/rk27xx/lcd-rk27xx.c
+target/arm/rk27xx/adc-rk27xx.c
+target/arm/rk27xx/sd-rk27xx.c
+target/arm/rk27xx/ftl-rk27xx.c
+#endif
+
+#if defined(RK27_GENERIC)
+target/arm/rk27xx/rk27generic/button-rk27generic.c
+#endif
+#endif
+
+#ifndef SIMULATOR
 #if CONFIG_CPU == JZ4732
 target/mips/ingenic_jz47xx/ata-nand-jz4740.c
 target/mips/ingenic_jz47xx/ata-sd-jz4740.c
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 003f683..2c7c6e8 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -75,6 +75,7 @@
 #define AT91SAM9260  9260
 #define AS3525v2    35252
 #define IMX233        233
+#define RK27XX       2700
 
 /* platforms
  * bit fields to allow PLATFORM_HOSTED to be OR'ed e.g. with a
@@ -222,6 +223,7 @@
 #define LCD_VIBE500   39 /* as used by the Packard Bell Vibe 500 */
 #define LCD_IPOD6G    40 /* as used by the iPod Nano 2nd Generation */
 #define LCD_FUZEPLUS  41
+#define LCD_SPFD5420A 42 /* rk27xx */
 
 /* LCD_PIXELFORMAT */
 #define HORIZONTAL_PACKING 1
@@ -260,6 +262,7 @@ Lyre prototype 1 */
 #define I2C_AS3525  15
 #define I2C_S5L8702 16 /* Same as S5L8700, but with two channels */
 #define I2C_IMX233  17
+#define I2C_RK27XX  18
 
 /* CONFIG_LED */
 #define LED_REAL     1 /* SW controlled LED (Archos recorders, player) */
@@ -271,6 +274,7 @@ Lyre prototype 1 */
 #define NAND_TCC     2
 #define NAND_SAMSUNG 3
 #define NAND_CC      4 /* ChinaChip */
+#define NAND_RK27XX  5
 
 /* CONFIG_RTC */
 #define RTC_M41ST84W 1 /* Archos Recorder */
@@ -448,6 +452,8 @@ Lyre prototype 1 */
 #include "config/mpiohd200.h"
 #elif defined(MPIO_HD300)
 #include "config/mpiohd300.h"
+#elif defined(RK27_GENERIC)
+#include "config/rk27generic.h"
 #elif defined(SDLAPP)
 #include "config/sdlapp.h"
 #elif defined(ANDROID)
@@ -533,8 +539,8 @@ Lyre prototype 1 */
 
 #elif defined(CPU_TCC77X) || defined(CPU_TCC780X) || (CONFIG_CPU == DM320) \
   || (CONFIG_CPU == AT91SAM9260) || (CONFIG_CPU == AS3525v2) \
-  || (CONFIG_CPU == S5L8702) || (CONFIG_PLATFORM & PLATFORM_ANDROID) \
-  || (CONFIG_CPU == IMX233)
+  || (CONFIG_CPU == S5L8702) || (CONFIG_CPU == IMX233) \
+  || (CONFIG_CPU == RK27XX) ||(CONFIG_PLATFORM & PLATFORM_ANDROID)
 #define CPU_ARM
 #define ARM_ARCH 5 /* ARMv5 */
 
@@ -774,7 +780,8 @@ Lyre prototype 1 */
 #if defined(HAVE_USBSTACK) || (CONFIG_CPU == JZ4732) \
     || (CONFIG_CPU == AS3525) || (CONFIG_CPU == AS3525v2) \
     || defined(CPU_S5L870X) || (CONFIG_CPU == S3C2440) \
-    || defined(APPLICATION) || (CONFIG_CPU == PP5002)
+    || defined(APPLICATION) || (CONFIG_CPU == PP5002) \
+    || (CONFIG_CPU == RK27XX)
 #define HAVE_SEMAPHORE_OBJECTS
 #endif
 
diff --git a/firmware/export/config/rk27generic.h b/firmware/export/config/rk27generic.h
new file mode 100644
index 0000000..5427d1f
--- /dev/null
+++ b/firmware/export/config/rk27generic.h
@@ -0,0 +1,175 @@
+/*
+ * This config file is for Rockchip rk27xx reference design
+ */
+#define TARGET_TREE /* this target is using the target tree system */
+
+/* For Rolo and boot loader */
+#define MODEL_NUMBER 78
+
+#define MODEL_NAME   "Rockchip 27xx generic"
+
+/* define this if you have recording possibility */
+/* #define HAVE_RECORDING */
+
+/* Define bitmask of input sources - recordable bitmask can be defined
+   explicitly if different */
+#define INPUT_SRC_CAPS (SRC_CAP_LINEIN)
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS   (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 \
+                       | SAMPR_CAP_48 | SAMPR_CAP_24 | SAMPR_CAP_12 \
+                       | SAMPR_CAP_32 | SAMPR_CAP_16 | SAMPR_CAP_8)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS  (SAMPR_CAP_44 | SAMPR_CAP_22 | SAMPR_CAP_11 \
+                       | SAMPR_CAP_48 | SAMPR_CAP_24 | SAMPR_CAP_12 \
+                       | SAMPR_CAP_32 | SAMPR_CAP_16 | SAMPR_CAP_8)
+
+/* define this if you have a bitmap LCD display */
+#define HAVE_LCD_BITMAP
+
+/* define this if you can flip your LCD */
+/* #define HAVE_LCD_FLIP */
+
+/* define this if you have a colour LCD */
+#define HAVE_LCD_COLOR
+
+/* define this if you want album art for this target */
+#define HAVE_ALBUMART
+
+/* define this to enable bitmap scaling */
+#define HAVE_BMP_SCALING
+
+/* define this to enable JPEG decoding */
+#define HAVE_JPEG
+
+/* define this if you can invert the colours on your LCD */
+/* #define HAVE_LCD_INVERT */
+
+/* define this if you have access to the quickscreen */
+#define HAVE_QUICKSCREEN
+
+/* define this if you have access to the pitchscreen */
+#define HAVE_PITCHSCREEN
+
+/* define this if you would like tagcache to build on this target */
+#define HAVE_TAGCACHE
+
+/* define this if you have a flash memory storage */
+#define HAVE_FLASH_STORAGE
+
+#define CONFIG_STORAGE (STORAGE_SD | STORAGE_NAND)
+
+#define CONFIG_NAND NAND_RK27XX
+
+/* commented for now */
+/* #define HAVE_HOTSWAP */
+
+#define NUM_DRIVES 2
+#define SECTOR_SIZE 512
+
+/* for small(ish) SD cards */
+#define HAVE_FAT16SUPPORT
+
+/* LCD dimensions */
+#define LCD_WIDTH  400
+#define LCD_HEIGHT 240
+#define LCD_DEPTH  16   /* pseudo 262.144 colors */
+#define LCD_PIXELFORMAT RGB565 /* rgb565 */
+
+/* Define this if the LCD can shut down */
+/* #define HAVE_LCD_SHUTDOWN */
+
+/* Define this if your LCD can be enabled/disabled */
+/* #define HAVE_LCD_ENABLE */
+
+/* Define this if your LCD can be put to sleep. HAVE_LCD_ENABLE
+   should be defined as well. */
+#ifndef BOOTLOADER
+/* TODO: #define HAVE_LCD_SLEEP */
+/* TODO: #define HAVE_LCD_SLEEP_SETTING */
+#endif
+
+#define CONFIG_KEYPAD IPOD_4G_PAD
+
+/* Define this to enable morse code input */
+#define HAVE_MORSE_INPUT
+
+/* Define this if you do software codec */
+#define CONFIG_CODEC SWCODEC
+
+/* define this if you have a real-time clock */
+/* #define CONFIG_RTC RTC_NANO2G */
+
+/* Define if the device can wake from an RTC alarm */
+/* #define HAVE_RTC_ALARM */
+
+#define CONFIG_LCD LCD_SPFD5420A
+
+/* Define the type of audio codec */
+/* #define HAVE_RK27XX_CODEC */
+
+/* #define HAVE_PCM_DMA_ADDRESS */
+
+/* Define this for LCD backlight available */
+#define HAVE_BACKLIGHT
+
+/* Doesn't work so comment it out for now */
+/* #define HAVE_BACKLIGHT_BRIGHTNESS */
+
+/* Define this if you have a software controlled poweroff */
+#define HAVE_SW_POWEROFF
+
+/* The number of bytes reserved for loadable codecs */
+#define CODEC_SIZE 0x100000
+
+/* The number of bytes reserved for loadable plugins */
+#define PLUGIN_BUFFER_SIZE 0x80000
+
+/* TODO: Figure out real values */
+#define BATTERY_CAPACITY_DEFAULT 400 /* default battery capacity */
+#define BATTERY_CAPACITY_MIN     300 /* min. capacity selectable */
+#define BATTERY_CAPACITY_MAX     500 /* max. capacity selectable */
+#define BATTERY_CAPACITY_INC      10 /* capacity increment */
+#define BATTERY_TYPES_COUNT        1 /* only one type */
+
+/* Hardware controlled charging with monitoring */
+#define CONFIG_CHARGING CHARGING_MONITOR
+
+/* define current usage levels */
+/* TODO: #define CURRENT_NORMAL
+ * TODO: #define CURRENT_BACKLIGHT  23
+ */
+
+/* define this if the unit can be powered or charged via USB */
+#define HAVE_USB_POWER
+
+/* Define this if your LCD can set contrast */
+/* #define HAVE_LCD_CONTRAST */
+
+/* The exact type of CPU */
+#define CONFIG_CPU RK27XX
+
+/* I2C interface */
+#define CONFIG_I2C I2C_RK27XX
+
+/* Define this to the CPU frequency */
+#define CPU_FREQ        200000000
+
+/* define this if the hardware can be powered off while charging */
+#define HAVE_POWEROFF_WHILE_CHARGING
+
+/* Offset ( in the firmware file's header ) to the file CRC */
+#define FIRMWARE_OFFSET_FILE_CRC 0
+
+/* Offset ( in the firmware file's header ) to the real data */
+#define FIRMWARE_OFFSET_FILE_DATA 8
+
+#define STORAGE_NEEDS_ALIGN
+
+/* Define this if you have adjustable CPU frequency */
+/* #define HAVE_ADJUSTABLE_CPU_FREQ */
+
+#define BOOTFILE_EXT "rk27"
+#define BOOTFILE "rockbox." BOOTFILE_EXT
+#define BOOTDIR "/.rockbox"
diff --git a/firmware/export/cpu.h b/firmware/export/cpu.h
index 777e36f..7405319 100644
--- a/firmware/export/cpu.h
+++ b/firmware/export/cpu.h
@@ -77,3 +77,6 @@
 #if CONFIG_CPU == IMX233
 #include "imx233.h"
 #endif
+#if CONFIG_CPU == RK27XX
+#include "rk27xx.h"
+#endif
diff --git a/firmware/export/i2c-rk27xx.h b/firmware/export/i2c-rk27xx.h
new file mode 100644
index 0000000..96baf56
--- /dev/null
+++ b/firmware/export/i2c-rk27xx.h
@@ -0,0 +1,32 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2009 by Bertrik Sikken
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _I2C_RK27XX_H
+#define _I2C_RK27XX_H
+
+#include "config.h"
+
+void i2c_init(void);
+int i2c_write(unsigned char slave, int address, int len, const unsigned char *data);
+int i2c_read(unsigned char slave, int address, int len, unsigned char *data);
+
+#endif /* _I2C_RK27XX_H */
+
diff --git a/firmware/export/rk27xx.h b/firmware/export/rk27xx.h
new file mode 100644
index 0000000..a5cd396
--- /dev/null
+++ b/firmware/export/rk27xx.h
@@ -0,0 +1,1022 @@
+/* ARM part only for now */
+#define AHB_SRAM 0x00000000
+
+#define ARM_BUS0_BASE 0x18000000
+#define ARM_BUS1_BASE 0x18400000
+
+#define FLASH_BANK0 0x10000000
+#define FLASH_BANK1 0x11000000
+
+/* Timers */
+#define APB0_TIMER             (ARM_BUS0_BASE + 0x00000000)
+#define TMR0LR                 (*(volatile unsigned long *)(APB0_TIMER + 0x00))
+#define TMR0CVR                (*(volatile unsigned long *)(APB0_TIMER + 0x04))
+#define TMR0CON                (*(volatile unsigned long *)(APB0_TIMER + 0x08))
+
+#define TMR1LR                 (*(volatile unsigned long *)(APB0_TIMER + 0x10))
+#define TMR1CVR                (*(volatile unsigned long *)(APB0_TIMER + 0x14))
+#define TMR1CON                (*(volatile unsigned long *)(APB0_TIMER + 0x18))
+
+#define TMR2LR                 (*(volatile unsigned long *)(APB0_TIMER + 0x20))
+#define TMR2CVR                (*(volatile unsigned long *)(APB0_TIMER + 0x24))
+#define TMR2CON                (*(volatile unsigned long *)(APB0_TIMER + 0x28))
+
+/* UART0  */
+#define APB0_UART0             (ARM_BUS0_BASE + 0x00004000)
+#define UART0_RBR              (*(volatile unsigned long *)(APB0_UART0 + 0x00))
+#define UART0_THR              (*(volatile unsigned long *)(APB0_UART0 + 0x00))
+#define UART0_DLL              (*(volatile unsigned long *)(APB0_UART0 + 0x00))
+#define UART0_DLH              (*(volatile unsigned long *)(APB0_UART0 + 0x04))
+#define UART0_IER              (*(volatile unsigned long *)(APB0_UART0 + 0x04))
+#define UART0_IIR              (*(volatile unsigned long *)(APB0_UART0 + 0x08))
+#define UART0_FCR              (*(volatile unsigned long *)(APB0_UART0 + 0x08))
+#define UART0_LCR              (*(volatile unsigned long *)(APB0_UART0 + 0x0C))
+#define UART0_MCR              (*(volatile unsigned long *)(APB0_UART0 + 0x10))
+#define UART0_LSR              (*(volatile unsigned long *)(APB0_UART0 + 0x14))
+#define UART0_MSR              (*(volatile unsigned long *)(APB0_UART0 + 0x18))
+
+/* UART1 */
+#define APB0_UART1             (ARM_BUS0_BASE + 0x00008000)
+#define UART1_RBR              (*(volatile unsigned long *)(APB0_UART1 + 0x00))
+#define UART1_THR              (*(volatile unsigned long *)(APB0_UART1 + 0x00))
+#define UART1_DLL              (*(volatile unsigned long *)(APB0_UART1 + 0x00))
+#define UART1_DLH              (*(volatile unsigned long *)(APB0_UART1 + 0x04))
+#define UART1_IER              (*(volatile unsigned long *)(APB0_UART1 + 0x04))
+#define UART1_IIR              (*(volatile unsigned long *)(APB0_UART1 + 0x08))
+#define UART1_FCR              (*(volatile unsigned long *)(APB0_UART1 + 0x08))
+#define UART1_LCR              (*(volatile unsigned long *)(APB0_UART1 + 0x0C))
+#define UART1_MCR              (*(volatile unsigned long *)(APB0_UART1 + 0x10))
+#define UART1_LSR              (*(volatile unsigned long *)(APB0_UART1 + 0x14))
+#define UART1_MSR              (*(volatile unsigned long *)(APB0_UART1 + 0x18))
+
+/* GPIO ports A,B,C,D */
+#define APB0_GPIO0             (ARM_BUS0_BASE + 0x0000C000)
+#define GPIO_PADR              (*(volatile unsigned long *)(APB0_GPIO0 + 0x00))
+#define GPIO_PACON             (*(volatile unsigned long *)(APB0_GPIO0 + 0x04))
+#define GPIO_PBDR              (*(volatile unsigned long *)(APB0_GPIO0 + 0x08))
+#define GPIO_PBCON             (*(volatile unsigned long *)(APB0_GPIO0 + 0x0C))
+#define GPIO_PCDR              (*(volatile unsigned long *)(APB0_GPIO0 + 0x10))
+#define GPIO_PCCON             (*(volatile unsigned long *)(APB0_GPIO0 + 0x14))
+#define GPIO_PDDR              (*(volatile unsigned long *)(APB0_GPIO0 + 0x18))
+#define GPIO_PDCON             (*(volatile unsigned long *)(APB0_GPIO0 + 0x1C))
+#define GPIO_TEST              (*(volatile unsigned long *)(APB0_GPIO0 + 0x20))
+#define GPIO_IEA               (*(volatile unsigned long *)(APB0_GPIO0 + 0x24))
+#define GPIO_IEB               (*(volatile unsigned long *)(APB0_GPIO0 + 0x28))
+#define GPIO_IEC               (*(volatile unsigned long *)(APB0_GPIO0 + 0x2C))
+#define GPIO_IED               (*(volatile unsigned long *)(APB0_GPIO0 + 0x30))
+#define GPIO_ISA               (*(volatile unsigned long *)(APB0_GPIO0 + 0x34))
+#define GPIO_ISB               (*(volatile unsigned long *)(APB0_GPIO0 + 0x38))
+#define GPIO_ISC               (*(volatile unsigned long *)(APB0_GPIO0 + 0x3C))
+#define GPIO_ISD               (*(volatile unsigned long *)(APB0_GPIO0 + 0x40))
+#define GPIO_IBEA              (*(volatile unsigned long *)(APB0_GPIO0 + 0x44))
+#define GPIO_IBEB              (*(volatile unsigned long *)(APB0_GPIO0 + 0x48))
+#define GPIO_IBEC              (*(volatile unsigned long *)(APB0_GPIO0 + 0x4C))
+#define GPIO_IBED              (*(volatile unsigned long *)(APB0_GPIO0 + 0x50))
+#define GPIO_IEVA              (*(volatile unsigned long *)(APB0_GPIO0 + 0x54))
+#define GPIO_IEVB              (*(volatile unsigned long *)(APB0_GPIO0 + 0x58))
+#define GPIO_IEVC              (*(volatile unsigned long *)(APB0_GPIO0 + 0x5C))
+#define GPIO_IEVD              (*(volatile unsigned long *)(APB0_GPIO0 + 0x60))
+#define GPIO_ICA               (*(volatile unsigned long *)(APB0_GPIO0 + 0x64))
+#define GPIO_ICB               (*(volatile unsigned long *)(APB0_GPIO0 + 0x68))
+#define GPIO_ICC               (*(volatile unsigned long *)(APB0_GPIO0 + 0x6C))
+#define GPIO_ICD               (*(volatile unsigned long *)(APB0_GPIO0 + 0x70))
+#define GPIO_ISR               (*(volatile unsigned long *)(APB0_GPIO0 + 0x74))
+
+/* Watchdog */
+#define APB0_WDT               (ARM_BUS0_BASE + 0x00010000)
+#define WDTLR                  (*(volatile unsigned long *)(APB0_WDT + 0x00))
+#define WDTCVR                 (*(volatile unsigned long *)(APB0_WDT + 0x04))
+#define WDTCON                 (*(volatile unsigned long *)(APB0_WDT + 0x08))
+
+/* RTC module documentation missing */
+#define APB0_RTC               (ARM_BUS0_BASE + 0x00014000)
+#define RTC_TIME               (*(volatile unsigned long *)(APB0_RTC + 0x00))
+#define RTC_DATE               (*(volatile unsigned long *)(APB0_RTC + 0x04))
+#define RTC_TALARM             (*(volatile unsigned long *)(APB0_RTC + 0x08))
+#define RTC_DALARM             (*(volatile unsigned long *)(APB0_RTC + 0x0C))
+#define RTC_CTRL               (*(volatile unsigned long *)(APB0_RTC + 0x10))
+#define RTC_RESET              (*(volatile unsigned long *)(APB0_RTC + 0x14))
+#define RTC_PWOFF              (*(volatile unsigned long *)(APB0_RTC + 0x18))
+#define RTC_PWFAIL             (*(volatile unsigned long *)(APB0_RTC + 0x1C))
+
+/* SPI */
+#define APB0_SPI               (ARM_BUS0_BASE + 0x00018000)
+#define SPI_TXR                (*(volatile unsigned long *)(APB0_SPI + 0x00))
+#define SPI_RXR                (*(volatile unsigned long *)(APB0_SPI + 0x00))
+#define SPI_IER                (*(volatile unsigned long *)(APB0_SPI + 0x04))
+#define SPI_FCR                (*(volatile unsigned long *)(APB0_SPI + 0x08))
+#define SPI_FWCR               (*(volatile unsigned long *)(APB0_SPI + 0x0C))
+#define SPI_DLYCR              (*(volatile unsigned long *)(APB0_SPI + 0x10))
+#define SPI_TXCR               (*(volatile unsigned long *)(APB0_SPI + 0x14))
+#define SPI_RXCR               (*(volatile unsigned long *)(APB0_SPI + 0x18))
+#define SPI_SSCR               (*(volatile unsigned long *)(APB0_SPI + 0x1C))
+#define SPI_ISR                (*(volatile unsigned long *)(APB0_SPI + 0x20))
+
+/* SCU module */
+#define APB0_SCU               (ARM_BUS0_BASE + 0x0001C000)
+#define SCU_ID                 (*(volatile unsigned long *)(APB0_SCU + 0x00))
+#define SCU_REMAP              (*(volatile unsigned long *)(APB0_SCU + 0x04))
+#define SCU_PLLCON1            (*(volatile unsigned long *)(APB0_SCU + 0x08))
+#define SCU_PLLCON2            (*(volatile unsigned long *)(APB0_SCU + 0x0C))
+#define SCU_PLLCON3            (*(volatile unsigned long *)(APB0_SCU + 0x10))
+#define SCU_DIVCON1            (*(volatile unsigned long *)(APB0_SCU + 0x14))
+#define SCU_CLKCFG             (*(volatile unsigned long *)(APB0_SCU + 0x18))
+#define SCU_RSTCFG             (*(volatile unsigned long *)(APB0_SCU + 0x1C))
+#define SCU_PWM                (*(volatile unsigned long *)(APB0_SCU + 0x20))
+#define SCU_CPUPD              (*(volatile unsigned long *)(APB0_SCU + 0x24))
+#define SCU_CHIPCFG            (*(volatile unsigned long *)(APB0_SCU + 0x28))
+#define SCU_STATUS             (*(volatile unsigned long *)(APB0_SCU + 0x2C))
+#define SCU_IOMUXA_CON         (*(volatile unsigned long *)(APB0_SCU + 0x30))
+/* 20 - 31 reserved */
+#define IOMUX_I2S_PAD          (1<<19)
+#define IOMUX_I2S_CODEC        (0<<19)
+#define IOMUX_I2C_PAD          (1<<18)
+#define IOMUX_I2C_CODEC        (0<<18)
+#define IOMUX_GPIO_B7          (2<<16)
+#define IOMUX_NAND_CS3         (1<<16)
+#define IOMUX_I2C_SDA          (0<<16)
+#define IOMUX_GPIO_B6          (2<<14)
+#define IOMUX_NAND_CS2         (1<<14)
+#define IOMUX_I2C_SCL          (0<<14)
+#define IOMUX_SPI              (2<<12)
+#define IOMUX_SD               (1<<12)
+#define IOMUX_GPIO_B05         (0<<12)
+#define IOMUX_LCD_VSYNC        (1<<11)
+#define IOMUX_GPIO_A7          (0<<11)
+#define IOMUX_LCD_DEN          (1<<10)
+#define IOMUX_GPIO_A6          (0<<10)
+#define IOMUX_NAND_CS1         (1<<9)
+#define IOMUX_GPIO_A5          (0<<9)
+#define IOMUX_LCD_D22          (1<<8)
+#define IOMUX_GPIO_A4          (0<<8)
+#define IOMUX_UART0_NRTS       (2<<6)
+#define IOMUX_LCD_D20          (1<<6)
+#define IOMUX_GPIO_A3          (0<<6)
+#define IOMUX_UART0_NCTS       (2<<4)
+#define IOMUX_LCD_D18          (1<<4)
+#define IOMUX_GPIO_A2          (0<<4)
+#define IOMUX_UART0_TXD        (2<<2)
+#define IOMUX_LCD_D17          (1<<2)
+#define IOMUX_GPIO_A1          (0<<2)
+#define IOMUX_UART0_RXD        (2<<0)
+#define IOMUX_LCD_D16          (1<<0)
+#define IOMUX_GPIO_A0          (0<<0)                 
+
+#define SCU_IOMUXB_CON         (*(volatile unsigned long *)(APB0_SCU + 0x34))
+/* bits 31 - 23 reserved */
+#define IOMUX_HADC             (1<<22)
+#define IOMUX_VIP              (0<<22)
+#define IOMUX_SDRAM_CKE        (1<<21)
+#define IOMUX_GPIO_D3          (0<<21)
+#define IOMUX_UHC_VBUS         (1<<20)
+#define IOMUX_GPIO_F4          (0<<20)
+#define IOMUX_UHC_OCUR         (1<<19)
+#define IOMUX_GPIO_F3          (0<<19)
+#define IOMUX_GPIO_F2          (1<<18)
+#define IOMUX_SDRAM_A12        (0<<18)
+#define IOMUX_GPIO_F1          (1<<17)
+#define IOMUX_SDRAM_A11        (0<<17)
+#define IOMUX_VIP_CLK          (1<<16)
+#define IOMUX_GPIO_F0          (0<<16)
+#define IOMUX_LCD_D815         (1<<15)
+#define IOMUX_GPIO_E07         (0<<15)
+#define IOMUX_PWM3             (1<<14)
+#define IOMUX_GPIO_D7          (0<<14)
+#define IOMUX_PWM2             (1<<13)
+#define IOMUX_GPIO_D6          (0<<13)
+#define IOMUX_PWM1             (1<<12)
+#define IOMUX_GPIO_D5          (0<<12)
+#define IOMUX_PWM0             (1<<11)
+#define IOMUX_GPIO_D4          (0<<11)
+#define IOMUX_SD_WPA           (1<<10)
+#define IOMUX_GPIO_D2          (0<<10)
+#define IOMUX_UART1_RXD        (2<<8)
+#define IOMUX_SD_CDA           (1<<8)
+#define IOMUX_GPIO_D1          (0<<8)
+#define IOMUX_UART1_TXD        (2<<6)
+#define IOMUX_SD_PCA           (1<<6)
+#define IOMUX_GPIO_D0          (0<<6)
+#define IOMUX_STMEM_CS1        (1<<5)
+#define IOMUX_GPIO_C7          (0<<5)
+#define IOMUX_I2S_CLK          (1<<4)
+#define IOMUX_GPIO_C6          (0<<4)
+#define IOMUX_I2S_SDO          (1<<3)
+#define IOMUX_GPIO_C5          (0<<3)
+#define IOMUX_I2S_SDI          (1<<2)
+#define IOMUX_GPIO_C4          (0<<2)
+#define IOMUX_I2S_LRCK         (1<<1)
+#define IOMUX_GPIO_C3          (0<<1)
+#define IOMUX_I2S_SCLK         (1<<0)
+#define IOMUX_GPIO_C2          (0<<0)
+
+#define SCU_GPIOUPCON          (*(volatile unsigned long *)(APB0_SCU + 0x38))
+#define SCU_DIVCON2            (*(volatile unsigned long *)(APB0_SCU + 0x3C))
+
+/* I2C controller */
+#define APB0_I2C               (ARM_BUS0_BASE + 0x00020000)
+#define I2C_MTXR               (*(volatile unsigned long *)(APB0_I2C + 0x00))
+#define I2C_MRXR               (*(volatile unsigned long *)(APB0_I2C + 0x04))
+#define I2C_STXR               (*(volatile unsigned long *)(APB0_I2C + 0x08))
+#define I2C_SRXR               (*(volatile unsigned long *)(APB0_I2C + 0x0C))
+#define I2C_SADDR              (*(volatile unsigned long *)(APB0_I2C + 0x10))
+#define I2C_IER                (*(volatile unsigned long *)(APB0_I2C + 0x14))
+#define I2C_ISR                (*(volatile unsigned long *)(APB0_I2C + 0x18))
+#define I2C_LCMR               (*(volatile unsigned long *)(APB0_I2C + 0x1C))
+#define I2C_LSR                (*(volatile unsigned long *)(APB0_I2C + 0x20))
+#define I2C_CONR               (*(volatile unsigned long *)(APB0_I2C + 0x24))
+#define I2C_OPR                (*(volatile unsigned long *)(APB0_I2C + 0x28))
+
+/* SD card controller */
+#define APB0_SD                (ARM_BUS0_BASE + 0x00024000)
+#define MMU_CTRL               (*(volatile unsigned long *)(APB0_SD + 0x00))
+#define MMU_BIG_ENDIAN         (1<<12)
+#define MMU_DMA_START          (1<<11)
+#define MMU_DMA_WRITE          (1<<10)
+#define MMU_MMU0_BUFI          (0<<9)
+#define MMU_MMU0_BUFII         (1<<9)
+#define MMU_CPU_BUFI           (0<<8)
+#define MMU_CPU_BUFII          (1<<8)
+#define MMU_BUFII_RESET        (1<<7)
+#define MMU_BUFII_END          (1<<6)
+#define MMU_BUFII_BYTE         (0<<4)
+#define MMU_BUFII_HALFWORD     (1<<4)
+#define MMU_BUFII_WORD         (3<<4)
+#define MMU_BUFI_RESET         (1<<3)
+#define MMU_BUFI_END           (1<<2)
+#define MMU_BUFI_BYTE          (0<<0)
+#define MMU_BUFI_HALFWORD      (1<<0)
+#define MMU_BUFI_WORD          (3<<0)
+
+#define MMU_PNRI               (*(volatile unsigned long *)(APB0_SD + 0x04))
+#define CUR_PNRI               (*(volatile unsigned long *)(APB0_SD + 0x08))
+#define MMU_PNRII              (*(volatile unsigned long *)(APB0_SD + 0x0C))
+#define CUR_PNRII              (*(volatile unsigned long *)(APB0_SD + 0x10))
+#define MMU_ADDR               (*(volatile unsigned long *)(APB0_SD + 0x14))
+#define CUR_ADDR               (*(volatile unsigned long *)(APB0_SD + 0x18))
+#define MMU_DATA               (*(volatile unsigned long *)(APB0_SD + 0x1C))
+
+#define SD_CTRL                (*(volatile unsigned long *)(APB0_SD + 0x20))
+#define SD_PWR_CD              (1<<13)
+#define SD_PWR_CPU             (0<<13)
+#define SD_DETECT_CDDAT3       (1<<12)
+#define SD_DETECT_MECH         (0<<12)
+#define SD_CLOCK_DIS           (1<<11)
+#define SD_CLOCK_EN            (0<<11)
+#define SD_DIV(x)              ((x)&0x7ff)
+                 
+#define SD_INT                 (*(volatile unsigned long *)(APB0_SD + 0x24))
+#define CMD_RES_STAT           (1<<6)
+#define DATA_XFER_STAT         (1<<5)
+#define CD_DETECT_STAT         (1<<4)
+#define CMD_RES_INT_EN         (1<<2)
+#define DATA_XFER_INT_EN       (1<<1)
+#define CD_DETECT_IN_EN        (1<<0)
+
+#define SD_CARD                (*(volatile unsigned long *)(APB0_SD + 0x28))
+#define SD_CARD_SELECT         (1<<7)
+#define SD_CARD_PWR_EN         (1<<6)
+#define SD_CARD_DETECT_INT_EN  (1<<5)
+#define SD_CARD_BSY            (1<<2)
+#define SD_CARD_WRITE_PROTECT  (1<<1)
+#define SD_CARD_DETECT         (1<<0)
+
+#define SD_CMDREST             (*(volatile unsigned long *)(APB0_SD + 0x30))
+#define CMD_XFER_START         (1<<13)
+#define CMD_XFER_END           (0<<13)
+#define RES_XFER_START         (1<<12)
+#define RES_XFER_END           (0<<12)
+#define RES_R1                 (0<<9)
+#define RES_R1b                (1<<9)
+#define RES_R2                 (2<<9)
+#define RES_R3                 (3<<9)
+#define RES_R6                 (6<<9)
+#define CMD_RES_ERROR          (1<<8)
+/* bits 0-5 cmd index */
+
+#define SD_CMDRES              (*(volatile unsigned long *)(APB0_SD + 0x34))
+#define STAT_CMD_XFER_START    (1<<8)
+#define STAT_RES_XFER_START    (1<<7)
+#define STAT_CMD_RES_ERR       (1<<6)
+#define STAT_CMD_RES_BUS_ERR   (1<<5)
+#define STAT_RES_TIMEOUT_ERR   (1<<4)
+#define STAT_RES_STARTBIT_ERR  (1<<3)
+#define STAT_RES_INDEX_ERR     (1<<2)
+#define STAT_RES_CRC_ERR       (1<<1)
+#define STAT_RES_ENDBIT_ERR    (1<<0)
+
+#define SD_DATAT               (*(volatile unsigned long *)(APB0_SD + 0x3C))
+#define DATA_XFER_START        (1<<13)
+#define DATA_XFER_WRITE        (1<<12)
+#define DATA_XFER_READ         (0<<12)
+#define DATA_BUS_4LINES        (1<<11) /* rk2705/6/8 does not support this mode */
+#define DATA_BUS_1LINE         (0<<11)
+#define DATA_XFER_DMA_EN       (1<<10)
+#define DATA_XFER_DMA_DIS      (0<<10)
+#define DATA_XFER_MULTI        (1<<9)
+#define DATA_XFER_SINGLE       (0<<9)
+#define DATA_XFER_ERR          (1<<8)
+#define DATA_BUS_ERR           (1<<7)
+#define DATA_TIMEOUT_ERR       (1<<6)
+#define DATA_CRC_ERR           (1<<5)
+#define READ_DAT_STARTBIT_ERR  (1<<4)
+#define READ_DAT_ENDBIT_ERR    (1<<3)
+#define WRITE_DAT_NOERR        (2<<0)
+#define WRITE_DAT_CRC_ERR      (5<<0)
+#define WRITE_DAT_NO_RES       (7<<0)
+
+#define SD_CMD                 (*(volatile unsigned long *)(APB0_SD + 0x40))
+#define SD_RES3                (*(volatile unsigned long *)(APB0_SD + 0x44))
+#define SD_RES2                (*(volatile unsigned long *)(APB0_SD + 0x48))
+#define SD_RES1                (*(volatile unsigned long *)(APB0_SD + 0x4C))
+#define SD_RES0                (*(volatile unsigned long *)(APB0_SD + 0x50))
+
+/* I2S controller */
+#define APB0_I2S               (ARM_BUS0_BASE + 0x00028000)
+#define I2S_OPR                (*(volatile unsigned long *)(APB0_I2S + 0x00))
+#define I2S_TXR                (*(volatile unsigned long *)(APB0_I2S + 0x04))
+#define I2S_RXR                (*(volatile unsigned long *)(APB0_I2S + 0x08))
+#define I2S_TXCTL              (*(volatile unsigned long *)(APB0_I2S + 0x0C))
+#define I2S_RXCTL              (*(volatile unsigned long *)(APB0_I2S + 0x10))
+#define I2S_FIFOSTS            (*(volatile unsigned long *)(APB0_I2S + 0x14))
+#define I2S_IER                (*(volatile unsigned long *)(APB0_I2S + 0x18))
+#define I2S_ISR                (*(volatile unsigned long *)(APB0_I2S + 0x1C))
+
+/* PWM timer */
+#define APB0_PWM               (ARM_BUS0_BASE + 0x0002C000)
+#define PWMT0_CNTR             (*(volatile unsigned long *)(APB0_PWM + 0x00))
+#define PWMT0_HRC              (*(volatile unsigned long *)(APB0_PWM + 0x04))
+#define PWMT0_LRC              (*(volatile unsigned long *)(APB0_PWM + 0x08))
+#define PWMT0_CTRL             (*(volatile unsigned long *)(APB0_PWM + 0x0C))
+#define PWMT1_CNTR             (*(volatile unsigned long *)(APB0_PWM + 0x10))
+#define PWMT1_HRC              (*(volatile unsigned long *)(APB0_PWM + 0x14))
+#define PWMT1_LRC              (*(volatile unsigned long *)(APB0_PWM + 0x18))
+#define PWMT1_CTRL             (*(volatile unsigned long *)(APB0_PWM + 0x1C))
+#define PWMT2_CNTR             (*(volatile unsigned long *)(APB0_PWM + 0x20))
+#define PWMT2_HRC              (*(volatile unsigned long *)(APB0_PWM + 0x24))
+#define PWMT2_LRC              (*(volatile unsigned long *)(APB0_PWM + 0x28))
+#define PWMT2_CTRL             (*(volatile unsigned long *)(APB0_PWM + 0x2C))
+#define PWMT3_CNTR             (*(volatile unsigned long *)(APB0_PWM + 0x30))
+#define PWMT3_HRC              (*(volatile unsigned long *)(APB0_PWM + 0x34))
+#define PWMT3_LRC              (*(volatile unsigned long *)(APB0_PWM + 0x38))
+#define PWMT3_CTRL             (*(volatile unsigned long *)(APB0_PWM + 0x3C))
+
+/* ADC converter */
+#define APB0_ADC0              (ARM_BUS0_BASE + 0x00030000)
+#define ADC_DATA               (*(volatile unsigned long *)(APB0_ADC0 + 0x00))
+#define ADC_STAT               (*(volatile unsigned long *)(APB0_ADC0 + 0x04))
+#define ADC_CTRL               (*(volatile unsigned long *)(APB0_ADC0 + 0x08))
+
+/* 0x18034000 - 0x18038000 reserved */
+
+/* GPIO ports E,F */
+#define APB0_GPIO1             (ARM_BUS0_BASE + 0x00038000)
+#define GPIO_PEDR              (*(volatile unsigned long *)(APB0_GPIO1 + 0x00))
+#define GPIO_PECON             (*(volatile unsigned long *)(APB0_GPIO1 + 0x04))
+#define GPIO_PFDR              (*(volatile unsigned long *)(APB0_GPIO1 + 0x08))
+#define GPIO_PFCON             (*(volatile unsigned long *)(APB0_GPIO1 + 0x0C))
+
+#define GPIO1_TEST             (*(volatile unsigned long *)(APB0_GPIO1 + 0x20))
+#define GPIO_IEE               (*(volatile unsigned long *)(APB0_GPIO1 + 0x24))
+#define GPIO_IEF               (*(volatile unsigned long *)(APB0_GPIO1 + 0x28))
+
+#define GPIO_ISE               (*(volatile unsigned long *)(APB0_GPIO1 + 0x34))
+#define GPIO_ISF               (*(volatile unsigned long *)(APB0_GPIO1 + 0x38))
+
+#define GPIO_IBEE              (*(volatile unsigned long *)(APB0_GPIO1 + 0x44))
+#define GPIO_IBEF              (*(volatile unsigned long *)(APB0_GPIO1 + 0x48))
+
+#define GPIO_IEVE              (*(volatile unsigned long *)(APB0_GPIO1 + 0x54))
+#define GPIO_IEVF              (*(volatile unsigned long *)(APB0_GPIO1 + 0x58))
+
+#define GPIO_ICE               (*(volatile unsigned long *)(APB0_GPIO1 + 0x64))
+#define GPIO_ICF               (*(volatile unsigned long *)(APB0_GPIO1 + 0x68))
+
+#define GPIO1_ISR              (*(volatile unsigned long *)(APB0_GPIO1 + 0x74))
+
+
+/* 0x1803C000 - 0x18080000 reserved */
+
+/* Interrupt controller */
+#define AHB0_INTC              (ARM_BUS0_BASE + 0x00080000)
+#define INTC_SCR0              (*(volatile unsigned long *)(AHB0_INTC + 0x00))
+#define INTC_SCR1              (*(volatile unsigned long *)(AHB0_INTC + 0x04))
+#define INTC_SCR2              (*(volatile unsigned long *)(AHB0_INTC + 0x08))
+#define INTC_SCR3              (*(volatile unsigned long *)(AHB0_INTC + 0x0C))
+#define INTC_SCR4              (*(volatile unsigned long *)(AHB0_INTC + 0x10))
+#define INTC_SCR5              (*(volatile unsigned long *)(AHB0_INTC + 0x14))
+#define INTC_SCR6              (*(volatile unsigned long *)(AHB0_INTC + 0x18))
+#define INTC_SCR7              (*(volatile unsigned long *)(AHB0_INTC + 0x1C))
+#define INTC_SCR8              (*(volatile unsigned long *)(AHB0_INTC + 0x20))
+#define INTC_SCR9              (*(volatile unsigned long *)(AHB0_INTC + 0x24))
+#define INTC_SCR10             (*(volatile unsigned long *)(AHB0_INTC + 0x28))
+#define INTC_SCR11             (*(volatile unsigned long *)(AHB0_INTC + 0x2C))
+#define INTC_SCR12             (*(volatile unsigned long *)(AHB0_INTC + 0x30))
+#define INTC_SCR13             (*(volatile unsigned long *)(AHB0_INTC + 0x34))
+#define INTC_SCR14             (*(volatile unsigned long *)(AHB0_INTC + 0x38))
+#define INTC_SCR15             (*(volatile unsigned long *)(AHB0_INTC + 0x3C))
+#define INTC_SCR16             (*(volatile unsigned long *)(AHB0_INTC + 0x40))
+#define INTC_SCR17             (*(volatile unsigned long *)(AHB0_INTC + 0x44))
+#define INTC_SCR18             (*(volatile unsigned long *)(AHB0_INTC + 0x48))
+#define INTC_SCR19             (*(volatile unsigned long *)(AHB0_INTC + 0x4C))
+#define INTC_SCR20             (*(volatile unsigned long *)(AHB0_INTC + 0x50))
+#define INTC_SCR21             (*(volatile unsigned long *)(AHB0_INTC + 0x54))
+#define INTC_SCR22             (*(volatile unsigned long *)(AHB0_INTC + 0x58))
+#define INTC_SCR23             (*(volatile unsigned long *)(AHB0_INTC + 0x5C))
+#define INTC_SCR24             (*(volatile unsigned long *)(AHB0_INTC + 0x60))
+#define INTC_SCR25             (*(volatile unsigned long *)(AHB0_INTC + 0x64))
+#define INTC_SCR26             (*(volatile unsigned long *)(AHB0_INTC + 0x68))
+#define INTC_SCR27             (*(volatile unsigned long *)(AHB0_INTC + 0x6C))
+#define INTC_SCR28             (*(volatile unsigned long *)(AHB0_INTC + 0x70))
+#define INTC_SCR29             (*(volatile unsigned long *)(AHB0_INTC + 0x74))
+#define INTC_SCR30             (*(volatile unsigned long *)(AHB0_INTC + 0x78))
+#define INTC_SCR31             (*(volatile unsigned long *)(AHB0_INTC + 0x7C))
+
+#define INTC_ISR               (*(volatile unsigned long *)(AHB0_INTC + 0x104))
+#define INTC_IPR               (*(volatile unsigned long *)(AHB0_INTC + 0x108))
+#define INTC_IMR               (*(volatile unsigned long *)(AHB0_INTC + 0x10C))
+
+#define INTC_IECR              (*(volatile unsigned long *)(AHB0_INTC + 0x114))
+#define INTC_ICCR              (*(volatile unsigned long *)(AHB0_INTC + 0x118))
+#define INTC_ISCR              (*(volatile unsigned long *)(AHB0_INTC + 0x11C))
+
+#define INTC_TEST              (*(volatile unsigned long *)(AHB0_INTC + 0x124))
+
+/* Bus arbiter module */
+#define AHB0_ARBITER           (ARM_BUS0_BASE + 0x00084000)
+#define ARB_MODE               (*(volatile unsigned long *)(AHB0_ARBITER + 0x00))
+#define ARB_PRIO1              (*(volatile unsigned long *)(AHB0_ARBITER + 0x04))
+#define ARB_PRIO2              (*(volatile unsigned long *)(AHB0_ARBITER + 0x08))
+#define ARB_PRIO3              (*(volatile unsigned long *)(AHB0_ARBITER + 0x0C))
+#define ARB_PRIO4              (*(volatile unsigned long *)(AHB0_ARBITER + 0x10))
+#define ARB_PRIO5              (*(volatile unsigned long *)(AHB0_ARBITER + 0x14))
+#define ARB_PRIO6              (*(volatile unsigned long *)(AHB0_ARBITER + 0x18))
+#define ARB_PRIO7              (*(volatile unsigned long *)(AHB0_ARBITER + 0x1C))
+#define ARB_PRIO8              (*(volatile unsigned long *)(AHB0_ARBITER + 0x20))
+#define ARB_PRIO9              (*(volatile unsigned long *)(AHB0_ARBITER + 0x24))
+#define ARB_PRIO10             (*(volatile unsigned long *)(AHB0_ARBITER + 0x28))
+#define ARB_PRIO11             (*(volatile unsigned long *)(AHB0_ARBITER + 0x2C))
+#define ARB_PRIO12             (*(volatile unsigned long *)(AHB0_ARBITER + 0x30))
+#define ARB_PRIO13             (*(volatile unsigned long *)(AHB0_ARBITER + 0x34))
+#define ARB_PRIO14             (*(volatile unsigned long *)(AHB0_ARBITER + 0x38))
+#define ARB_PRIO15             (*(volatile unsigned long *)(AHB0_ARBITER + 0x3C))
+
+/* Interprocessor communication module */
+#define AHB0_CPU_MAILBOX       (ARM_BUS0_BASE + 0x00088000)
+#define MAILBOX_ID             (*(volatile unsigned long *)(AHB0_CPU_MAILBOX + 0x00))
+#define H2C_STA                (*(volatile unsigned long *)(AHB0_CPU_MAILBOX + 0x10))
+#define H2C0_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x20))
+#define H2C0_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x24))
+#define H2C1_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x28))
+#define H2C1_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x2C))
+#define H2C2_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x30))
+#define H2C2_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x24))
+#define H2C3_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x38))
+#define H2C3_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x3C))
+
+#define C2H_STA                (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x40))
+#define C2H0_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x50))
+#define C2H0_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x54))
+#define C2H1_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x58))
+#define C2H1_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x5C))
+#define C2H2_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x60))
+#define C2H2_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x64))
+#define C2H3_DATA              (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x68))
+#define C2H3_CMD               (*(volatile unsigned long *)(AHB0_CPU_MAILBOC + 0x6C))
+
+/* Debug module */
+#define AHB0_CPU_DEBUGIF       (ARM_BUS0_BASE + 0x0008C000)
+
+/* AHB DMA */
+#define AHB0_HDMA              (ARM_BUS0_BASE + 0x00090000)
+#define HDMA_CON0              (*(volatile unsigned long *)(AHB0_HDMA + 0x00))
+#define HDMA_CON1              (*(volatile unsigned long *)(AHB0_HDMA + 0x04))
+#define HDMA_ISRC0             (*(volatile unsigned long *)(AHB0_HDMA + 0x08))
+#define HDMA_IDST0             (*(volatile unsigned long *)(AHB0_HDMA + 0x0C))
+#define HDMA_ICNT0             (*(volatile unsigned long *)(AHB0_HDMA + 0x10))
+#define HDMA_ISRC1             (*(volatile unsigned long *)(AHB0_HDMA + 0x14))
+#define HDMA_IDST1             (*(volatile unsigned long *)(AHB0_HDMA + 0x18))
+#define HDMA_ICNT1             (*(volatile unsigned long *)(AHB0_HDMA + 0x1C))
+#define HDMA_CSRC0             (*(volatile unsigned long *)(AHB0_HDMA + 0x20))
+#define HDMA_CDST0             (*(volatile unsigned long *)(AHB0_HDMA + 0x24))
+#define HDMA_CCNT0             (*(volatile unsigned long *)(AHB0_HDMA + 0x28))
+#define HDMA_CSRC1             (*(volatile unsigned long *)(AHB0_HDMA + 0x2C))
+#define HDMA_CDST1             (*(volatile unsigned long *)(AHB0_HDMA + 0x30))
+#define HDMA_CCNT1             (*(volatile unsigned long *)(AHB0_HDMA + 0x34))
+#define HDMA_ISR               (*(volatile unsigned long *)(AHB0_HDMA + 0x38))
+#define HDMA_DSR               (*(volatile unsigned long *)(AHB0_HDMA + 0x3C))
+#define HDMA_ISCNT0            (*(volatile unsigned long *)(AHB0_HDMA + 0x40))
+#define HDMA_IPNCNTD0          (*(volatile unsigned long *)(AHB0_HDMA + 0x44))
+#define HDMA_IADDR_BS0         (*(volatile unsigned long *)(AHB0_HDMA + 0x48))
+#define HDMA_ISCNT1            (*(volatile unsigned long *)(AHB0_HDMA + 0x4C))
+#define HDMA_IPNCNTD1          (*(volatile unsigned long *)(AHB0_HDMA + 0x50))
+#define HDMA_IADDR_BS1         (*(volatile unsigned long *)(AHB0_HDMA + 0x54))
+#define HDMA_CSCNT0            (*(volatile unsigned long *)(AHB0_HDMA + 0x58))
+#define HDMA_CPNCNTD0          (*(volatile unsigned long *)(AHB0_HDMA + 0x5C))
+#define HDMA_CADDR_BS0         (*(volatile unsigned long *)(AHB0_HDMA + 0x60))
+#define HDMA_CSCNT1            (*(volatile unsigned long *)(AHB0_HDMA + 0x64))
+#define HDMA_CPNCNTD1          (*(volatile unsigned long *)(AHB0_HDMA + 0x68))
+#define HDMA_CADDR_BS1         (*(volatile unsigned long *)(AHB0_HDMA + 0x6C))
+#define HDMA_PACNT0            (*(volatile unsigned long *)(AHB0_HDMA + 0x70))
+#define HDMA_PACNT1            (*(volatile unsigned long *)(AHB0_HDMA + 0x74))
+
+/* AHB-to-AHB bridge controller */
+#define AHB0_A2A_DMA           (ARM_BUS0_BASE + 0x00094000)
+#define A2A_CON0               (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x00))
+#define A2A_ISRC0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x04))
+#define A2A_IDST0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x08))
+#define A2A_ICNT0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x0C))
+#define A2A_CSRC0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x10))
+#define A2A_CDST0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x14))
+#define A2A_CCNT0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x18))
+#define A2A_CON1               (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x1C))
+#define A2A_ISRC1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x20))
+#define A2A_IDST1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x24))
+#define A2A_ICNT1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x28))
+#define A2A_CSRC1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x2C))
+#define A2A_CDST1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x30))
+#define A2A_CCNT1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x34))
+#define A2A_INT_STS            (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x38))
+#define A2A_DMA_STS            (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x3C))
+#define A2A_ERR_ADR0           (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x40))
+#define A2A_ERR_OP0            (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x44))
+#define A2A_ERR_ADR1           (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x48))
+#define A2A_ERR_OP1            (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x4C))
+#define A2A_LCNT0              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x50))
+#define A2A_LCNT1              (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x54))
+#define A2A_DOMAIN             (*(volatile unsigned long *)(AHB0_A2A_DMA + 0x58))
+
+/* 0x18098000 - 0x180A000 reserved */
+
+/* USB device controller */
+#define AHB0_UDC               (ARM_BUS0_BASE + 0x000A0000)
+#define PHY_TEST_EN            (*(volatile unsigned long *)(AHB0_UDC + 0x00))
+#define PHY_TEST               (*(volatile unsigned long *)(AHB0_UDC + 0x04))
+#define DEV_CTL                (*(volatile unsigned long *)(AHB0_UDC + 0x08))
+
+#define DEV_INFO               (*(volatile unsigned long *)(AHB0_UDC + 0x10))
+#define EN_INT                 (*(volatile unsigned long *)(AHB0_UDC + 0x14))
+#define EN_SOF_INTR            (1<<0)
+#define EN_SETUP_INTR          (1<<1)
+#define EN_IN0_INTR            (1<<2)
+#define EN_OUT0_INTR           (1<<3)
+#define EN_USBRST_INTR         (1<<4)
+#define EN_RESUME_INTR         (1<<5)
+#define EN_SUSP_INTR           (1<<6)
+/* bit 7 reserved */
+#define EN_BOUT1_INTR          (1<<8)
+#define EN_BIN2_INTR           (1<<9)
+#define EN_IIN3_INTR           (1<<10)
+#define EN_BOUT4_INTR          (1<<11)
+#define EN_BIN5_INTR           (1<<12)
+#define EN_IIN6_INTR           (1<<13)
+#define EN_BOUT7_INTR          (1<<14)
+#define EN_BIN8_INTR           (1<<15)
+#define EN_IIN9_INTR           (1<<16)
+#define EN_BOUT10_INTR         (1<<17)
+#define EN_BIN11_INTR          (1<<18)
+#define EN_IIN12_INTR          (1<<19)
+#define EN_BOUT13_INTR         (1<<20)
+#define EN_BIN14_INTR          (1<<21)
+#define EN_IIN15_INTR          (1<<22)
+/* bits 23-26 TEST */
+/* bits 27-31 reserved */
+
+#define INT2FLAG               (*(volatile unsigned long *)(AHB0_UDC + 0x18))
+#define SOF_INTR              (1<<0)
+#define SETUP_INTR            (1<<1)
+#define IN0_INTR              (1<<2)
+#define OUT0_INTR             (1<<3)
+#define USBRST_INTR           (1<<4)
+#define RESUME_INTR           (1<<5)
+#define SUSP_INTR             (1<<6)
+/* bit 7 reserved */
+#define BOUT1_INTR            (1<<8)
+#define BIN2_INTR             (1<<9)
+#define IIN3_INTR             (1<<10)
+#define BOUT4_INTR            (1<<11)
+#define BIN5_INTR             (1<<12)
+#define IIN6_INTR             (1<<13)
+#define BOUT7_INTR            (1<<14)
+#define BIN8_INTR             (1<<15)
+#define IIN9_INTR             (1<<16)
+#define BOUT10_INTR           (1<<17)
+#define BIN11_INTR            (1<<18)
+#define IIN12_INTR            (1<<19)
+#define BOUT13_INTR           (1<<20)
+#define BIN14_INTR            (1<<21)
+#define IIN15_INTR            (1<<22)
+/* bits 23-26 TEST */
+/* bits 27-31 reserved */
+
+#define INTCON                 (*(volatile unsigned long *)(AHB0_UDC + 0x1C))
+#define SETUP1                 (*(volatile unsigned long *)(AHB0_UDC + 0x20))
+#define SETUP2                 (*(volatile unsigned long *)(AHB0_UDC + 0x24))
+#define AHBCON                 (*(volatile unsigned long *)(AHB0_UDC + 0x28))
+
+#define RX0STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x30))
+#define RX0CON                 (*(volatile unsigned long *)(AHB0_UDC + 0x34))
+#define RX0FFRC                (1<<0)
+#define RX0CLR                 (1<<1)
+#define RX0STALL               (1<<2)
+#define RX0NAK                 (1<<3)
+#define EP0EN                  (1<<4)
+#define RX0VOIDINTEN           (1<<5)
+#define RX0ERRINTEN            (1<<6)
+#define RX0ACKINTEN            (1<<7)
+/* bits 8-31 reserved */
+
+#define RX0DMACTLO             (*(volatile unsigned long *)(AHB0_UDC + 0x38))
+#define RX0DMAOUTLMADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x3C))
+#define TX0STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x40))
+#define TX0CON                 (*(volatile unsigned long *)(AHB0_UDC + 0x44))
+#define TX0CLR                 (1<<0)
+#define TX0STALL               (1<<1)
+#define TX0NAK                 (1<<2)
+/* bit 3 reserved */
+#define TX0VOIDINTEN           (1<<4)
+#define TX0ERRINTEN            (1<<5)
+#define TX0ACKINTEN            (1<<6)
+/* bits 7-31 reserved */
+
+#define TX0BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0x48))
+#define TX0FULL                (1<<0)
+#define TX0URF                 (1<<1)
+/* bits 2-31 reserved */
+
+#define TX0DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0x4C))
+#define TX0DMAINSTA            (1<<0)
+/* bits 1-31 reserved */
+
+#define TX0DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0x50))
+#define RX1STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x54))
+#define RX1CON                 (*(volatile unsigned long *)(AHB0_UDC + 0x58))
+#define RX1DMACTLO             (*(volatile unsigned long *)(AHB0_UDC + 0x5C))
+#define RX1DMAOUTLMADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x60))
+#define TX2STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x64))
+#define TX2CON                 (*(volatile unsigned long *)(AHB0_UDC + 0x68))
+#define TX2BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0x6C))
+#define TX2DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0x70))
+#define TX2DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0x74))
+#define TX3STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x78))
+#define TX3CON                 (*(volatile unsigned long *)(AHB0_UDC + 0x7C))
+#define TX3BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0x80))
+#define TX3DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0x84))
+#define TX3DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0x88))
+#define RX4STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x8C))
+#define RX4CON                 (*(volatile unsigned long *)(AHB0_UDC + 0x90))
+#define RX4DMACTLO             (*(volatile unsigned long *)(AHB0_UDC + 0x94))
+#define RX4DMAOUTLMADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x98))
+#define TX5STAT                (*(volatile unsigned long *)(AHB0_UDC + 0x9C))
+#define TX5CON                 (*(volatile unsigned long *)(AHB0_UDC + 0xA0))
+#define TX5BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0xA4))
+#define TX5DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0xA8))
+#define TX5DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0xAC))
+#define TX6STAT                (*(volatile unsigned long *)(AHB0_UDC + 0xB0))
+#define TX6CON                 (*(volatile unsigned long *)(AHB0_UDC + 0xB4))
+#define TX6BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0xB8))
+#define TX6DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0xBC))
+#define TX6DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0xC0))
+#define RX7STAT                (*(volatile unsigned long *)(AHB0_UDC + 0xC4))
+#define RX7CON                 (*(volatile unsigned long *)(AHB0_UDC + 0xC8))
+#define RX7DMACTLO             (*(volatile unsigned long *)(AHB0_UDC + 0xCC))
+#define RX7DMAOUTLMADDR        (*(volatile unsigned long *)(AHB0_UDC + 0xD0))
+#define TX8STAT                (*(volatile unsigned long *)(AHB0_UDC + 0xD4))
+#define TX8CON                 (*(volatile unsigned long *)(AHB0_UDC + 0xD8))
+#define TX8BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0xDC))
+#define TX8DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0xE0))
+#define TX8DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0xE4))
+#define TX9STAT                (*(volatile unsigned long *)(AHB0_UDC + 0xE8))
+#define TX9CON                 (*(volatile unsigned long *)(AHB0_UDC + 0xEC))
+#define TX9BUF                 (*(volatile unsigned long *)(AHB0_UDC + 0xF0))
+#define TX9DMAINCTL            (*(volatile unsigned long *)(AHB0_UDC + 0xF4))
+#define TX9DMALM_IADDR         (*(volatile unsigned long *)(AHB0_UDC + 0xF8))
+#define RX10STAT               (*(volatile unsigned long *)(AHB0_UDC + 0xFC))
+#define RX10CON                (*(volatile unsigned long *)(AHB0_UDC + 0x100))
+#define RX10DMACTLO            (*(volatile unsigned long *)(AHB0_UDC + 0x104))
+#define RX10DMAOUTLMADDR       (*(volatile unsigned long *)(AHB0_UDC + 0x108))
+#define TX11STAT               (*(volatile unsigned long *)(AHB0_UDC + 0x10C))
+#define TX11CON                (*(volatile unsigned long *)(AHB0_UDC + 0x110))
+#define TX11BUF                (*(volatile unsigned long *)(AHB0_UDC + 0x114))
+#define TX11DMAINCTL           (*(volatile unsigned long *)(AHB0_UDC + 0x118))
+#define TX11DMALM_IADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x11C))
+#define TX12STAT               (*(volatile unsigned long *)(AHB0_UDC + 0x120))
+#define TX12CON                (*(volatile unsigned long *)(AHB0_UDC + 0x124))
+#define TX12BUF                (*(volatile unsigned long *)(AHB0_UDC + 0x128))
+#define TX12DMAINCTL           (*(volatile unsigned long *)(AHB0_UDC + 0x12C))
+#define TX12DMALM_IADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x130))
+#define RX13STAT               (*(volatile unsigned long *)(AHB0_UDC + 0x134))
+#define RX13CON                (*(volatile unsigned long *)(AHB0_UDC + 0x138))
+#define RX13DMACTLO            (*(volatile unsigned long *)(AHB0_UDC + 0x13C))
+#define RX13DMAOUTLMADDR       (*(volatile unsigned long *)(AHB0_UDC + 0x140))
+#define TX14STAT               (*(volatile unsigned long *)(AHB0_UDC + 0x144))
+#define TX14CON                (*(volatile unsigned long *)(AHB0_UDC + 0x148))
+#define TX14BUF                (*(volatile unsigned long *)(AHB0_UDC + 0x14C))
+#define TX14DMAINCTL           (*(volatile unsigned long *)(AHB0_UDC + 0x150))
+#define TX14DMALM_IADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x154))
+#define TX15STAT               (*(volatile unsigned long *)(AHB0_UDC + 0x158))
+#define TX15CON                (*(volatile unsigned long *)(AHB0_UDC + 0x15C))
+#define TX15BUF                (*(volatile unsigned long *)(AHB0_UDC + 0x160))
+#define TX15DMAINCTL           (*(volatile unsigned long *)(AHB0_UDC + 0x164))
+#define TX15DMALM_IADDR        (*(volatile unsigned long *)(AHB0_UDC + 0x168))
+
+/* USB host controller */
+#define AHB0_UHC               (ARM_BUS0_BASE + 0x000A4000)
+/* documentation missing */
+
+/* 0x180A8000 - 0x180B0000 reserved */
+
+/* Static/SDRAM memory controller */
+#define AHB0_SDRSTMC           (ARM_BUS0_BASE + 0x000B0000)
+#define MCSDR_MODE             (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x100))
+#define MCSDR_ADDMAP           (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x104))
+#define MCSDR_ADDCFG           (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x108))
+#define MCSDR_BASIC            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x10C))
+#define MCSDR_T_REF            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x110))
+#define MCSDR_T_RFC            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x114))
+#define MCSDR_T_MRD            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x118))
+#define MCSDR_T_RP             (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x120))
+#define MCSDR_T_RCD            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x124))
+
+#define MCST0_T_CEWD           (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x200))
+#define MCST0_T_CE2WE          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x204))
+#define MCST0_WEWD             (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x208))
+#define MCST0_T_WE2CE          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x20C))
+#define MCST0_T_CEWDR          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x210))
+#define MCST0_T_CE2RD          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x214))
+#define MCST0_T_RDWD           (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x218))
+#define MCST0_T_RD2CE          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x21C))
+#define MCST0_BASIC            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x220))
+
+#define MCST1_T_CEWD           (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x300))
+#define MCST1_T_CE2WE          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x304))
+#define MCST1_WEWD             (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x308))
+#define MCST1_T_WE2CE          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x30C))
+#define MCST1_T_CEWDR          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x310))
+#define MCST1_T_CE2RD          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x314))
+#define MCST1_T_RDWD           (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x318))
+#define MCST1_T_RD2CE          (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x31C))
+#define MCST1_BASIC            (*(volatile unsigned long *)(AHB0_SDRSTMC + 0x320))
+
+/* 0x180B4000 - 0x180C000 reserved */
+
+/* VIP - video input processor */
+#define AHB0_VIP               (ARM_BUS0_BASE + 0x000C0000)
+
+/* 0x180C4000 - 0x180E8000 reserved */
+
+/* NAND flash controller */
+#define AHB0_NANDC             (ARM_BUS0_BASE + 0x000E8000)
+
+#define FMCTL                  (*(volatile unsigned long *)(AHB0_NANDC))
+#define FM_RDY                 (1<<5) /* status of line R/B# */
+#define FM_PROTECT             (1<<4) /* WP# line (active low) */
+/* bits 0-3 are chip selects */
+
+#define FMWAIT                 (*(volatile unsigned long *)(AHB0_NANDC + 0x04))
+#define FLCTL                  (*(volatile unsigned long *)(AHB0_NANDC + 0x08))
+#define FL_RDY                 (1<<12)
+#define FL_COR_EN              (1<<11)
+#define FL_INT_EN              (1<<10)
+#define FL_XFER_EN             (1<<9)
+#define FL_INTCLR_EN           (1<<8)
+/* bits 3-7 unknown */
+#define FL_START               (1<<2)
+#define FL_WR                  (1<<1)
+#define FL_RST                 (1<<0)
+
+#define BCHCTL                 (*(volatile unsigned long *)(AHB0_NANDC + 0x0C))
+/* bit 13 is used but unknown */
+/* bit 12 is used but unknown */
+#define BCH_WR                 (1<<1)
+#define BCH_RST                (1<<0)
+
+#define BCHST                  (*(volatile unsigned long *)(AHB0_NANDC + 0xD0))
+/* bit 2 ERR ?? */
+/* bit 0 ?? */
+
+#define FLASH_DATA(n)          (*(volatile unsigned char *)(AHB0_NANDC + 0x200 + (n<<9)))
+#define FLASH_ADDR(n)          (*(volatile unsigned char *)(AHB0_NANDC + 0x204 + (n<<9)))
+#define FLASH_CMD(n)           (*(volatile unsigned char *)(AHB0_NANDC + 0x208 + (n<<9)))
+
+#define PAGE_BUF               (*(volatile unsigned char *)(AHB0_NANDC + 0xA00))
+#define SPARE_BUF              (*(volatile unsigned char *)(AHB0_NANDC + 0x1200))
+
+#define AHB0_ROM               (ARM_BUS0_BASE + 0x000EC000)
+#define AHB0_ES3               (ARM_BUS0_BASE + 0x000F4000)
+#define AHB0_ES4               (ARM_BUS0_BASE + 0x000F8000)
+#define AHB0_ES5               (ARM_BUS0_BASE + 0x000FC000)
+#define AHB0_ES6               (ARM_BUS0_BASE + 0x00100000)
+#define AHB0_EMD_SRAM          (ARM_BUS0_BASE + 0x00200000)
+
+/* 0x18204000 - 0x1840000 reserved */
+
+/* 0x18400000 - 0x18484000 reserved*/
+
+#define AHB1_ARBITER           0x18484000
+/* 0x18488000 - 0x186E8000 reserved*/
+
+/* LCD controller */
+#define AHB1_LCDC            0x186E8000
+#define LCDC_CTRL            (*(volatile unsigned long *)(AHB1_LCDC + 0x00))
+/* bits 14-31 reserved */
+#define ALPHA24B             (1<<13)
+#define UVBUFEXCH            (1<<12)
+#define ALPHA(x)             (((x)&0x07)<<9)
+#define Y_MIX                (1<<8)
+#define LCDC_MCU             (1<<7)
+#define RGB24B               (1<<6)
+#define START_EVEN           (1<<5)
+#define EVEN_EN              (1<<4)
+#define RGB_DUMMY(x)         (((x)&0x03)<<2)
+#define LCDC_EN              (1<<1)
+#define LCDC_STOP            (1<<0)
+#define MCU_CTRL             (*(volatile unsigned long *)(AHB1_LCDC + 0x04))
+
+#define ALPHA_BASE(x)        (((x)&0x3f)<<8)
+#define MCU_CTRL_FIFO_EN     (1<<6)
+#define MCU_CTRL_RS_HIGH     (1<<5)
+#define MCU_CTRL_BUFF_WRITE  (1<<2)
+#define MCU_CTRL_BUFF_START  (1<<1)
+#define MCU_CTRL_BYPASS      (1<<0)
+
+#define HOR_PERIOD           (*(volatile unsigned long *)(AHB1_LCDC + 0x08))
+#define VERT_PERIOD          (*(volatile unsigned long *)(AHB1_LCDC + 0x0C))
+#define HOR_PW               (*(volatile unsigned long *)(AHB1_LCDC + 0x10))
+#define VERT_PW              (*(volatile unsigned long *)(AHB1_LCDC + 0x14))
+#define HOR_ACT              (*(volatile unsigned long *)(AHB1_LCDC + 0x18))
+#define VERT_ACT             (*(volatile unsigned long *)(AHB1_LCDC + 0x1C))
+#define HOR_BP               (*(volatile unsigned long *)(AHB1_LCDC + 0x20))
+#define VERT_BP              (*(volatile unsigned long *)(AHB1_LCDC + 0x24))
+#define LINE0_YADDR          (*(volatile unsigned long *)(AHB1_LCDC + 0x28))
+#define LINE_ALPHA_EN        (1<<14)
+#define LINE_SCALE_EN        (1<<13)
+#define LINE_GBR             (1<<12)
+#define LINE_RGB             (0<<12)
+#define LINE_YUV_SRC         (1<<11)
+#define LINE_RGB_SRC         (0<<11)
+/* bits 0-10 Y_BASE */
+
+#define LINE0_UVADDR         (*(volatile unsigned long *)(AHB1_LCDC + 0x2C))
+#define LINE1_YADDR          (*(volatile unsigned long *)(AHB1_LCDC + 0x30))
+#define LINE1_UVADDR         (*(volatile unsigned long *)(AHB1_LCDC + 0x34))
+#define LINE2_YADDR          (*(volatile unsigned long *)(AHB1_LCDC + 0x38))
+#define LINE2_UVADDR         (*(volatile unsigned long *)(AHB1_LCDC + 0x3C))
+#define LINE3_YADDR          (*(volatile unsigned long *)(AHB1_LCDC + 0x40))
+#define LINE3_UVADDR         (*(volatile unsigned long *)(AHB1_LCDC + 0x44))
+#define START_X              (*(volatile unsigned long *)(AHB1_LCDC + 0x48))
+#define START_Y              (*(volatile unsigned long *)(AHB1_LCDC + 0x4C))
+#define DELTA_X              (*(volatile unsigned long *)(AHB1_LCDC + 0x50))
+#define DELTA_Y              (*(volatile unsigned long *)(AHB1_LCDC + 0x54))
+#define LCDC_INTR_MASK       (*(volatile unsigned long *)(AHB1_LCDC + 0x58))
+#define INTR_MASK_LINE       (1<<3)
+#define INTR_MASK_EVENLINE   (0<<3)
+#define INTR_MASK_BUFF       (1<<2)
+#define INTR_MASK_VERT       (1<<1)
+#define INTR_MASK_HOR        (1<<0)
+
+#define LCDC_STA             (*(volatile unsigned long *)(AHB1_LCDC + 0x7C))
+#define LCDC_MCU_IDLE        (1<<12)
+
+#define LCD_COMMAND          (*(volatile unsigned long *)(AHB1_LCDC + 0x1000))
+#define LCD_DATA             (*(volatile unsigned long *)(AHB1_LCDC + 0x1004))
+
+#define LCD_BUFF             (*(volatile unsigned long *)(AHB1_LCDC + 0x2000))
+/* High speed ADC interface */
+#define AHB1_HS_ADC            0x186EC000
+#define HSADC_DATA           (*(volatile unsigned long *)(AHB1_HS_ADC + 0x00))
+#define HSADC_CTRL           (*(volatile unsigned long *)(AHB1_HS_ADC + 0x04))
+#define HSADC_IER            (*(volatile unsigned long *)(AHB1_HS_ADC + 0x08))
+#define HSADC_ISR            (*(volatile unsigned long *)(AHB1_HS_ADC + 0x0C))
+
+/* AHB-to-AHB DMA controller */
+#define AHB1_DWDMA             0x186F0000
+#define DWDMA_SAR(n)         (*(volatile unsigned long *)(AHB1_DWDMA + 0x00 + 0x58*n))
+#define DWDMA_DAR(n)         (*(volatile unsigned long *)(AHB1_DWDMA + 0x08 + 0x58*n))
+#define DWDMA_LLP(n)         (*(volatile unsigned long *)(AHB1_DWDMA + 0x10 + 0x58*n))
+#define DWDMA_CTL_L(n)       (*(volatile unsigned long *)(AHB1_DWDMA + 0x18 + 0x58*n))
+#define CTLL_LLP_SRC_EN      (1<<28)
+#define CTLL_LLP_DST_EN      (1<<27)
+#define CTLL_SMS_M2          (1<<25)
+#define CTLL_SMS_M1          (0<<25)
+#define CTLL_DMS_M2          (1<<23)
+#define CTLL_DMS_M1          (0<<23)
+#define CTLL_FC_PER2PER      (3<<20)
+#define CTLL_FC_PER2MEM      (2<<20)
+#define CTLL_FC_MEM2PER      (1<<20)
+#define CTLL_FC_MEM2MEM      (0<<20)
+/* bit 19 reserved */
+#define CTLL_DST_SCATTER_EN  (1<<18)
+#define CTLL_SRC_GATHER_EN   (1<<17)
+#define CTLL_SRC_MSIZE_32    (4<<14)
+#define CTLL_SRC_MSIZE_16    (3<<14)
+#define CTLL_SRC_MSIZE_8     (2<<14)
+#define CTLL_SRC_MSIZE_4     (1<<14)
+#define CTLL_SRC_MSIZE_1     (0<<14)
+#define CTLL_DST_MSIZE_32    (4<<11)
+#define CTLL_DST_MSIZE_16    (3<<11)
+#define CTLL_DST_MSIZE_8     (2<<11)
+#define CTLL_DST_MSIZE_4     (1<<11)
+#define CTLL_DST_MSIZE_1     (0<<11)
+#define CTLL_SINC_NO         (2<<9)
+#define CTLL_SINC_DEC        (1<<9)
+#define CTLL_SINC_INC        (0<<9)
+#define CTLL_DINC_NO         (2<<7)
+#define CTLL_DINC_DEC        (1<<7)
+#define CTLL_DINC_INC        (0<<7)
+#define CTLL_SRC_TR_WIDTH_32 (2<<4)
+#define CTLL_SRC_TR_WIDTH_16 (1<<4)
+#define CTLL_SRC_TR_WIDTH_8  (0<<4)     
+#define CTLL_DST_TR_WIDTH_32 (2<<1)
+#define CTLL_DST_TR_WIDTH_16 (1<<1)
+#define CTLL_DST_TR_WIDTH_8  (0<<1)     
+#define CTLL_INT_EN          (1<<0)
+
+#define DWDMA_CTL_H(n)       (*(volatile unsigned long *)(AHB1_DWDMA + 0x1C + 0x58*n))
+#define DWDMA_SSTAT(n)       (*(volatile unsigned long *)(AHB1_DWDMA + 0x20 + 0x58*n))
+#define DWDMA_DSTAT(n)       (*(volatile unsigned long *)(AHB1_DWDMA + 0x28 + 0x58*n))
+#define DWDMA_SSTATAR(n)     (*(volatile unsigned long *)(AHB1_DWDMA + 0x30 + 0x58*n))
+#define DWDMA_DSTATAR(n)     (*(volatile unsigned long *)(AHB1_DWDMA + 0x38 + 0x58*n))
+#define DWDMA_CFG_L(n)       (*(volatile unsigned long *)(AHB1_DWDMA + 0x40 + 0x58*n))
+#define CFGL_RELOAD_DST      (1<<31)
+#define CFGL_RELOAD_SRC      (1<<30)
+#define CFGL_MAX_ABRST(n)    ((n)<<20)
+#define CFGL_SRC_HS_POL_LOW  (1<<19)
+#define CFGL_DST_HS_POL_LOW  (1<<18)
+#define CFGL_LOCK_B          (1<<17)
+#define CFGL_LOCK_CH         (1<<16)
+#define CFGL_LOCK_B_L(n)     (((n)&0x03)<<14)
+#define CFGL_LOCK_CH_L(n)    (((n)&0x03)<<12)
+#define CFGL_HS_SEL_SRC      (1<<11)
+#define CFGL_HS_SEL_DST      (1<<10)
+#define CFGL_FIFO_EMPTY      (1<<9)
+#define CFGL_CH_SUSP         (1<<8)
+#define CFGL_CH_PRIOR(n)     (((n) & 0x03)<<5)
+/* bits 0-4 reserved */
+#define DWDMA_CFG_H(n)       (*(volatile unsigned long *)(AHB1_DWDMA + 0x44 + 0x58*n))
+#define CFGH_DST_PER(n)      (((n)&0x0F)<<11)
+#define CFGH_SRC_PER(n)      (((n)&0x0F)<<7)
+#define CFGH_SRC_UPD_EN      (1<<6)
+#define CFGH_DST_UPD_EN      (1<<5)
+#define CFGH_PROTCTL(n)      (((n)&0x07)<<2)
+#define CFGH_FIFO_MODE       (1<<1)
+#define CFGH_FC_MODE         (1<<0)
+
+#define DWDMA_SGR(n)         (*(volatile unsigned long *)(AHB1_DWDMA + 0x48 + 0x58*n))
+#define DWDMA_DSR(n)         (*(volatile unsigned long *)(AHB1_DWDMA + 0x50 + 0x58*n))
+
+#define DWDMA_RAW_TFR        (*(volatile unsigned long *)(AHB1_DWDMA + 0x2C0))
+#define DWDMA_RAW_BLOCK      (*(volatile unsigned long *)(AHB1_DWDMA + 0x2C8))
+#define DWDMA_RAW_SRCTRAN    (*(volatile unsigned long *)(AHB1_DWDMA + 0x2D0))
+#define DWDMA_RAW_DSTTRAN    (*(volatile unsigned long *)(AHB1_DWDMA + 0x2D8))
+#define DWDMA_RAW_ERR        (*(volatile unsigned long *)(AHB1_DWDMA + 0x2E0))
+
+#define DWDMA_STATUS_TFR     (*(volatile unsigned long *)(AHB1_DWDMA + 0x2E8))
+#define DWDMA_STATUS_BLOCK   (*(volatile unsigned long *)(AHB1_DWDMA + 0x2F0))
+#define DWDMA_STATUS_SRCTRAN (*(volatile unsigned long *)(AHB1_DWDMA + 0x2F8))
+#define DWDMA_STATUS_DSTTRAN (*(volatile unsigned long *)(AHB1_DWDMA + 0x300))
+#define DWDMA_STATUS_ERR     (*(volatile unsigned long *)(AHB1_DWDMA + 0x308))
+
+#define DWDMA_MASK_TFR       (*(volatile unsigned long *)(AHB1_DWDMA + 0x310))
+#define DWDMA_MASK_BLOCK     (*(volatile unsigned long *)(AHB1_DWDMA + 0x318))
+#define DWDMA_MASK_SRCTRAN   (*(volatile unsigned long *)(AHB1_DWDMA + 0x320))
+#define DWDMA_MASK_DSTTRAN   (*(volatile unsigned long *)(AHB1_DWDMA + 0x328))
+#define DWDMA_MASK_ERR       (*(volatile unsigned long *)(AHB1_DWDMA + 0x330))
+
+#define DWDMA_CLEAR_TFR      (*(volatile unsigned long *)(AHB1_DWDMA + 0x338))
+#define DWDMA_CLEAR_BLOCK    (*(volatile unsigned long *)(AHB1_DWDMA + 0x340))
+#define DWDMA_CLEAR_SRCTRAN  (*(volatile unsigned long *)(AHB1_DWDMA + 0x348))
+#define DWDMA_CLEAR_DSTTRAN  (*(volatile unsigned long *)(AHB1_DWDMA + 0x350))
+#define DWDMA_CLEAR_ERR      (*(volatile unsigned long *)(AHB1_DWDMA + 0x358))
+
+#define DWDMA_STATUS_INT     (*(volatile unsigned long *)(AHB1_DWDMA + 0x360))
+
+#define DWDMA_REQ_SRC        (*(volatile unsigned long *)(AHB1_DWDMA + 0x368))
+#define DWDMA_REQ_DST        (*(volatile unsigned long *)(AHB1_DWDMA + 0x370))
+#define DWDMA_S_REQ_SRC      (*(volatile unsigned long *)(AHB1_DWDMA + 0x378))
+#define DWDMA_S_REQ_DST      (*(volatile unsigned long *)(AHB1_DWDMA + 0x380))
+#define DWDMA_L_REQ_SRC      (*(volatile unsigned long *)(AHB1_DWDMA + 0x388))
+#define DWDMA_L_REQ_DST      (*(volatile unsigned long *)(AHB1_DWDMA + 0x390))
+
+#define DWDMA_DMA_CFG        (*(volatile unsigned long *)(AHB1_DWDMA + 0x398))
+#define GLOB_EN              (1<<0)
+#define DWDMA_DMA_CHEN       (*(volatile unsigned long *)(AHB1_DWDMA + 0x3A0))
+#define DMACHEN_CH0          (0x101<<0)
+#define DMACHEN_CH1          (0x101<<1)
+#define DMACHEN_CH2          (0x101<<2)
+#define DMACHEN_CH3          (0x101<<3)
+
+/* ARM7 cache controller */
+#define ARM_CACHE_CNTRL        0xEFFF0000
+#define DEVID               (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x00))
+#define CACHEOP             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x04))
+#define CACHELKDN           (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x08))
+
+#define MEMMAPA             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x10))
+#define MEMMAPB             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x14))
+#define MEMMAPC             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x18))
+#define MEMMAPD             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x1C))
+#define PFCNTRA_CTRL        (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x20))
+#define PFCNTRA             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x24))
+#define PFCNTRB_CTRL        (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x28))
+#define PFCNTRB             (*(volatile unsigned long *)(ARM_CACHE_CTRL + 0x2C))
+
diff --git a/firmware/target/arm/rk27xx/adc-rk27xx.c b/firmware/target/arm/rk27xx/adc-rk27xx.c
new file mode 100644
index 0000000..c8bbae7
--- /dev/null
+++ b/firmware/target/arm/rk27xx/adc-rk27xx.c
@@ -0,0 +1,47 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "cpu.h"
+#include "system.h"
+#include "kernel.h"
+#include "thread.h"
+#include "adc.h"
+
+unsigned int adc_scan(int channel)
+{
+    ADC_CTRL = (1<<4) | (1<<3) | (channel & (NUM_ADC_CHANNELS - 1));
+
+    /* wait for conversion ready ~10us */    
+    while (ADC_STAT & 0x01);
+
+    /* 10bits result */
+    return (ADC_DATA & 0x3ff);
+}
+
+void adc_init(void)
+{
+    /* ADC clock divider to reach max 1MHz */
+    SCU_DIVCON1 = (SCU_DIVCON1 & ~(0xff<<10)) | (49<<10);
+
+    /* enable clocks for ADC */
+    SCU_CLKCFG |= 3<<23;
+}
diff --git a/firmware/target/arm/rk27xx/adc-target.h b/firmware/target/arm/rk27xx/adc-target.h
new file mode 100644
index 0000000..f6b8c98
--- /dev/null
+++ b/firmware/target/arm/rk27xx/adc-target.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _ADC_TARGET_H_
+#define _ADC_TARGET_H_
+
+#define NUM_ADC_CHANNELS 4
+
+#define ADC_BATTERY     0
+#define ADC_UNKNOWN_1   1
+#define ADC_UNKNOWN_2   2
+#define ADC_VREF        3 /* that is what datasheet says */
+
+#define ADC_UNREG_POWER ADC_BATTERY /* For compatibility */
+
+#endif
diff --git a/firmware/target/arm/rk27xx/ata-nand-rk27xx.c b/firmware/target/arm/rk27xx/ata-nand-rk27xx.c
new file mode 100644
index 0000000..dad49d4
--- /dev/null
+++ b/firmware/target/arm/rk27xx/ata-nand-rk27xx.c
@@ -0,0 +1,118 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Dave Chapman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include "system.h"
+#include <string.h>
+#include "thread.h"
+#include "disk.h"
+#include "storage.h"
+#include "panic.h"
+#include "usb.h"
+#include "ftl-target.h"
+#include "nand-target.h"
+
+/* This file provides only STUBS for now */
+
+/** static, private data **/ 
+static bool initialized = false;
+
+/* API Functions */
+int nand_read_sectors(IF_MD2(int drive,) unsigned long start, int incount,
+                     void* inbuf)
+{
+    (void)drive;
+    return ftl_read(start, incount, inbuf);
+}
+
+int nand_write_sectors(IF_MD2(int drive,) unsigned long start, int count,
+                      const void* outbuf)
+{
+    (void)drive;
+    return ftl_write(start, count, outbuf);
+}
+
+void nand_spindown(int seconds)
+{
+    (void)seconds;
+}
+
+void nand_sleep(void)
+{
+    nand_power_down();
+}
+
+void nand_sleepnow(void)
+{
+    nand_power_down();
+}
+
+void nand_spin(void)
+{
+    nand_set_active();
+}
+
+void nand_enable(bool on)
+{
+    (void)on;
+}
+
+void nand_get_info(IF_MD2(int drive,) struct storage_info *info)
+{
+    (void)drive;
+    uint32_t ppb = ftl_banks * (*ftl_nand_type).pagesperblock;
+    (*info).sector_size = SECTOR_SIZE;
+    (*info).num_sectors = (*ftl_nand_type).userblocks * ppb;
+    (*info).vendor = "Apple";
+    (*info).product = "iPod Nano 2G";
+    (*info).revision = "1.0";
+}
+
+long nand_last_disk_activity(void)
+{
+    return nand_last_activity();
+}
+
+#ifdef HAVE_STORAGE_FLUSH
+int nand_flush(void)
+{
+    int rc = ftl_sync();
+    if (rc != 0) panicf("Failed to unmount flash: %X", rc);
+    return rc;
+}
+#endif
+
+int nand_init(void)
+{
+    if (ftl_init()) return 1;
+
+    initialized = true;
+    return 0;
+}
+
+#ifdef CONFIG_STORAGE_MULTI
+int nand_num_drives(int first_drive)
+{
+    /* We don't care which logical drive number(s) we have been assigned */
+    (void)first_drive;
+    
+    return 1;
+}
+#endif
diff --git a/firmware/target/arm/rk27xx/backlight-rk27xx.c b/firmware/target/arm/rk27xx/backlight-rk27xx.c
new file mode 100644
index 0000000..0d87192
--- /dev/null
+++ b/firmware/target/arm/rk27xx/backlight-rk27xx.c
@@ -0,0 +1,86 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdbool.h>
+
+#include "config.h"
+#include "backlight.h"
+#include "backlight-target.h"
+#include "system.h"
+
+bool _backlight_init(void)
+{
+    /* configure PD4 as output */
+    GPIO_PDCON |= (1<<4);
+
+    /* set PD4 low (backlight off) */
+    GPIO_PDDR &= ~(1<<4);
+
+    /* IOMUXB - set PWM0 pin as GPIO */
+    SCU_IOMUXB_CON &= ~(1 << 11); /* type<<11<<channel */
+
+    /* setup pwm */
+    PWMT0_CTRL = (0<<9) | (1<<7);
+
+    /* set pwm frequency ~10kHz */
+    /* (apb_freq/pwm_freq)/pwm_div = (50 000 000/10 000)/2 */
+    PWMT0_LRC = 2500; 
+    PWMT0_HRC = 1;
+
+    PWMT0_CNTR = 0x00;
+    PWMT0_CTRL = (0<<9) | (1<<3) | (1<<0);
+
+    return true;
+}
+
+void _backlight_on(void)
+{
+    /* enable PWM clock */
+    SCU_CLKCFG &= ~(1<<29);
+
+    /* set output pin as PWM pin */
+    SCU_IOMUXB_CON |= (1<<11); /* type<<11<<channel */
+
+    /* 100% duty cycle. Other settings doesn't work for
+     * unknown reason
+     */
+    PWMT0_HRC = PWMT0_LRC;
+
+    /* pwm enable */
+    PWMT0_CTRL |= (1<<3) | (1<<0);
+}
+
+void _backlight_off(void)
+{
+    /* setup PWM0 pin as GPIO which is pulled low */
+    SCU_IOMUXB_CON &= ~(1<<11);
+
+    /* stop pwm timer */
+    PWMT0_CTRL &= ~(1<<3) | (1<<0);
+
+    /* disable PWM clock */
+    SCU_CLKCFG |= (1<<29);
+}
+
+void _backlight_set_brightness(int brightness)
+{
+    (void)brightness;
+    /* doesn't work for unknown reason */
+}
diff --git a/firmware/target/arm/rk27xx/backlight-target.h b/firmware/target/arm/rk27xx/backlight-target.h
new file mode 100644
index 0000000..91579b0
--- /dev/null
+++ b/firmware/target/arm/rk27xx/backlight-target.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2008 by Marcoen Hirschberg
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef BACKLIGHT_TARGET_H
+#define BACKLIGHT_TARGET_H
+
+bool _backlight_init(void);
+void _backlight_on(void);
+void _backlight_off(void);
+void _backlight_set_brightness(int brightness);
+
+#endif
diff --git a/firmware/target/arm/rk27xx/boot.lds b/firmware/target/arm/rk27xx/boot.lds
new file mode 100644
index 0000000..b7bc9be
--- /dev/null
+++ b/firmware/target/arm/rk27xx/boot.lds
@@ -0,0 +1,81 @@
+#include "config.h"
+
+ENTRY(start)
+#ifdef ROCKBOX_LITTLE_ENDIAN
+OUTPUT_FORMAT(elf32-littlearm)
+#else
+OUTPUT_FORMAT(elf32-bigarm)
+#endif
+OUTPUT_ARCH(arm)
+STARTUP(target/arm/rk27xx/crt0.o)
+
+#define DRAMORIG 0x60000000
+#define DRAMSIZE (MEMORYSIZE * 0x100000)
+
+#define IRAMORIG 0x00000000
+#define IRAMSIZE 4K
+
+MEMORY
+{
+    DRAM  : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
+    IRAM  : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
+}
+
+SECTIONS
+{
+  .intvect : {
+    _intvectstart = . ;
+    *(.intvect)
+    _intvectend = _newstart ;  
+  } > IRAM AT > DRAM
+  _intvectcopy = LOADADDR(.intvect) ;
+
+  .text : {
+    *(.init.text)
+    *(.text*)
+    *(.glue_7*)
+  } > DRAM
+
+  .data : {
+    *(.rodata*)
+    *(.data*)
+    *(.ncdata*);
+    . = ALIGN(0x4);
+  } > DRAM
+
+  .idata : {
+    _datastart = . ;
+    *(.irodata)
+    *(.icode)
+    *(.idata)
+    . = ALIGN(0x4);
+    _dataend = . ;
+   } > DRAM
+   _datacopy = LOADADDR(.idata) ;
+
+  .stack (NOLOAD) :
+  {
+     *(.stack)
+     _stackbegin = .;
+     stackbegin = .;
+     . += 0x2000;
+     _stackend = .;
+     stackend = .;
+     _irqstackbegin = .;
+     . += 0x400;
+     _irqstackend = .;
+     _fiqstackbegin = .;
+     . += 0x400;
+     _fiqstackend = .;
+  } > DRAM
+
+  .bss (NOLOAD) : {
+     _edata = .;
+     *(.bss*);
+     *(.ibss);
+     *(.ncbss*);
+     *(COMMON);
+    . = ALIGN(0x4);
+     _end = .;
+  } > DRAM
+}
diff --git a/firmware/target/arm/rk27xx/crt0.S b/firmware/target/arm/rk27xx/crt0.S
new file mode 100644
index 0000000..032c637
--- /dev/null
+++ b/firmware/target/arm/rk27xx/crt0.S
@@ -0,0 +1,209 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: crt0.S 18776 2008-10-11 18:32:17Z gevaerts $
+ *
+ * Copyright (C) 2008 by Marcoen Hirschberg
+ * Copyright (C) 2008 by Denes Balatoni
+ * Copyright (C) 2010 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#define ASM
+#include "config.h"
+#include "cpu.h"
+
+    .section .intvect,"ax",%progbits
+    .global    start
+    .global    _newstart
+    /* Exception vectors */
+start:
+    b _newstart
+    ldr pc, =undef_instr_handler
+    ldr pc, =software_int_handler
+    ldr pc, =prefetch_abort_handler
+    ldr pc, =data_abort_handler
+    ldr pc, =reserved_handler
+    ldr pc, =irq_handler
+    ldr pc, =fiq_handler
+    .ltorg
+_newstart:
+    ldr pc, =newstart2
+    .section .init.text,"ax",%progbits
+newstart2:
+    msr     cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */
+
+    mov     r0, #0x18000000
+    add     r0, r0, #0x1c000
+
+    /* setup ARM core freq = 200MHz */
+    /* AHB bus freq (HCLK) = 100MHz */
+    /* APB bus freq (PCLK) = 50MHz  */
+    ldr     r1, [r0,#0x14]   /* SCU_DIVCON1 */
+    orr     r1, #9           /* ARM slow mode, HCLK:PCLK = 2:1 */
+    str     r1, [r0,#0x14]
+
+    ldr     r1,=0x01970c70   /* (1<<24) | (1<<23) | (23<<16) | (199<<4) */
+    str     r1, [r0,#0x08]
+
+    ldr     r2,=0x40000
+1:
+    ldr     r1, [r0,#0x2c]   /* SCU_STATUS */
+    tst     r1, #1           /* ARM pll lock */
+    bne     1f
+    subs    r2, #1
+    bne     1b
+1:
+    ldr     r1, [r0,#0x14]   /* SCU_DIVCON1 */
+    bic     r1, #5           /* leave ARM slow mode, ARMclk:HCLK = 2:1 */
+    str     r1, [r0,#0x14]
+  
+#if defined(BOOTLOADER)
+    /* remap iram to 0x00000000 */
+    ldr     r1,=0xdeadbeef
+    str     r1, [r0, #4]
+#endif
+
+#if 0
+    /* setup caches */
+    ldr     r0, =0xefff0000    /* cache controler base address */
+    ldrh    r1, [r0]
+    strh    r1, [r0]          /* global cache disable */
+
+    /* setup uncached regions */
+    mov     r1, #0x18000000
+    orr     r1, r1, #0xfe
+    str     r1, [r0,#0x10]     /* MemMapA BUS0IP, 32MB */
+    str     r1, [r0,#0x14]     /* MemMapB BUS0IP, 32MB */
+    mov     r1, #0x30000000
+    orr     r1, r1, #0xfe
+    str     r1, [r0,#0x18]     /* MemMapC DSPMEM, 32MB */
+    mov     r1, #0xee000000    /* 0xefff0000 & 0xfe000000 */
+    orr     r1, r1, #0xfe
+    str     r1, [r0,#0x1c]     /* MemMapD cache controller, 32MB */
+
+    mov     r1, #2             /* invalidate way opcode */
+    str     r1, [r0,#4]        /* invalidate way0 */
+1:
+    ldr     r2, [r0,#4]
+    tst     r2, #3
+    bne     1b                 /* wait for invalidate to complete */
+
+    orr     r1, r1, #0x80000000
+    str     r1, [r0,#4]        /* invalidate way1 */
+1:
+    ldr     r2, [r0,#4]
+    tst     r2, #3
+    bne     1b                 /* wait for invalidate to complete */
+
+    ldr     r1, [r0]
+    orr     r1, r1, #0x80000000
+    str     r1, [r0]           /* global cache enable */
+#endif
+
+    /* Copy interrupt vectors to iram */
+    ldr     r2, =_intvectstart
+    ldr     r3, =_intvectend
+    ldr     r4, =_intvectcopy
+1:
+    cmp     r3, r2
+    ldrhi   r1, [r4], #4
+    strhi   r1, [r2], #4
+    bhi     1b
+
+    /* Initialise bss section to zero */
+    ldr     r2, =_edata
+    ldr     r3, =_end
+    mov     r4, #0
+1:
+    cmp     r3, r2
+    strhi   r4, [r2], #4
+    bhi     1b
+
+#ifndef BOOTLOADER
+    /* Copy icode and data to ram */
+    ldr     r2, =_iramstart
+    ldr     r3, =_iramend
+    ldr     r4, =_iramcopy
+1:
+    cmp     r3, r2
+    ldrhi   r1, [r4], #4
+    strhi   r1, [r2], #4
+    bhi     1b
+    
+    /* Initialise ibss section to zero */
+    ldr     r2, =_iedata
+    ldr     r3, =_iend
+    mov     r4, #0
+1:
+    cmp     r3, r2
+    strhi   r4, [r2], #4
+    bhi     1b
+#endif
+
+    /* Set up some stack and munge it with 0xdeadbeef */
+    ldr     sp, =stackend
+    ldr     r2, =stackbegin
+    ldr     r3, =0xdeadbeef
+1:
+    cmp     sp, r2
+    strhi   r3, [r2], #4
+    bhi     1b
+
+    /* Set up stack for IRQ mode */ 
+    msr     cpsr_c, #0xd2
+    ldr     sp, =_irqstackend
+
+    /* Set up stack for FIQ mode */ 
+    msr     cpsr_c, #0xd1
+    ldr     sp, =_fiqstackend
+
+    /* Let abort and undefined modes use IRQ stack */
+    msr     cpsr_c, #0xd7
+    ldr     sp, =_irqstackend
+    msr     cpsr_c, #0xdb
+    ldr     sp, =_irqstackend
+
+    /* Switch back to supervisor mode */
+    msr     cpsr_c, #0xd3
+
+    bl      main
+
+    .text
+/*    .global UIE*/
+
+/* All illegal exceptions call into UIE with exception address as first
+ * parameter. This is calculated differently depending on which exception
+ * we're in. Second parameter is exception number, used for a string lookup
+ * in UIE. */
+undef_instr_handler:
+    sub    r0, lr, #4
+    mov    r1, #0
+    b      UIE
+
+/* We run supervisor mode most of the time, and should never see a software
+ * exception being thrown. Perhaps make it illegal and call UIE? */
+software_int_handler:
+reserved_handler:
+    movs   pc, lr
+
+prefetch_abort_handler:
+    sub    r0, lr, #4
+    mov    r1, #1
+    b      UIE
+
+data_abort_handler:
+    sub    r0, lr, #8 
+    mov    r1, #2
+    b      UIE
diff --git a/firmware/target/arm/rk27xx/ftl-rk27xx.c b/firmware/target/arm/rk27xx/ftl-rk27xx.c
new file mode 100644
index 0000000..40248ab
--- /dev/null
+++ b/firmware/target/arm/rk27xx/ftl-rk27xx.c
@@ -0,0 +1,56 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010 by Bertrik Sikken
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+ 
+#include "config.h"
+#include "ftl-target.h"
+
+/* this file provides empty STUBS for now */
+
+uint32_t ftl_init(void)
+{
+    /* TODO implement */
+    return 0;
+}
+
+uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer)
+{
+    /* TODO implement */
+    (void)sector;
+    (void)count;
+    (void)buffer;
+    return 0;
+}
+
+uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer)
+{
+    /* TODO implement */
+    (void)sector;
+    (void)count;
+    (void)buffer;
+    return 0;
+}
+
+uint32_t ftl_sync(void)
+{
+    /* TODO implement */
+    return 0;
+}
+
diff --git a/firmware/target/arm/rk27xx/ftl-target.h b/firmware/target/arm/rk27xx/ftl-target.h
new file mode 100644
index 0000000..ad4dc04
--- /dev/null
+++ b/firmware/target/arm/rk27xx/ftl-target.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2009 by Michael Sparmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __FTL_TARGET_H__
+#define __FTL_TARGET_H__
+
+#include "config.h"
+#include "inttypes.h"
+
+#ifdef BOOTLOADER
+/* Bootloaders don't need write access */
+#define FTL_READONLY
+#endif
+
+/* Pointer to an info structure regarding the flash type used */
+const struct nand_device_info_type* ftl_nand_type;
+
+/* Number of banks we detected a chip on */
+uint32_t ftl_banks;
+
+uint32_t ftl_init(void);
+uint32_t ftl_read(uint32_t sector, uint32_t count, void* buffer);
+uint32_t ftl_write(uint32_t sector, uint32_t count, const void* buffer);
+uint32_t ftl_sync(void);
+
+
+#endif
diff --git a/firmware/target/arm/rk27xx/i2c-rk27xx.c b/firmware/target/arm/rk27xx/i2c-rk27xx.c
new file mode 100644
index 0000000..34a6f49
--- /dev/null
+++ b/firmware/target/arm/rk27xx/i2c-rk27xx.c
@@ -0,0 +1,242 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "system.h"
+#include "kernel.h"
+#include "i2c-rk27xx.h"
+
+/* NOT TESTED YET */
+
+/*  Driver for the rockchip rk27xx built-in I2C controller in master mode
+    
+    Both the i2c_read and i2c_write function take the following arguments:
+    * slave, the address of the i2c slave device to read from / write to
+    * address, optional sub-address in the i2c slave (unused if -1)
+    * len, number of bytes to be transfered
+    * data, pointer to data to be transfered
+    A return value other than 0 indicates an error.
+*/
+
+static struct mutex i2c_mtx;
+
+static bool i2c_write_byte(uint8_t data, bool start)
+{
+   long timeout = current_tick + HZ / 50;
+
+   /* START */
+   I2C_CONR |= (1<<3)  | (1<<2); /* master port enable, transmit bit */
+   I2C_MTXR = data;
+
+   if (start)
+       I2C_LCMR = (1<<2) | (1<<0); /* resume op, start bit */
+   else
+       I2C_LCMR = (1<<2); /* resume op */
+
+   I2C_CONR &= ~(1<<4); /* ACK enable */
+
+   /* wait for ACK from slave */
+   while ( !(I2C_ISR & (1<<0)) || (I2C_LSR & (1<<0)) )
+       if (TIME_AFTER(current_tick, timeout))
+           return false;
+
+   /* clear status bit */
+   I2C_ISR &= ~(1<<0);
+
+   return true;
+}
+
+static bool i2c_read_byte(unsigned char *data)
+{
+   long timeout = current_tick + HZ / 50;
+
+   I2C_LCMR = (1<<2); /* resume op */
+
+   while (I2C_ISR & (1<<1))
+       if (TIME_AFTER(current_tick, timeout))
+           return false;
+
+   *data = I2C_MRXR;
+
+   /* clear status bit */
+   I2C_ISR &= ~(1<<1);
+
+  return true;
+}
+
+static bool i2c_stop(void)
+{
+   long timeout = current_tick + HZ / 50;
+
+   I2C_CONR &= ~(1<<4);
+   I2C_LCMR |= (1<<2) | (1<<1); /* resume op, stop */
+
+   while (I2C_LCMR & (1<<1))
+       if (TIME_AFTER(current_tick, timeout))
+           return false;
+
+   return true;
+}
+
+/* route i2c bus to internal codec or external bus
+ * internal codec has 0x27 i2c slave address so
+ * access to this address is routed to internal bus.
+ * All other addresses are routed to external pads
+ */
+static void i2c_iomux(unsigned char slave)
+{
+   unsigned long muxa = SCU_IOMUXA_CON & ~(0x1f<<14);
+
+   if (slave == (0x27<<1))
+   {
+       /* internal codec */
+       SCU_IOMUXA_CON = muxa | (1<<16) | (1<<14);
+   }
+   else
+   {
+       /* external I2C bus */
+       SCU_IOMUXA_CON = muxa | (1<<18);
+   }
+}
+
+void i2c_init(void)
+{
+   mutex_init(&i2c_mtx);
+
+   SCU_CLKCFG &= ~(1<< 20);
+
+   I2C_OPR |= (1<<7); /* reset state machine */
+   sleep(HZ/100);
+   I2C_OPR &= ~((1<<7) | (1<<6)); /* clear ENABLE bit, deasert reset */
+
+   /* set I2C divider to stay within allowed SCL freq limit
+    * APBfreq = 50Mhz
+    * SCLfreq = (APBfreq/5*(I2CCDVR[5:3] + 1) * 2^((I2CCDVR[2:0] + 1))
+    */
+   I2C_OPR = (I2C_OPR & ~(0x3F)) | (6<<3) | 1<<0;
+
+   I2C_IER = 0x00;
+
+   I2C_OPR |= (1<<6); /* enable i2c core */
+}
+
+int i2c_write(unsigned char slave, int address, int len,
+              const unsigned char *data)
+{
+   mutex_lock(&i2c_mtx);
+
+   i2c_iomux(slave);
+
+   /* START */
+   if (! i2c_write_byte(slave & ~1, true))
+   {
+       mutex_unlock(&i2c_mtx);
+       return 1;
+   }
+
+   if (address >= 0)
+   {
+       if (! i2c_write_byte(address, false))
+       {
+           mutex_unlock(&i2c_mtx);
+           return 2;
+       }
+   }
+
+   /* write data */
+   while (len--)
+   {
+       if (! i2c_write_byte(*data++, false))
+       {
+           mutex_unlock(&i2c_mtx);
+           return 4;
+       }
+   }
+
+   /* STOP */
+   if (! i2c_stop())
+   {
+       mutex_unlock(&i2c_mtx);
+       return 5;
+   }
+
+   mutex_unlock(&i2c_mtx);
+   return 0;
+}
+
+int i2c_read(unsigned char slave, int address, int len, unsigned char *data)
+{
+   mutex_lock(&i2c_mtx);
+
+   i2c_iomux(slave);
+
+   if (address >= 0)
+   {
+       /* START */
+       if (! i2c_write_byte(slave & ~1, true))
+       {
+           mutex_unlock(&i2c_mtx);
+           return 1;
+       }
+
+       /* write address */
+       if (! i2c_write_byte(address, false))
+       {
+           mutex_unlock(&i2c_mtx);
+           return 2;
+       }
+   }
+
+   /* (repeated) START */
+   if (! i2c_write_byte(slave | 1, true))
+   {
+       mutex_unlock(&i2c_mtx);
+       return 3;
+   }
+
+   I2C_CONR &= ~(1<<3); /* clear transmit bit (switch to receive mode) */
+
+   while (len)
+   {
+       if (! i2c_read_byte(data++))
+       {
+               mutex_unlock(&i2c_mtx);
+               return 4;
+       }
+
+       if (len == 1)
+           I2C_CONR |= (1<<4); /* NACK */
+       else
+           I2C_CONR &= ~(1<<4); /* ACK */
+
+       len--;
+   }
+
+   /* STOP */
+   if (! i2c_stop())
+   {
+       mutex_unlock(&i2c_mtx);
+       return 5;
+   }
+
+   mutex_unlock(&i2c_mtx);
+   return 0;
+}
diff --git a/firmware/target/arm/rk27xx/kernel-rk27xx.c b/firmware/target/arm/rk27xx/kernel-rk27xx.c
new file mode 100644
index 0000000..d54a09d
--- /dev/null
+++ b/firmware/target/arm/rk27xx/kernel-rk27xx.c
@@ -0,0 +1,54 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "config.h"
+#include "system.h"
+#include "kernel.h"
+
+/*  rockchip rk27xx driver for the kernel timer */
+
+/* sys timer ISR */
+void INT_TIMER0(void)
+{
+    /* clear interrupt */
+    TMR0CON &= ~0x04;
+
+    call_tick_tasks();  /* Run through the list of tick tasks */
+}
+
+/* this assumes 50MHz APB bus frequency */
+void tick_start(unsigned int interval_in_ms)
+{
+    unsigned int cycles = 50000 * interval_in_ms;
+    
+    /* enable timer clock */
+    SCU_CLKCFG &= (1<<28);
+    
+    /* configure timer0 */
+    TMR0LR = cycles;
+    TMR0CON = (1<<8) | (1<<7) | (1<<1); /* periodic, 1/1, interrupt enable */
+
+    /* unmask timer0 interrupt */
+    INTC_IMR |= 0x04;
+
+    /* enable timer0 interrupt */
+    INTC_IECR |= 0x04;
+}
+
diff --git a/firmware/target/arm/rk27xx/lcd-rk27xx.c b/firmware/target/arm/rk27xx/lcd-rk27xx.c
new file mode 100644
index 0000000..bb71458
--- /dev/null
+++ b/firmware/target/arm/rk27xx/lcd-rk27xx.c
@@ -0,0 +1,304 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "kernel.h"
+#include "lcd.h"
+#include "system.h"
+#include "cpu.h"
+#include "spfd5420a.h"
+
+static inline void delay_nop(int cycles)
+{
+    asm volatile ("1: subs %[n], %[n], #1     \n\t"
+                  "   bne  1b"
+                  :  
+                  : [n] "r" (cycles));
+}
+
+static unsigned int lcd_data_transform(unsigned int data)
+{
+    /* 18 bit interface */
+    unsigned int r, g, b;
+    r = (data & 0x0000fc00)<<8;
+    /* g = ((data & 0x00000300) >> 2) | ((data & 0x000000e0) >> 3); */
+    g = ((data & 0x00000300) << 6) | ((data & 0x000000e0) << 5);
+    b = (data & 0x00000001f) << 3;
+
+    return (r | g | b);
+}
+
+/* converts RGB565 pixel into internal lcd bus format */
+static unsigned int lcd_pixel_transform(unsigned short rgb565)
+{
+    unsigned int r, g, b;
+    b = rgb565 & 0x1f;
+    g = (rgb565 >> 5) & 0x3f;
+    r = (rgb565 >> 11) & 0x1f;
+
+    return r<<19 | g<<10 | b<<3;
+}
+
+static void lcd_cmd(unsigned int cmd)
+{
+    LCD_COMMAND = lcd_data_transform(cmd);
+}
+
+static void lcd_data(unsigned int data)
+{
+    LCD_DATA = lcd_data_transform(data);
+}
+
+static void lcd_write_reg(unsigned int reg, unsigned int val)
+{
+    lcd_cmd(reg);
+    lcd_data(val);
+}
+
+static void lcdctrl_bypass(unsigned int on_off)
+{
+    while (!(LCDC_STA & LCDC_MCU_IDLE));
+
+    if (on_off)
+        MCU_CTRL |= MCU_CTRL_BYPASS;
+    else
+        MCU_CTRL &= ~MCU_CTRL_BYPASS;
+}
+
+/* This part is unclear. I am unable to use buffered/FIFO based writes
+ * to lcd. Depending on settings of IF I get various patterns on display
+ * but not what I want to display apparently.
+ */
+static void lcdctrl_init(void)
+{
+    /* alpha b111
+     * stop at current frame complete
+     * MCU mode
+     * 24b RGB
+     */
+    LCDC_CTRL = ALPHA(7) | LCDC_STOP | LCDC_MCU | RGB24B;
+    MCU_CTRL = ALPHA_BASE(0x3f) | MCU_CTRL_BYPASS;
+
+    HOR_ACT = 400 + 3;    /* define horizonatal active region */
+    VERT_ACT = 240;       /* define vertical active region */
+    VERT_PERIOD = 0xfff;  /* CSn/WEn/RDn signal timings */
+
+    LINE0_YADDR = LINE_ALPHA_EN | 0x7fe;
+    LINE1_YADDR = LINE_ALPHA_EN | ((1 * 400) - 2);
+    LINE2_YADDR = LINE_ALPHA_EN | ((2 * 400) - 2);
+    LINE3_YADDR = LINE_ALPHA_EN | ((3 * 400) - 2);
+
+    LINE0_UVADDR = 0x7fe + 1;
+    LINE1_UVADDR = ((1 * 400) - 2 + 1);
+    LINE2_UVADDR = ((2 * 400) - 2 + 1);
+    LINE3_UVADDR = ((3 * 400) - 2 + 1);
+
+#if 0
+    LINE0_YADDR = 0;
+    LINE1_YADDR = (1 * 400);
+    LINE2_YADDR = (2 * 400);
+    LINE3_YADDR = (3 * 400);
+
+    LINE0_UVADDR = 1;
+    LINE1_UVADDR = (1 * 400) + 1;
+    LINE2_UVADDR = (2 * 400) + 1;
+    LINE3_UVADDR = (3 * 400) + 1;
+
+    START_X = 0;
+    START_Y = 0;
+    DELTA_X = 0x200; /* no scaling */
+    DELTA_Y = 0x200; /* no scaling */
+#endif
+    LCDC_INTR_MASK = INTR_MASK_LINE; /* INTR_MASK_EVENLINE; */
+}
+
+/* configure pins to drive lcd in 18bit mode */
+static void iomux_lcd(void)
+{
+    unsigned long muxa;
+
+    muxa = SCU_IOMUXA_CON & ~(IOMUX_LCD_VSYNC|IOMUX_LCD_DEN|0xff);
+    muxa |= IOMUX_LCD_D18|IOMUX_LCD_D20|IOMUX_LCD_D22|IOMUX_LCD_D17|IOMUX_LCD_D16;
+
+    SCU_IOMUXA_CON = muxa;
+    SCU_IOMUXB_CON |= IOMUX_LCD_D815;
+}
+
+/* not tested */
+static void lcd_sleep(unsigned int sleep)
+{
+    if (sleep)
+    {
+        /* enter sleep mode */
+        lcd_write_reg(DISPLAY_CTRL1, 0x0170);
+        delay_nop(50);
+        lcd_write_reg(DISPLAY_CTRL1, 0x0000);
+        delay_nop(50);
+        lcd_write_reg(PWR_CTRL1,     0x14B4);
+    }
+    else
+    {
+         /* return to normal operation */
+        lcd_write_reg(PWR_CTRL1,     0x14B0);
+        delay_nop(50);
+        lcd_write_reg(DISPLAY_CTRL1, 0x0173);
+    }
+
+    lcd_cmd(GRAM_WRITE);
+}
+
+void lcd_init_device()
+{
+    unsigned int x, y;
+
+    iomux_lcd();       /* setup pins for 18bit lcd interface */
+    lcdctrl_init();    /* basic lcdc module configuration */
+
+    lcdctrl_bypass(1); /* run in bypass mode - all writes goes directly to lcd controller */
+
+    lcd_write_reg(RESET, 0x0001);
+    delay_nop(10000);
+    lcd_write_reg(RESET, 0x0000);
+    delay_nop(10000);
+    lcd_write_reg(IF_ENDIAN,       0x0000); /* order of receiving data */
+    lcd_write_reg(DRIVER_OUT_CTRL, 0x0000);
+    lcd_write_reg(ENTRY_MODE,      0x1038);
+    lcd_write_reg(WAVEFORM_CTRL,   0x0100); 
+    lcd_write_reg(SHAPENING_CTRL,  0x0000);
+    lcd_write_reg(DISPLAY_CTRL2,   0x0808);
+    lcd_write_reg(LOW_PWR_CTRL1,   0x0001);
+    lcd_write_reg(LOW_PWR_CTRL2,   0x0010);
+    lcd_write_reg(EXT_DISP_CTRL1,  0x0000);
+    lcd_write_reg(EXT_DISP_CTRL2,  0x0000);
+    lcd_write_reg(BASE_IMG_SIZE,   0x3100);
+    lcd_write_reg(BASE_IMG_CTRL,   0x0001);
+    lcd_write_reg(VSCROLL_CTRL,    0x0000);
+    lcd_write_reg(PART1_POS,       0x0000);
+    lcd_write_reg(PART1_START,     0x0000);
+    lcd_write_reg(PART1_END,       0x018F);
+    lcd_write_reg(PART2_POS,       0x0000);
+    lcd_write_reg(PART2_START,     0x0000);
+    lcd_write_reg(PART2_END,       0x0000);
+
+    lcd_write_reg(PANEL_IF_CTRL1,  0x0011);
+    delay_nop(10000);
+    lcd_write_reg(PANEL_IF_CTRL2,  0x0202);
+    lcd_write_reg(PANEL_IF_CTRL3,  0x0300);
+    delay_nop(10000);
+    lcd_write_reg(PANEL_IF_CTRL4,  0x021E);
+    lcd_write_reg(PANEL_IF_CTRL5,  0x0202);
+    lcd_write_reg(PANEL_IF_CTRL6,  0x0100);
+    lcd_write_reg(FRAME_MKR_CTRL,  0x0000);
+    lcd_write_reg(MDDI_CTRL,       0x0000);
+
+    lcd_write_reg(GAMMA_CTRL1,     0x0101);
+    lcd_write_reg(GAMMA_CTRL2,     0x0000);
+    lcd_write_reg(GAMMA_CTRL3,     0x0016);
+    lcd_write_reg(GAMMA_CTRL4,     0x2913);
+    lcd_write_reg(GAMMA_CTRL5,     0x260B);
+    lcd_write_reg(GAMMA_CTRL6,     0x0101);
+    lcd_write_reg(GAMMA_CTRL7,     0x1204);
+    lcd_write_reg(GAMMA_CTRL8,     0x0415);
+    lcd_write_reg(GAMMA_CTRL9,     0x0205);
+    lcd_write_reg(GAMMA_CTRL10,    0x0303);
+    lcd_write_reg(GAMMA_CTRL11,    0x0E05);
+    lcd_write_reg(GAMMA_CTRL12,    0x0D01);
+    lcd_write_reg(GAMMA_CTRL13,    0x010D);
+    lcd_write_reg(GAMMA_CTRL14,    0x050E);
+    lcd_write_reg(GAMMA_CTRL15,    0x0303);
+    lcd_write_reg(GAMMA_CTRL16,    0x0502);
+
+    /* power on */
+    lcd_write_reg(DISPLAY_CTRL1,   0x0001);
+    lcd_write_reg(PWR_CTRL6,       0x0001);
+    lcd_write_reg(PWR_CTRL7,       0x0060);
+    delay_nop(50000);
+    lcd_write_reg(PWR_CTRL1,       0x16B0);
+    delay_nop(10000);
+    lcd_write_reg(PWR_CTRL2,       0x0147);
+    delay_nop(10000);
+    lcd_write_reg(PWR_CTRL3,       0x0117);
+    delay_nop(10000);
+    lcd_write_reg(PWR_CTRL4,       0x2F00);
+    delay_nop(50000);
+    lcd_write_reg(VCOM_HV2,        0x0000); /* src 0x0090 */
+    delay_nop(10000);
+    lcd_write_reg(VCOM_HV1,        0x0008); /* src 0x000A */
+    lcd_write_reg(PWR_CTRL3,       0x01BE);
+    delay_nop(10000);
+
+    /* addresses setup */
+    lcd_write_reg(WINDOW_H_START,  0x0000);
+    lcd_write_reg(WINDOW_H_END,    0x00EF); /* 239 */
+    lcd_write_reg(WINDOW_V_START,  0x0000);
+    lcd_write_reg(WINDOW_V_END,    0x018F); /* 399 */
+    lcd_write_reg(GRAM_H_ADDR,     0x0000);
+    lcd_write_reg(GRAM_V_ADDR,     0x0000);
+
+    /* display on */
+    lcd_write_reg(DISPLAY_CTRL1,   0x0021);
+    delay_nop(40000);
+    lcd_write_reg(DISPLAY_CTRL1,   0x0061);
+    delay_nop(100000);
+    lcd_write_reg(DISPLAY_CTRL1,   0x0173);
+    delay_nop(300000);
+   
+ 
+    /* clear screen */
+    lcd_cmd(GRAM_WRITE);
+
+    for (x=0; x<400; x++)
+        for(y=0; y<240; y++)
+            lcd_data(0x000000);
+
+    lcd_sleep(0);
+}
+
+/* This is ugly hack. We drive lcd in bypass mode
+ * where all writes goes directly to lcd controller.
+ * This is suboptimal at best. IF module povides
+ * FIFO, internal sram buffer, hardware scaller,
+ * DMA signals, hardware alpha blending and more.
+ * BUT the fact is that I have no idea how to use
+ * this modes. Datasheet floating around is very
+ * unclean in this regard and OF uses ackward
+ * lcd update routines which are hard to understand.
+ * Moreover OF sets some bits in IF module registers
+ * which are referred as reseved in datasheet.
+ */
+void lcd_update()
+{
+    unsigned int x,y;
+
+    for (y=0; y<240; y++)
+        for (x=0; x<400; x++)
+            LCD_DATA = lcd_pixel_transform(lcd_framebuffer[y][x]);
+}
+
+/* not implemented yet */
+void lcd_update_rect(int x, int y, int width, int height)
+{
+    (void)x;
+    (void)y;
+    (void)width;
+    (void)height;
+    lcd_update();
+}
diff --git a/firmware/target/arm/rk27xx/nand-target.h b/firmware/target/arm/rk27xx/nand-target.h
new file mode 100644
index 0000000..dee690e
--- /dev/null
+++ b/firmware/target/arm/rk27xx/nand-target.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2009 by Michael Sparmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef __NAND_TARGET_H__
+#define __NAND_TARGET_H__
+
+#include "config.h"
+#include "inttypes.h"
+
+
+struct nand_device_info_type
+{
+    uint32_t id;
+    uint16_t blocks;
+    uint16_t userblocks;
+    uint16_t pagesperblock;
+    uint8_t blocksizeexponent;
+    uint8_t tunk1;
+    uint8_t twp;
+    uint8_t tunk2;
+    uint8_t tunk3;
+} __attribute__((packed));
+
+uint32_t nand_read_page(uint32_t bank, uint32_t page, void* databuffer,
+                        void* sparebuffer, uint32_t doecc,
+                        uint32_t checkempty);
+uint32_t nand_write_page(uint32_t bank, uint32_t page, void* databuffer,
+                         void* sparebuffer, uint32_t doecc);
+uint32_t nand_block_erase(uint32_t bank, uint32_t page);
+
+const struct nand_device_info_type* nand_get_device_type(uint32_t bank);
+uint32_t nand_reset(uint32_t bank);
+uint32_t nand_device_init(void);
+void nand_set_active(void);
+long nand_last_activity(void);
+void nand_power_up(void);
+void nand_power_down(void);
+
+
+#endif
diff --git a/firmware/target/arm/rk27xx/rk27generic/button-rk27generic.c b/firmware/target/arm/rk27xx/rk27generic/button-rk27generic.c
new file mode 100644
index 0000000..cd60482
--- /dev/null
+++ b/firmware/target/arm/rk27xx/rk27generic/button-rk27generic.c
@@ -0,0 +1,39 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "cpu.h"
+#include "system.h"
+#include "button.h"
+
+void button_init_device(void)
+{
+    /* setup button gpios as input */
+    GPIO_PCCON &= ~(BUTTON_MAIN);
+}
+
+/*
+ * Get button pressed from hardware
+ */
+int button_read_device(void)
+{
+    return (GPIO_PCDR & BUTTON_MAIN);
+}
diff --git a/firmware/target/arm/rk27xx/rk27generic/button-target.h b/firmware/target/arm/rk27xx/rk27generic/button-target.h
new file mode 100644
index 0000000..cc14dfc
--- /dev/null
+++ b/firmware/target/arm/rk27xx/rk27generic/button-target.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _BUTTON_TARGET_H_
+#define _BUTTON_TARGET_H_
+
+#include <stdbool.h>
+#include "config.h"
+
+void button_init_device(void);
+int button_read_device(void);
+
+/* Main unit's buttons */
+/* values assigned corespond to GPIOs numbers */
+#define BUTTON_PLAY         0x00000002
+
+#define BUTTON_REW          0x00000008
+#define BUTTON_FF           0x00000004
+#define BUTTON_VOL          0x00000040
+#define BUTTON_M            0x00000010
+
+#define BUTTON_LEFT BUTTON_REW
+#define BUTTON_RIGHT BUTTON_FF
+#define BUTTON_ON BUTTON_PLAY
+
+#define BUTTON_REMOTE       0
+
+#define BUTTON_MAIN (BUTTON_PLAY|BUTTON_REW|BUTTON_FF|\
+                     BUTTON_VOL|BUTTON_M)
+
+#define POWEROFF_BUTTON BUTTON_PLAY
+#define POWEROFF_COUNT 30
+
+#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/arm/rk27xx/sd-rk27xx.c b/firmware/target/arm/rk27xx/sd-rk27xx.c
new file mode 100644
index 0000000..c5a23ad
--- /dev/null
+++ b/firmware/target/arm/rk27xx/sd-rk27xx.c
@@ -0,0 +1,719 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2006 Daniel Ankers
+ * Copyright © 2008-2009 Rafaël Carré
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h" /* for HAVE_MULTIVOLUME */
+#include "fat.h"
+#include "thread.h"
+#include "gcc_extensions.h"
+#include "led.h"
+#include "sdmmc.h"
+#include "system.h"
+#include "kernel.h"
+#include "cpu.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "panic.h"
+#include "stdbool.h"
+#include "ata_idle_notify.h"
+#include "sd.h"
+#include "usb.h"
+
+#ifdef HAVE_HOTSWAP
+#include "disk.h"
+#endif
+
+#include "lcd.h"
+#include <stdarg.h>
+#include "sysfont.h"
+
+#define RES_NO (-1)
+
+static tCardInfo card_info;
+
+/* for compatibility */
+static long last_disk_activity = -1;
+
+static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x200)/sizeof(long)];
+static const char         sd_thread_name[] = "ata/sd";
+static struct mutex       sd_mtx SHAREDBSS_ATTR;
+static struct event_queue sd_queue;
+#ifndef BOOTLOADER
+bool sd_enabled = false;
+#endif
+
+static struct semaphore transfer_completion_signal;
+static struct semaphore command_completion_signal;
+static volatile bool retry;
+static volatile int cmd_error;
+
+/* interrupt handler for SD */
+void INT_SD(void)
+{
+    const int status = SD_INT;
+
+    SD_INT = 0; /* disable sd interrupts, clear pending interrupts */
+
+    /* cmd and response status pending */
+    if(status & CMD_RES_STAT)
+    {
+        /* get the status */
+        cmd_error = SD_CMDRES;
+        semaphore_release(&command_completion_signal);
+    }    
+
+    /* data transfer status pending */
+    if(status & DATA_XFER_STAT)
+    {
+        cmd_error = SD_DATAT;
+        if (cmd_error & DATA_XFER_ERR)
+            retry = true;
+
+        semaphore_release(&transfer_completion_signal);
+    }
+
+    SD_INT = CMD_RES_INT_EN | DATA_XFER_INT_EN;
+}
+
+/* Exchange buffers - the one where SD module puts into/reads from
+ * data and the one controlled by MCU. This allows some overlap
+ * in transfer operations and should increase throuput.
+ */
+static void mmu_switch_buff(void)
+{
+    static unsigned int i = 0;
+
+    if (i++ & 0x01)
+    {
+        MMU_CTRL = MMU_MMU0_BUFII | MMU_CPU_BUFI | MMU_BUFII_RESET |
+                   MMU_BUFII_BYTE | MMU_BUFI_RESET | MMU_BUFI_WORD;
+    }
+    else
+    {
+        MMU_CTRL = MMU_MMU0_BUFI | MMU_CPU_BUFII | MMU_BUFII_RESET |
+                   MMU_BUFII_WORD | MMU_BUFI_RESET | MMU_BUFI_BYTE;
+    }
+}
+
+/* Reset internal pointers of the MMU submodule */
+static void mmu_buff_reset(void)
+{
+    MMU_CTRL |= MMU_BUFII_RESET | MMU_BUFI_RESET;
+}
+
+/* My generic device uses PC7 pin, active low */
+static inline bool card_detect_target(void)
+{
+    return !(GPIO_PCDR & 0x80);
+}
+
+/* Send command to the SD card. Command finish is signaled in ISR */
+static bool send_cmd(const int cmd, const int arg, const int res,
+        unsigned long *response)
+{
+    SD_CMD = arg;
+
+    if (res > 0)
+       SD_CMDREST = CMD_XFER_START | RES_XFER_START | res | cmd;
+    else
+       SD_CMDREST = CMD_XFER_START | RES_XFER_END | RES_R1 | cmd;
+
+    semaphore_wait(&command_completion_signal, TIMEOUT_BLOCK);
+
+    /*  Handle command responses & errors */
+    if(res != RES_NO)
+    {
+        if(cmd_error & STAT_CMD_RES_ERR)
+            return false;
+
+        if(res == RES_R2)
+        {
+            response[0] = SD_RES3;
+            response[1] = SD_RES2;
+            response[2] = SD_RES1;
+            response[3] = SD_RES0;
+        }
+        else
+            response[0] = SD_RES3;
+    }
+    return true;
+}
+
+#if 0
+/* for some misterious reason the card does not report itself as being in TRAN
+ * but transfers are successful. Rockchip OF does not check the card state
+ * after SELECT. I checked two different cards. 
+ */
+static void print_card_status(void)
+{
+    unsigned long response;
+    send_cmd(SD_SEND_STATUS, card_info.rca, RES_R1,
+             &response);
+
+    printf("card status: 0x%0x, state: 0x%0x", response, (response>>9)&0xf);
+}
+
+static int sd_wait_for_tran_state(void)
+{
+    unsigned long response;
+    unsigned int timeout = current_tick + 5*HZ;
+    int cmd_retry = 10;
+
+    while (1)
+    {
+        while (!send_cmd(SD_SEND_STATUS, card_info.rca, RES_R1,
+                         &response) && cmd_retry > 0)
+        {
+            cmd_retry--;
+        }
+
+        if (cmd_retry <= 0)
+        {
+            return -1;
+        }
+
+        if (((response >> 9) & 0xf) == SD_TRAN)
+        {
+            return 0;
+        }
+    
+        if(TIME_AFTER(current_tick, timeout))
+        {
+            return -10 * ((response >> 9) & 0xf);
+        }
+
+        last_disk_activity = current_tick;
+    }
+}
+#endif
+
+static bool sd_wait_card_busy(void)
+{
+    unsigned int timeout = current_tick + 5*HZ;
+
+    while (!(SD_CARD & SD_CARD_BSY))
+    {
+        if(TIME_AFTER(current_tick, timeout))
+            return false;
+    }
+
+    return true;
+}
+
+static int sd_init_card(void)
+{
+    unsigned long response;
+    long init_timeout;
+    bool sd_v2 = false;
+
+    card_info.rca = 0;
+
+    /*  assume 50 MHz APB freq / 125 = 400 kHz  */
+    SD_CTRL = (SD_CTRL & ~(0x7FF)) | 0x7D;
+
+    /* 100 - 400kHz clock required for Identification Mode  */
+    /*  Start of Card Identification Mode ************************************/
+
+    /* CMD0 Go Idle  */
+    if(!send_cmd(SD_GO_IDLE_STATE, 0, RES_NO, NULL))
+        return -1;
+    
+    sleep(1);
+
+    /* CMD8 Check for v2 sd card.  Must be sent before using ACMD41
+      Non v2 cards will not respond to this command*/
+    if(send_cmd(SD_SEND_IF_COND, 0x1AA, RES_R6, &response))
+        if((response & 0xFFF) == 0x1AA)
+            sd_v2 = true;
+
+    /* timeout for initialization is 1sec, from SD Specification 2.00 */
+    init_timeout = current_tick + HZ;
+
+    do {
+        /* this timeout is the only valid error for this loop*/
+        if(TIME_AFTER(current_tick, init_timeout))
+            return -2;
+
+        if(!send_cmd(SD_APP_CMD, card_info.rca, RES_R1, &response))
+            return -3;
+
+        sleep(1); /* bus conflict otherwise */
+
+         /* ACMD41 For v2 cards set HCS bit[30] & send host voltage range to all */
+        if(!send_cmd(SD_APP_OP_COND, (0x00FF8000 | (sd_v2 ? 1<<30 : 0)),
+                     RES_R3, &card_info.ocr))
+            return -4;
+    } while(!(card_info.ocr & (1<<31)) );
+
+    /* CMD2 send CID */
+    if(!send_cmd(SD_ALL_SEND_CID, 0, RES_R2, card_info.cid))
+        return -5;
+
+    /* CMD3 send RCA */
+    if(!send_cmd(SD_SEND_RELATIVE_ADDR, 0, RES_R6, &card_info.rca))
+        return -6;
+
+    /*  End of Card Identification Mode   ************************************/
+
+    /*  Card back to full speed  25MHz*/
+    SD_CTRL = (SD_CTRL & ~0x7FF) | 1; /* FIXME check this divider - OF uses 0 here*/
+
+    /* CMD9 send CSD */
+    if(!send_cmd(SD_SEND_CSD, card_info.rca, RES_R2, card_info.csd))
+        return -11;
+
+    sd_parse_csd(&card_info);
+
+    if(!send_cmd(SD_SELECT_CARD, card_info.rca, RES_R1b, &response))
+        return -20;
+
+    if (!sd_wait_card_busy())
+        return -21;
+
+    card_info.initialized = 1;
+
+    return 0;
+}
+
+static void sd_thread(void) NORETURN_ATTR;
+static void sd_thread(void)
+{
+    struct queue_event ev;
+    bool idle_notified = false;
+
+    while (1)
+    {
+        queue_wait_w_tmo(&sd_queue, &ev, HZ);
+
+        switch ( ev.id )
+        {
+#ifdef HAVE_HOTSWAP
+        case SYS_HOTSWAP_INSERTED:
+        case SYS_HOTSWAP_EXTRACTED:
+        {
+            int microsd_init = 1;
+            fat_lock();          /* lock-out FAT activity first -
+                                    prevent deadlocking via disk_mount that
+                                    would cause a reverse-order attempt with
+                                    another thread */
+            mutex_lock(&sd_mtx); /* lock-out card activity - direct calls
+                                    into driver that bypass the fat cache */
+
+            /* We now have exclusive control of fat cache and ata */
+
+            disk_unmount(sd_first_drive);     /* release "by force", ensure file
+                                    descriptors aren't leaked and any busy
+                                    ones are invalid if mounting */
+            /* Force card init for new card, re-init for re-inserted one or
+             * clear if the last attempt to init failed with an error. */
+            card_info.initialized = 0;
+
+            if (ev.id == SYS_HOTSWAP_INSERTED)
+            {
+                sd_enable(true);
+                microsd_init = sd_init_card(sd_first_drive);
+                if (microsd_init < 0) /* initialisation failed */
+                    panicf("microSD init failed : %d", microsd_init);
+
+                microsd_init = disk_mount(sd_first_drive); /* 0 if fail */
+            }
+
+            /*
+             * Mount succeeded, or this was an EXTRACTED event,
+             * in both cases notify the system about the changed filesystems
+             */
+            if (microsd_init)
+                queue_broadcast(SYS_FS_CHANGED, 0);
+
+            sd_enable(false);
+
+            /* Access is now safe */
+            mutex_unlock(&sd_mtx);
+            fat_unlock();
+            }
+            break;
+#endif
+        case SYS_TIMEOUT:
+            if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
+            {
+                idle_notified = false;
+            }
+            else if (!idle_notified)
+            {
+                call_storage_idle_notifys(false);
+                idle_notified = true;
+            }
+            break;
+
+        case SYS_USB_CONNECTED:
+            usb_acknowledge(SYS_USB_CONNECTED_ACK);
+            /* Wait until the USB cable is extracted again */
+            usb_wait_for_disconnect(&sd_queue);
+
+            break;
+        }
+    }
+}
+
+static void init_controller(void)
+{
+    /* reset SD module */
+    SCU_RSTCFG |= (1<<9);
+    sleep(1);
+    SCU_RSTCFG &= ~(1<<9);
+
+    /* set pins functions as SD signals */
+    SCU_IOMUXA_CON |= IOMUX_SD;
+
+    /* enable and unmask SD interrupts in interrupt controller */
+    INTC_IMR |= (1<<10);
+    INTC_IECR |= (1<<10);
+
+    SD_CTRL = SD_PWR_CPU | SD_DETECT_MECH | SD_CLOCK_EN | 0x7D;
+    SD_INT = CMD_RES_INT_EN | DATA_XFER_INT_EN;
+    SD_CARD = SD_CARD_SELECT | SD_CARD_PWR_EN;
+
+    /* setup mmu buffers */
+    MMU_PNRI = 0x1ff;
+    MMU_PNRII = 0x1ff;
+    MMU_CTRL = MMU_MMU0_BUFII | MMU_CPU_BUFI | MMU_BUFII_RESET |
+               MMU_BUFII_BYTE | MMU_BUFI_RESET | MMU_BUFI_WORD;
+
+}
+
+int sd_init(void)
+{
+    int ret;
+
+    semaphore_init(&transfer_completion_signal, 1, 0);
+    semaphore_init(&command_completion_signal, 1, 0);
+
+    init_controller();
+
+    ret = sd_init_card();
+    if(ret < 0)
+        return ret;
+
+    /* init mutex */
+    mutex_init(&sd_mtx);
+
+    queue_init(&sd_queue, true);
+    create_thread(sd_thread, sd_stack, sizeof(sd_stack), 0,
+            sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
+
+    return 0;
+}
+
+int sd_read_sectors(IF_MD2(int drive,) unsigned long start, int count,
+                    void* buf)
+{
+#ifdef HAVE_MULTIDRIVE
+    (void)drive;
+#endif
+    unsigned long response;
+    unsigned int retry_cnt = 0;
+    int cnt, ret = 0;
+    unsigned char *dst;
+
+    mutex_lock(&sd_mtx);
+    sd_enable(true);
+
+    if (count <= 0 || start + count > card_info.numblocks)
+        return -1;
+
+    if(!(card_info.ocr & (1<<30)))
+        start <<= 9; /* not SDHC */
+
+    /* setup A2A DMA CH0 */
+    A2A_ISRC0 = (unsigned long)(&MMU_DATA);
+    A2A_ICNT0 = 512;
+    A2A_LCNT0 = 1;
+    A2A_DOMAIN = 0;
+
+    while (retry_cnt++ < 20)
+    {
+        cnt = count;
+        dst = (unsigned char *)buf;
+
+        ret = 0;
+        retry = false;       /* reset retry flag */
+        mmu_buff_reset();    /* reset recive buff state */
+
+        /* issue read command to the card */
+        if (!send_cmd(SD_READ_MULTIPLE_BLOCK, start, RES_R1, &response))
+        {
+            ret = -4;
+            continue;
+        }
+
+        while (cnt > 0)
+        {
+            if (cnt == 1)
+            {
+                /* last block to tranfer */
+                SD_DATAT = DATA_XFER_START | DATA_XFER_READ |
+                           DATA_BUS_1LINE | DATA_XFER_DMA_DIS |
+                           DATA_XFER_SINGLE;
+            }
+            else
+            {
+                /* more than one block to transfer */
+                SD_DATAT = DATA_XFER_START | DATA_XFER_READ |
+                           DATA_BUS_1LINE | DATA_XFER_DMA_DIS |
+                           DATA_XFER_MULTI;
+            }
+
+            /* wait for transfer completion */
+            semaphore_wait(&transfer_completion_signal, TIMEOUT_BLOCK);
+
+            if (retry)
+            {
+                /* data transfer error */
+                ret = -5;
+                break;
+            }
+
+            /* exchange buffers */
+            mmu_switch_buff();
+
+            last_disk_activity = current_tick;
+
+            /* transfer data from receive buffer to the dest
+             * for (i=0; i<(512/4); i++)
+             *    *dst++ = MMU_DATA;
+             *
+             * below is DMA version in software mode.
+             * SD module provides DMAreq signals and all this
+             * can be done in hardware in theory but I can't
+             * figure this out. OF doesn't use DMA at all.
+             */
+            A2A_IDST0 = (unsigned long)dst;
+            A2A_CON0  = (3<<9) | (1<<6) | (1<<3) | (2<<1) | (1<<0);
+
+            /* wait for DMA engine to finish transfer */
+            while (A2A_DMA_STS & 1);
+
+            dst += 512;
+            cnt--;
+        } /* while (cnt > 0) */
+
+        if (!send_cmd(SD_STOP_TRANSMISSION, 0, RES_R1b, &response))
+            ret = -6;
+
+        /* transfer successfull - leave retry loop */
+        if (ret == 0)
+            break;
+    }
+
+    sd_enable(false);
+    mutex_unlock(&sd_mtx);
+
+    return ret;
+}
+
+/* Not tested */
+int sd_write_sectors(IF_MD2(int drive,) unsigned long start, int count,
+                     const void* buf)
+{
+#ifdef HAVE_MULTIDRIVE
+    (void) drive;
+#endif
+#if defined(BOOTLOADER) /* we don't need write support in bootloader */
+    (void) start;
+    (void) count;
+    (void) buf;
+    return -1;
+#else
+    unsigned long response;
+    unsigned int retry_cnt = 0;
+    int cnt, ret = 0;
+    unsigned char *src;
+    bool card_selected = false;
+
+    mutex_lock(&sd_mtx);
+    sd_enable(true);
+
+    if (count <= 0 || start + count > card_info.numblocks)
+        return -1;
+
+    if(!(card_info.ocr & (1<<30)))
+        start <<= 9; /* not SDHC */
+
+    /* setup A2A DMA CH0 */
+    A2A_IDST0 = (unsigned long)(&MMU_DATA);
+    A2A_ICNT0 = 512;
+    A2A_LCNT0 = 1;
+    A2A_DOMAIN = 0;
+
+    while (retry_cnt++ < 20)
+    {
+        cnt = count;
+        src = (unsigned char *)buf;
+
+        ret = 0;
+        retry = false;       /* reset retry flag */
+        mmu_buff_reset();    /* reset recive buff state */
+
+        if (!send_cmd(SD_WRITE_MULTIPLE_BLOCK, start, RES_R1, &response))
+        {
+            ret = -3;
+            continue;
+        }
+
+        while (cnt > 0)
+        {
+            /* transfer data from receive buffer to the dest
+             * for (i=0; i<(512/4); i++)
+             *    MMU_DATA = *src++;
+             *
+             * Below is DMA version in software mode.
+             */
+
+            A2A_ISRC0 = (unsigned long)src;
+            A2A_CON0  = (3<<9) | (1<<5) | (1<<3) | (2<<1) | (1<<0);
+
+            while (A2A_DMA_STS & 1);
+
+            src += 512;
+
+            /* exchange buffers */
+            mmu_switch_buff();
+
+            if (cnt == 1)
+            {
+                /* last block to tranfer */
+                SD_DATAT = DATA_XFER_START | DATA_XFER_WRITE |
+                           DATA_BUS_1LINE | DATA_XFER_DMA_DIS |
+                           DATA_XFER_SINGLE;
+
+            }
+            else
+            {
+                /* more than one block to transfer */
+                SD_DATAT = DATA_XFER_START | DATA_XFER_WRITE |
+                           DATA_BUS_1LINE | DATA_XFER_DMA_DIS |
+                           DATA_XFER_MULTI;
+
+            }
+
+            /* wait for transfer completion */
+            semaphore_wait(&transfer_completion_signal, TIMEOUT_BLOCK);
+
+            if (retry)
+            {
+                /* data transfer error */
+                ret = -3;
+                break;
+            }
+
+            cnt--;
+        } /* while (cnt > 0) */
+
+        if (!send_cmd(SD_STOP_TRANSMISSION, 0, RES_R1b, &response))
+            ret = -4;
+
+        if (!sd_wait_card_busy())
+            ret = -5;
+
+        /* transfer successfull - leave retry loop */
+        if (ret == 0)
+            break;
+    }
+
+    sd_enable(false);
+    mutex_unlock(&sd_mtx);
+
+    return ret;
+   
+#endif /* defined(BOOTLOADER) */
+}
+
+void sd_enable(bool on)
+{
+    /* enable or disable clock signal for SD module */
+    if (on)
+    {
+        SCU_CLKCFG &= ~(1<<22);
+    }
+    else
+    {
+        SCU_CLKCFG |= (1<<22);
+    }
+}
+
+#ifndef BOOTLOADER
+long sd_last_disk_activity(void)
+{
+    return last_disk_activity;
+}
+
+tCardInfo *card_get_info_target(int card_no)
+{
+    (void)card_no;
+    return &card_info;
+}
+#endif /* BOOTLOADER */
+
+#ifdef HAVE_HOTSWAP
+/* Not complete and disabled in config */
+bool sd_removable(IF_MD_NONVOID(int drive))
+{
+    (void)drive;
+    return true;
+}
+
+bool sd_present(IF_MD_NONVOID(int drive))
+{
+    (void)drive;
+    return card_detect_target();
+}
+
+static int sd_oneshot_callback(struct timeout *tmo)
+{
+    (void)tmo;
+
+    /* This is called only if the state was stable for 300ms - check state
+     * and post appropriate event. */
+    if (card_detect_target())
+    {
+        queue_broadcast(SYS_HOTSWAP_INSERTED, 0);
+    }
+    else
+        queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0);
+
+    return 0;
+}
+
+/* interrupt handler for SD detect */
+
+#endif /* HAVE_HOTSWAP */
+
+#ifdef CONFIG_STORAGE_MULTI
+int sd_num_drives(int first_drive)
+{
+    (void)first_drive;
+
+    /* we have only one SD drive */
+    return 1;
+}
+#endif /* CONFIG_STORAGE_MULTI */
diff --git a/firmware/target/arm/rk27xx/spfd5420a.h b/firmware/target/arm/rk27xx/spfd5420a.h
new file mode 100644
index 0000000..fcb998d
--- /dev/null
+++ b/firmware/target/arm/rk27xx/spfd5420a.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#define ID_READ                 0x000
+#define DRIVER_OUT_CTRL         0x001
+#define WAVEFORM_CTRL           0x002
+#define ENTRY_MODE              0x003
+/* 0x004 - 0x005 reserved */
+#define SHAPENING_CTRL          0x006 /* not present in datasheet */
+#define DISPLAY_CTRL1           0x007
+#define DISPLAY_CTRL2           0x008
+#define LOW_PWR_CTRL1           0x009
+/* 0x00A reserved */
+#define LOW_PWR_CTRL2           0x00B
+#define EXT_DISP_CTRL1          0x00C
+/* 0x00D - 0x00E reserved */
+#define EXT_DISP_CTRL2          0x00F
+#define PANEL_IF_CTRL1          0x010
+#define PANEL_IF_CTRL2          0x011
+#define PANEL_IF_CTRL3          0x012
+/* 0x013 - 0x01F reserved */
+#define PANEL_IF_CTRL4          0x020
+#define PANEL_IF_CTRL5          0x021
+#define PANEL_IF_CTRL6          0x022
+/* 0x023 - 0x08F reserved */
+#define FRAME_MKR_CTRL          0x090
+/* 0x091 reserved */
+#define MDDI_CTRL               0x092 /* not present in datasheet */
+/* 0x093 - 0x0FF reserved */
+#define PWR_CTRL1               0x100
+#define PWR_CTRL2               0x101
+#define PWR_CTRL3               0x102
+#define PWR_CTRL4               0x103 /* amplitude to VCOM */
+/* 0x104 - 0x106 reserved */
+#define PWR_CTRL5               0x107
+/* 0x108 - 0x10F reserved */
+#define PWR_CTRL6               0x110
+#define PWR_CTRL7               0x112 /* not present in datasheet */
+/* 0x113 - 0x1FF reserved */
+#define GRAM_H_ADDR             0x200
+#define GRAM_V_ADDR             0x201
+#define GRAM_READ               0x202
+#define GRAM_WRITE              0x202
+/* 0x203 - 0x20F reserved */
+#define WINDOW_H_START          0x210
+#define WINDOW_H_END            0x211
+#define WINDOW_V_START          0x212
+#define WINDOW_V_END            0x213
+/* 0x214 - 0x27F reserved */
+#define NVM_READ                0x280
+#define NVM_WRITE               0x280
+#define VCOM_HV1                0x281
+#define VCOM_HV2                0x282
+/* 0x283 - 0x2FF reserved */
+#define GAMMA_CTRL1             0x300
+#define GAMMA_CTRL2             0x301
+#define GAMMA_CTRL3             0x302
+#define GAMMA_CTRL4             0x303
+#define GAMMA_CTRL5             0x304
+#define GAMMA_CTRL6             0x305
+#define GAMMA_CTRL7             0x306
+#define GAMMA_CTRL8             0x307
+#define GAMMA_CTRL9             0x308
+#define GAMMA_CTRL10            0x309
+#define GAMMA_CTRL11            0x30A
+#define GAMMA_CTRL12            0x30B
+#define GAMMA_CTRL13            0x30C
+#define GAMMA_CTRL14            0x30D
+#define GAMMA_CTRL15            0x30E
+#define GAMMA_CTRL16            0x30F
+/* 0x310 - 0x3FF reserved */
+#define BASE_IMG_SIZE           0x400
+#define BASE_IMG_CTRL           0x401
+/* 0x402 - 0x403 reserved */
+#define VSCROLL_CTRL            0x404
+/* 0x405 - 0x4FF reserved */
+#define PART1_POS               0x500
+#define PART1_START             0x501
+#define PART1_END               0x502
+#define PART2_POS               0x503
+#define PART2_START             0x504
+#define PART2_END               0x505
+/* 0x506 - 0x5FF reserved */
+#define RESET                   0x600 /* not present in datasheet */
+/* 0x601 - 0x605 */
+#define IF_ENDIAN               0x606
+/* 0x607 - 0x6EF reserved */
+#define NVM_CTRL                0x6F0
+/* 0x6F1 - 0xFFF reserved */
diff --git a/firmware/target/arm/rk27xx/system-rk27xx.c b/firmware/target/arm/rk27xx/system-rk27xx.c
new file mode 100644
index 0000000..67024a5
--- /dev/null
+++ b/firmware/target/arm/rk27xx/system-rk27xx.c
@@ -0,0 +1,164 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "kernel.h"
+#include "system.h"
+#include "panic.h"
+#include "system-target.h"
+
+#define default_interrupt(name) \
+  extern __attribute__((weak,alias("UIRQ"))) void name (void)
+
+void irq_handler(void) __attribute__((interrupt ("IRQ"), naked));
+void fiq_handler(void) __attribute__((interrupt ("FIQ"), naked, \
+                                      weak, alias("fiq_dummy")));
+
+default_interrupt(INT_UART0);
+default_interrupt(INT_UART1);
+default_interrupt(INT_TIMER0);
+default_interrupt(INT_TIMER1);
+default_interrupt(INT_TIMER2);
+default_interrupt(INT_GPIO0);
+default_interrupt(INT_SW_INT0);
+default_interrupt(INT_AHB0_MAILBOX);
+default_interrupt(INT_RTC);
+default_interrupt(INT_SCU);
+default_interrupt(INT_SD);
+default_interrupt(INT_SPI);
+default_interrupt(INT_HDMA);
+default_interrupt(INT_A2A_BRIDGE);
+default_interrupt(INT_I2C);
+default_interrupt(INT_I2S);
+default_interrupt(INT_UDC);
+default_interrupt(INT_UHC);
+default_interrupt(INT_PWM0);
+default_interrupt(INT_PWM1);
+default_interrupt(INT_PWM2);
+default_interrupt(INT_ADC);
+default_interrupt(INT_GPIO1);
+default_interrupt(INT_VIP);
+default_interrupt(INT_DWDMA);
+default_interrupt(INT_NANDC);
+default_interrupt(INT_LCDC);
+default_interrupt(INT_DSP);
+default_interrupt(INT_SW_INT1);
+default_interrupt(INT_SW_INT2);
+default_interrupt(INT_SW_INT3);
+
+static void (* const irqvector[])(void) =
+{
+    INT_UART0,INT_UART1,INT_TIMER0,INT_TIMER1,INT_TIMER2,INT_GPIO0,INT_SW_INT0,INT_AHB0_MAILBOX,
+    INT_RTC,INT_SCU,INT_SD,INT_SPI,INT_HDMA,INT_A2A_BRIDGE,INT_I2C,
+    INT_I2S,INT_UDC,INT_UHC,INT_PWM0,INT_PWM1,INT_PWM2,INT_ADC,INT_GPIO1,
+    INT_VIP,INT_DWDMA,INT_NANDC,INT_LCDC,INT_DSP,INT_SW_INT1,INT_SW_INT2,INT_SW_INT3
+};
+
+static const char * const irqname[] =
+{
+    "INT_UART0","INT_UART1","INT_TIMER0","INT_TIMER1","INT_TIMER2","INT_GPIO0","INT_SW_INT0","INT_AHB0_MAILBOX",
+    "INT_RTC","INT_SCU","INT_SD","INT_SPI","INT_HDMA","INT_A2A_BRIDGE","INT_I2C",
+    "INT_I2S","INT_UDC","INT_UHC","INT_PWM0","INT_PWM1","INT_PWM2","INT_ADC","INT_GPIO1",
+    "INT_VIP","INT_DWDMA","INT_NANDC","INT_LCDC","INT_DSP","INT_SW_INT1","INT_SW_INT2","INT_SW_INT3"
+};
+
+static void UIRQ(void)
+{
+    unsigned int offset = INTC_ISR & 0x1f;
+    panicf("Unhandled IRQ %02X: %s", offset, irqname[offset]);
+}
+
+void irq_handler(void)
+{
+    /*
+     * Based on: linux/arch/arm/kernel/entry-armv.S and system-meg-fx.c
+     */
+
+    asm volatile(   "stmfd sp!, {r0-r7, ip, lr} \n"   /* Store context */
+                    "sub   sp, sp, #8           \n"); /* Reserve stack */
+
+    int irq_no = INTC_ISR & 0x1f;
+    
+    irqvector[irq_no]();
+
+    /* clear interrupt */
+    INTC_ICCR = (1 << irq_no);
+
+    asm volatile(   "add   sp, sp, #8           \n"   /* Cleanup stack   */
+                    "ldmfd sp!, {r0-r7, ip, lr} \n"   /* Restore context */
+                    "subs  pc, lr, #4           \n"); /* Return from IRQ */
+}
+
+void fiq_dummy(void)
+{
+    asm volatile (
+        "subs   pc, lr, #4   \r\n"
+    );
+}
+
+
+void system_init(void)
+{
+    return;
+}
+
+/* not tested */
+void system_reboot(void)
+{
+    /* use Watchdog to reset */
+    WDTLR = 1;
+    WDTCON = (1<<4) | (1<<3);
+
+    /* Wait for reboot to kick in */
+    while(1);
+}
+
+void system_exception_wait(void)
+{
+    while(1);
+}
+
+int system_memory_guard(int newmode)
+{
+    (void)newmode;
+    return 0;
+}
+
+/* usecs may be at most 2^32/200 (~21 seconds) for 200MHz max cpu freq */
+void udelay(unsigned usecs)
+{
+    unsigned cycles_per_usec;
+    unsigned delay;
+
+    if (cpu_frequency == CPUFREQ_MAX) {
+        cycles_per_usec = (CPUFREQ_MAX + 999999) / 1000000;
+    } else {
+        cycles_per_usec = (CPUFREQ_NORMAL + 999999) / 1000000;
+    }
+
+    delay = (usecs * cycles_per_usec + 3) / 4;
+
+    asm volatile(
+        "1: subs %0, %0, #1  \n"    /* 1 cycle  */
+        "   bne  1b          \n"    /* 3 cycles */
+        : : "r"(delay)
+    );
+}
+
diff --git a/firmware/target/arm/rk27xx/system-target.h b/firmware/target/arm/rk27xx/system-target.h
new file mode 100644
index 0000000..fb90409
--- /dev/null
+++ b/firmware/target/arm/rk27xx/system-target.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2008 Rafaël Carré
+ * Copyright (C) 2011 Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef SYSTEM_TARGET_H
+#define SYSTEM_TARGET_H
+
+#include "system-arm.h"
+#include "panic.h"
+
+void udelay(unsigned usecs);
+static inline void mdelay(unsigned msecs)
+{
+    udelay(1000 * msecs);
+}
+
+/* this needs more testing */
+static inline void core_sleep(void)
+{
+    enable_irq();
+    SCU_CPUPD = 0xdeedbabe;
+}
+
+#define CPUFREQ_NORMAL 200000000
+#define CPUFREQ_MAX    200000000
+
+#endif /* SYSTEM_TARGET_H */
diff --git a/firmware/target/arm/rk27xx/timer-rk27xx.c b/firmware/target/arm/rk27xx/timer-rk27xx.c
new file mode 100644
index 0000000..4493ccd
--- /dev/null
+++ b/firmware/target/arm/rk27xx/timer-rk27xx.c
@@ -0,0 +1,74 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2011 by Marcin Bukat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+
+#include "inttypes.h"
+#include "system.h"
+#include "timer.h"
+
+void INT_TIMER1(void)
+{
+    /* clear interrupt */
+    TMR1CON &= ~(1<<2);
+
+    if (pfn_timer != NULL)
+    {
+        pfn_timer();
+    }
+}
+
+bool timer_set(long cycles, bool start)
+{
+    /* optionally unregister any previously registered timer user */
+    if (start)
+    {
+        if (pfn_unregister != NULL)
+        {
+            pfn_unregister();
+            pfn_unregister = NULL;
+        }
+    }
+
+    TMR1LR = cycles;
+    TMR1CON = (1<<7) | (1<<1); /* periodic, 1/1 */
+
+    /* unmask timer1 interrupt */
+    INTC_IMR |= (1<<3);
+
+    /* enable timer1 interrupt */
+    INTC_IECR |= (1<<3);
+
+    return true;
+}
+
+bool timer_start(void)
+{
+    TMR1CON |= (1 << 8);       /* timer1 enable */
+
+    return true;
+}
+
+void timer_stop(void)
+{
+    TMR1CON &= ~(1 << 8);       /* timer1 disable */
+}
+
diff --git a/firmware/target/arm/thread-arm.c b/firmware/target/arm/thread-arm.c
index 84a3aec..83bdb92 100644
--- a/firmware/target/arm/thread-arm.c
+++ b/firmware/target/arm/thread-arm.c
@@ -109,7 +109,7 @@ static inline void core_sleep(void)
 }
 #else
 /* Skip this if special code is required and implemented */
-#ifndef CPU_PP
+#if !(defined(CPU_PP)) && CONFIG_CPU != RK27XX
 static inline void core_sleep(void)
 {
     #warning core_sleep not implemented, battery life will be decreased
diff --git a/tools/configure b/tools/configure
index 96953c8..d988e75 100755
--- a/tools/configure
+++ b/tools/configure
@@ -500,6 +500,13 @@ arm1176jzscc () {
  endian="little"
 }
 
+arm7ejscc () {
+ findarmgcc
+ GCCOPTS="$CCOPTS -march=armv5te"
+ GCCOPTIMIZE="-fomit-frame-pointer"
+ endian="little"
+}
+
 mipselcc () {
  prefixtools mipsel-elf-
  # mips is predefined, but we want it for paths. use __mips instead
@@ -1271,8 +1278,8 @@ cat <<EOF
  200) SDL                 170) HD200             131) Mini2440
  201) Android             171) HD300
  202) Nokia N8xx
- 203) Nokia N900
- 204) Pandora
+ 203) Nokia N900          ==ROCKCHIP==
+ 204) Pandora             180) rk27xx generic
 
 EOF
 
@@ -2969,6 +2976,29 @@ fi
     t_model="hd300"
     ;;
 
+   180|rk27generic)
+    target_id=78
+    modelname="rk27generic"
+    target="-DRK27_GENERIC"
+    memory=16 # always
+    arm7ejscc
+    tool="$rootdir/tools/scramble -add=rk27"
+    bmp2rb_mono="$rootdir/tools/bmp2rb -f 0"
+    bmp2rb_native="$rootdir/tools/bmp2rb -f 4"
+    output="rockbox.rk27"
+    bootoutput="bootloader.rk27"
+    appextra="recorder:gui:radio"
+    plugins="yes"
+    swcodec="yes"
+    # toolset is the tools within the tools directory that we build for
+    # this particular target.
+    toolset="$genericbitmaptools"
+    # architecture, manufacturer and model for the target-tree build
+    t_cpu="arm"
+    t_manufacturer="rk27xx"
+    t_model="rk27generic"
+    ;;
+
    200|sdlapp)
     application="yes"
     target_id=73
diff --git a/tools/scramble.c b/tools/scramble.c
index dbf41fd..6839733 100644
--- a/tools/scramble.c
+++ b/tools/scramble.c
@@ -126,7 +126,7 @@ void usage(void)
     printf("\t                   9200, 1630, 6330, ldax, m200, c100, clip, e2v2,\n"
            "\t                   m2v4, fuze, c2v2, clv2, y820, y920, y925, x747,\n"
            "\t                   747p, x777, nn2g, m244, cli+, fuz2, hd20, hd30,\n"
-           "\t                   ip6g)\n");
+           "\t                   ip6g, rk27)\n");
     printf("\nNo option results in Archos standard player/recorder format.\n");
 
     exit(1);
@@ -337,6 +337,8 @@ int main (int argc, char** argv)
             modelnum = 71;
         else if (!strcmp(&argv[1][5], "fuz+")) /* Sansa Fuze+ */
             modelnum = 72;
+        else if (!strcmp(&argv[1][5], "rk27")) /* rockchip 27xx generic */
+            modelnum = 73;
         else {
             fprintf(stderr, "unsupported model: %s\n", &argv[1][5]);
             return 2;
diff --git a/utils/rk27utils/README b/utils/rk27utils/README
new file mode 100644
index 0000000..a43d69a
--- /dev/null
+++ b/utils/rk27utils/README
@@ -0,0 +1,37 @@
+This is the collection of small utilities needed to hack Rockchip rk27xx
+series based DAPs. This tools were tested on linux only. 
+
+
+rk27load
+This directory contains tool which can send arbitrary image(s) to the device
+in rockchip recovery mode (VID:PID 0x071B:0x3201).
+
+The first image can not exceed 510 bytes (+2 bytes checksum) and entry
+point is 0x18020e00. Usually this code is used to configure SDRAM controller.
+One can use first stage image extracted from Rock27Boot.bin file (a bit
+more sofisticated) or the one provided in rk27load/stage1 directory.
+
+The second image is loaded at the begining of the dram (0x60000000)
+and executed. For some reason (which is still unclear) the size of 
+2nd stage image is limited to about 3-4 kB.
+
+You can find example of custom 2nd stage image in rk27load/stage2 directory.
+The purpose of this image is to configure bulk transfer and allow to
+load usercode without size restriction mentioned above (the max size
+is 8MB actually). The entry point of usercode is 0x60000000.
+
+You need libusb 1.0 + header files in order to compile this utility.
+You need working arm-eabi crosscompiler in order to compile stage1/stage2
+bootloader binaries (but You should have one already if You tinker whith this)
+
+
+rkboottool
+This directory contains tool which allows to extract (and decrypt) images
+stored in Rock27Boot.bin recovery file.
+
+
+rkusbtool
+This directory contains tool which sends custom scsi commands to the
+rockchip player.
+
+You need libusb-1.0 + header files in order to compile this utility.
diff --git a/utils/rk27utils/rk27load/Makefile b/utils/rk27utils/rk27load/Makefile
new file mode 100644
index 0000000..f777e76
--- /dev/null
+++ b/utils/rk27utils/rk27load/Makefile
@@ -0,0 +1,7 @@
+all: rk27load
+
+rk27load: main.c scramble.c checksum.c common.c stage1_upload.c stage2_upload.c stage3_upload.c
+	gcc -g -std=c99 -o $@ -W -Wall -lusb-1.0 -I/usr/include/libusb-1.0/ $^
+
+clean:
+	rm -fr *.o rk27load
diff --git a/utils/rk27utils/rk27load/checksum.c b/utils/rk27utils/rk27load/checksum.c
new file mode 100644
index 0000000..f0fe593
--- /dev/null
+++ b/utils/rk27utils/rk27load/checksum.c
@@ -0,0 +1,35 @@
+#include <stdint.h>
+#include "checksum.h"
+
+uint16_t checksum(void *buff, uint32_t size)
+{
+        uint32_t r2 = 0xffff;
+        uint32_t r3 = 0;
+        uint32_t i, j;
+
+        for (i=0; i<size; i++) {
+                r3 = 0x80;
+                for (j=0; j<8; j++) {
+                        if ((r2 & 0x8000) != 0) {
+                                r2 <<= 17;
+                                r2 >>= 16;
+                                r2 ^= 0x1000;
+                                r2 ^= 0x21;
+                        }
+                        else {
+                                r2 <<= 17;
+                                r2 >>= 16;
+                        }
+
+                        if ((((uint8_t *)buff)[i] & r3) != 0) {
+                                r2 ^= 0x1000;
+                                r2 ^= 0x21;
+                        }
+
+                        r3 >>= 1;
+                }
+        }
+
+        return r2 & 0xffff;
+}
+
diff --git a/utils/rk27utils/rk27load/checksum.h b/utils/rk27utils/rk27load/checksum.h
new file mode 100644
index 0000000..468ca66
--- /dev/null
+++ b/utils/rk27utils/rk27load/checksum.h
@@ -0,0 +1 @@
+uint16_t checksum(void *buff, uint32_t size);
diff --git a/utils/rk27utils/rk27load/common.c b/utils/rk27utils/rk27load/common.c
new file mode 100644
index 0000000..b97cfbc
--- /dev/null
+++ b/utils/rk27utils/rk27load/common.c
@@ -0,0 +1,16 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include "common.h"
+
+uint32_t filesize(FILE * f)
+{
+    uint32_t filesize;
+
+    fseek(f, 0, SEEK_END);
+    filesize = ftell(f);
+    fseek(f, 0, SEEK_SET);
+
+    return filesize;
+}
+
diff --git a/utils/rk27utils/rk27load/common.h b/utils/rk27utils/rk27load/common.h
new file mode 100644
index 0000000..f22ec7d
--- /dev/null
+++ b/utils/rk27utils/rk27load/common.h
@@ -0,0 +1 @@
+uint32_t filesize(FILE * f);
diff --git a/utils/rk27utils/rk27load/main.c b/utils/rk27utils/rk27load/main.c
new file mode 100644
index 0000000..d183ae2
--- /dev/null
+++ b/utils/rk27utils/rk27load/main.c
@@ -0,0 +1,165 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <libusb.h>
+
+#include "rk27load.h"
+#include "common.h"
+#include "stage1_upload.h"
+#include "stage2_upload.h"
+#include "stage3_upload.h"
+
+#define VERSION "v0.2"
+
+enum {
+      NONE = 0,
+      ENCODE_S1 = 1,
+      ENCODE_S2 = 2
+};
+
+static void usage(char *name)
+{
+    printf("usage: (sudo) %s [-e1 -e2] -s1 stage1.bin -s2 stage2.bin -s3 usercode.bin\n", name);
+    printf("stage1.bin - binary of the stage1 (sdram init)\n");
+    printf("stage2.bin - binary of the stage2 bootloader\n");
+    printf("usercode.bin - binary of the custom usercode\n");
+    printf("\n");
+    printf("options:\n");
+    printf("-e1 - encode stage1 bootloader\n");
+    printf("-e2 - encode stage2 bootloader\n");    
+}
+
+int main(int argc, char **argv)
+{
+    libusb_device_handle *hdev;
+    char *filenames[3];
+    int i=1, action=0, ret=0;
+
+    while (i < argc)
+    {
+        if (strcmp(argv[i],"-e1") == 0)
+        {
+            action |= ENCODE_S1;
+            i++;
+        }
+        else if (strcmp(argv[i],"-e2") == 0)
+        {
+            action |= ENCODE_S2;
+            i++;
+        }
+        else if (strcmp(argv[i],"-s1") == 0)
+        {
+            i++;
+            if (i == argc)
+            {
+                usage(argv[0]);
+                return -1;
+            }
+            filenames[0] = argv[i];
+            printf("%s", argv[i]);
+            i++;
+        }
+        else if (strcmp(argv[i],"-s2") == 0)
+        {
+            i++;
+            if (i == argc)
+            {
+                usage(argv[0]);
+                return -2;
+            }
+            filenames[1] = argv[i];
+            i++;
+        }
+        else if (strcmp(argv[i],"-s3") == 0)
+        {
+            i++;
+            if (i == argc)
+            {
+                usage(argv[0]);
+                return -3;
+            }
+            filenames[2] = argv[i];
+            i++;
+        }
+        else
+        {
+            usage(argv[0]);
+            return -4;
+        }
+    }
+
+ 
+    fprintf(stderr,"rk27load " VERSION "\n");
+    fprintf(stderr,"(C) Marcin Bukat 2011\n");
+    fprintf(stderr,"Based on rk27load ver. 0.1 written by AleMaxx (alemaxx at hotmail.de)\n\n");
+    fprintf(stderr,"This is free software; see the source for copying conditions.  There is NO\n");
+    fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+
+    /* initialize libusb */
+    libusb_init(NULL);
+
+    /* configure device */
+    fprintf(stderr, "[info]: Initializing device... ");
+    hdev = libusb_open_device_with_vid_pid(NULL, VENDORID, PRODUCTID);
+
+    if (hdev == NULL)
+    {
+        fprintf(stderr, "\n[error]: Could not find rockchip device\n");
+        ret = -2;
+        goto finish;
+    }
+
+    ret = libusb_set_configuration(hdev, 1);
+    if (ret < 0)
+    {
+        fprintf(stderr, "\n[error]: Could not select configuration (1)\n");
+        ret = -3;
+        goto finish;
+    }
+
+    ret = libusb_claim_interface(hdev, 0);
+    if (ret < 0)
+    {
+        fprintf(stderr, "\n[error]: Could not claim interface #0\n");
+        ret = -4;
+        goto finish;
+    }
+
+    ret = libusb_set_interface_alt_setting(hdev, 0, 0);
+    if (ret < 0)
+    {
+        fprintf(stderr, "\n[error]: Could not set alternate interface #0\n");
+        ret = -5;
+        goto finish;
+    }
+
+    fprintf(stderr, "done\n");
+
+
+    ret = upload_stage1_code(hdev, filenames[0], (action & ENCODE_S1));
+    if (ret < 0)
+        goto finish;
+
+    ret = upload_stage2_code(hdev, filenames[1], (action & ENCODE_S2));
+    if (ret < 0)
+        goto finish;
+
+    ret = upload_stage3_code(hdev, filenames[2]);
+    if (ret < 0)
+        goto finish;
+
+    /* done */
+    ret = 0;
+
+  finish:
+    if (hdev != NULL)
+        libusb_close(hdev);
+
+    if (ret < 0)
+        fprintf(stderr, "[error]: Error %d\n", ret);
+
+    return ret;
+}
diff --git a/utils/rk27utils/rk27load/rk27load.h b/utils/rk27utils/rk27load/rk27load.h
new file mode 100644
index 0000000..8239176
--- /dev/null
+++ b/utils/rk27utils/rk27load/rk27load.h
@@ -0,0 +1,11 @@
+#define USB_TIMEOUT     512
+
+#define VENDORID        0x71b
+#define PRODUCTID       0x3201
+
+#define USB_EP0         0x41
+
+#define VCMD_UPLOAD             0x0c
+#define VCMD_INDEX_STAGE1       0x471
+#define VCMD_INDEX_STAGE2       0x472
+
diff --git a/utils/rk27utils/rk27load/scramble.c b/utils/rk27utils/rk27load/scramble.c
new file mode 100644
index 0000000..7e5b151
--- /dev/null
+++ b/utils/rk27utils/rk27load/scramble.c
@@ -0,0 +1,46 @@
+#include <stdint.h>
+#include "scramble.h"
+
+void scramble(uint8_t *in, uint8_t *out, const int size)
+{
+    /* table extracted from bootrom */
+    static const uint8_t key[] = {
+        0x7C, 0x4E, 0x03, 0x04,
+        0x55, 0x05, 0x09, 0x07,
+        0x2D, 0x2C, 0x7B, 0x38,
+        0x17, 0x0D, 0x17, 0x11
+    };
+
+        int i, i3, x, val, idx;
+
+        uint8_t key1[0x100];
+        uint8_t key2[0x100];
+
+        for (i=0; i<0x100; i++) {
+                key1[i] = i;
+                key2[i] = key[i&0xf];
+        }
+
+        i3 = 0;
+        for (i=0; i<0x100; i++) {
+                x = key1[i];
+                i3 = key1[i] + i3;
+                i3 += key2[i];
+                i3 &= 0xff;
+                key1[i] = key1[i3];
+                key1[i3] = x;
+        }
+
+        idx = 0;
+        for (i=0; i<size; i++) {
+                x = key1[(i+1) & 0xff];
+                val = x;
+                idx = (x + idx) & 0xff;
+                key1[(i+1) & 0xff] = key1[idx];
+                key1[idx] = (x & 0xff);
+                val = (key1[(i+1)&0xff] + x) & 0xff;
+                val = key1[val];
+                out[i] = val ^ in[i];
+        }
+}
+
diff --git a/utils/rk27utils/rk27load/scramble.h b/utils/rk27utils/rk27load/scramble.h
new file mode 100644
index 0000000..ed4b291
--- /dev/null
+++ b/utils/rk27utils/rk27load/scramble.h
@@ -0,0 +1 @@
+void scramble(uint8_t *in, uint8_t *out, const int size);
diff --git a/utils/rk27utils/rk27load/stage1/Makefile b/utils/rk27utils/rk27load/stage1/Makefile
new file mode 100644
index 0000000..5291685
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage1/Makefile
@@ -0,0 +1,48 @@
+
+TARGET	= stage1
+
+TOOLCHAIN	= arm-elf-eabi-
+
+CC		= $(TOOLCHAIN)gcc
+CPP		= $(TOOLCHAIN)cpp
+LD		= $(TOOLCHAIN)gcc
+AS		= $(TOOLCHAIN)as
+OBJCOPY	= $(TOOLCHAIN)objcopy
+OBJDUMP	= $(TOOLCHAIN)objdump
+
+CFLAGS	= -Wundef -marm -march=armv5te -nostdlib -mfpu=fpa -O0 -c
+#ASFLAGS	= -mcpu=arm926ej-s
+
+OBJS	= main.o
+LDSCRIPT= stage1.lds
+
+#LIBDIRS	= -L../arm/lib/gcc/arm-elf/4.1.0/ -L../lib
+#LIBS	= -lgcc
+LIBS    =
+LDFLAGS	= -Wundef -marm -march=armv5te -T$(LDSCRIPT) -nostartfiles  \
+	-mfpu=fpa -nostdlib -Xlinker -Map=$(TARGET).map
+
+all		: $(TARGET).bin
+		ls -ls $(TARGET).bin
+
+%.o		: %.c
+		$(CC) $(CPPFLAGS) $(CFLAGS) $(INCDIRS) $< -o $@
+
+%.o		: %.S
+		$(CC) $(CFLAGS) -c $< -o $@
+
+$(TARGET).elf : $(OBJS)
+		$(LD) $(LDFLAGS) $(OBJS) $(LIBDIRS) $(LIBS) -o $(TARGET).elf
+
+$(TARGET).bin : $(TARGET).elf
+		$(OBJCOPY) -O binary $(TARGET).elf $(TARGET).bin
+
+dasm	: $(TARGET).bin
+		$(OBJDUMP) -m arm -D $(TARGET).elf | cat > $(TARGET).asm
+
+clean	:
+		rm -f $(OBJS)
+		rm -f $(TARGET).elf
+		rm -f $(TARGET).bin
+		rm -f $(TARGET).asm
+		rm -f $(TARGET).map
diff --git a/utils/rk27utils/rk27load/stage1/main.S b/utils/rk27utils/rk27load/stage1/main.S
new file mode 100644
index 0000000..d6abdbf
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage1/main.S
@@ -0,0 +1,44 @@
+.section .text,"ax",%progbits
+.global start
+
+start:
+    msr     cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */
+
+pll_setup:
+    mov     r0, #0x18000000
+    add     r0, r0, #0x1c000
+
+    /* setup ARM core freq = 200MHz */
+    /* AHB bus freq (HCLK) = 100MHz */
+    /* APB bus freq (PCLK) = 50MHz  */
+    ldr     r1, [r0,#0x14]   /* SCU_DIVCON1 */
+    orr     r1, #9           /* ARM slow mode, HCLK:PCLK = 2:1 */
+    str     r1, [r0,#0x14]
+
+    ldr     r1,=0x01970c70   /* (1<<24) | (1<<23) | (23<<16) | (199<<4) */
+    str     r1, [r0,#0x08]
+
+    ldr     r2,=0x40000
+1:
+    ldr     r1, [r0,#0x2c]   /* SCU_STATUS */
+    tst     r1, #1           /* ARM pll lock */
+    bne     1f
+    subs    r2, #1
+    bne     1b
+1:
+    ldr     r1, [r0,#0x14]   /* SCU_DIVCON1 */
+    bic     r1, #5           /* leave ARM slow mode, ARMclk:HCLK = 2:1 */
+    str     r1, [r0,#0x14]
+
+sdram_config:
+    add     r0, r0, #0x94000 /* SDRAM base */
+
+    mov     r1, #1
+    str     r1, [r0,#0x104]  /* MCSDR_ADDMAP 64MB module0, CS0 used*/
+
+    str     r1, [r0,#0x10c]  /* MCSDR_BASIC Round-robin, SDRAM width 16bits */
+
+    add     r1, #0x10
+    str     r1, [r0,#0x108] /* MCSDR_ADDCFG 12 bits row/9 bits col addr */
+
+    bx      lr               /* we are done, return to bootrom code */ 
diff --git a/utils/rk27utils/rk27load/stage1/stage1.lds b/utils/rk27utils/rk27load/stage1/stage1.lds
new file mode 100644
index 0000000..4af8b93
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage1/stage1.lds
@@ -0,0 +1,23 @@
+ENTRY(start)
+OUTPUT_FORMAT(elf32-littlearm)
+OUTPUT_ARCH(arm)
+/* STARTUP(crt0.o) */
+
+/* this is where bootrom loads sdram init code */
+MEMORY
+{
+    IRAM  : ORIGIN = 0x18200E00, LENGTH = 0x00000200
+}
+
+SECTIONS
+{
+    .text : {
+      *(.text*)
+      *(.glue_7*)
+    } > IRAM
+
+    .data : {
+      *(.rodata*)
+      *(.data*)
+    } > IRAM
+}
diff --git a/utils/rk27utils/rk27load/stage1_upload.c b/utils/rk27utils/rk27load/stage1_upload.c
new file mode 100644
index 0000000..8eb4ae9
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage1_upload.c
@@ -0,0 +1,113 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+
+#include "rk27load.h"
+#include "common.h"
+#include "scramble.h"
+#include "checksum.h"
+#include "stage1_upload.h"
+
+/* ### upload sdram init code ### */
+int upload_stage1_code(libusb_device_handle *hdev, char *fn_stage1,
+                       bool do_scramble)
+{
+    FILE *f;
+    int ret;
+    uint8_t *code;
+    uint32_t codesize;
+    uint16_t cks;
+
+    if ((f = fopen(fn_stage1, "rb")) == NULL)
+    {
+        fprintf(stderr, "[error]: Could not open file \"%s\"\n", fn_stage1);
+        return -10;
+    }
+
+    codesize = filesize(f);
+
+    if (codesize > 0x1fe)
+    {
+        fprintf(stderr, "[error]: Code too big for stage1\n");
+        return -11;
+    }
+
+    fprintf(stderr, "[stage1]: Loading %d bytes (%s) of code... ", codesize, fn_stage1);
+
+    code = (uint8_t *)malloc(0x200);
+    if (code == NULL)
+    {
+        fprintf(stderr, "\n[error]: Out of memory\n");
+        fclose(f);
+        return -12;
+    }
+
+    memset(code, 0, 0x200);
+    if (fread(code, 1, codesize, f) != codesize)
+    {
+        fprintf(stderr, "\n[error]: I/O error\n");
+        fclose(f);
+        free(code);
+        return -13;
+    }
+
+    fprintf(stderr, "done\n");
+    fclose(f);
+
+    /* encode data if requested */
+    if (do_scramble)
+    {
+
+        fprintf(stderr, "[stage1]: Encoding %d bytes of data ... ", codesize);
+        scramble(code, code, codesize);
+        fprintf(stderr, "done\n");
+    }
+
+
+    fprintf(stderr, "[stage1]: codesize = %d (0x%x)\n", codesize, codesize);
+
+    fprintf(stderr,  "[stage1]: Calculating checksum... ");
+    cks = checksum((void *)code, codesize);
+    fprintf(stderr, "0x%04x\n", cks);
+    code[0x1fe] = (cks >> 8) & 0xff;
+    code[0x1ff] = cks & 0xff;
+    codesize += 2;
+
+    fprintf(stderr, "[stage1]: Uploading code (%d bytes)... ", codesize);
+
+    ret = libusb_control_transfer(hdev,              /* device handle */
+                                  USB_EP0,           /* bmRequestType */
+                                  VCMD_UPLOAD,       /* bRequest */
+                                  0,                 /* wValue */
+                                  VCMD_INDEX_STAGE1, /* wIndex */
+                                  code,              /* data */
+                                  codesize,          /* wLength */
+                                  USB_TIMEOUT        /* timeout */
+                                 );
+    if (ret < 0)
+    {
+        fprintf(stderr, "\n[error]: Code upload request failed (ret=%d)\n", ret);
+        free(code);
+        return -14;
+    }
+
+    if (ret != (int)codesize)
+    {
+        fprintf(stderr, "\n[error]: Sent %d of %d total\n", ret, codesize);
+        free(code);
+        return -15;
+    }
+
+    sleep(1);               /* wait for code to finish */
+    fprintf(stderr, "done\n");
+
+    /* free code */
+    free(code);
+
+    return 0;
+}
+
diff --git a/utils/rk27utils/rk27load/stage1_upload.h b/utils/rk27utils/rk27load/stage1_upload.h
new file mode 100644
index 0000000..efb1c34
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage1_upload.h
@@ -0,0 +1,3 @@
+int upload_stage1_code(libusb_device_handle * hdev, char *fn_stage1,
+                       bool do_scramble);
+
diff --git a/utils/rk27utils/rk27load/stage2/Makefile b/utils/rk27utils/rk27load/stage2/Makefile
new file mode 100644
index 0000000..4b216db
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2/Makefile
@@ -0,0 +1,48 @@
+
+TARGET	= stage2
+
+TOOLCHAIN	= arm-elf-eabi-
+
+CC		= $(TOOLCHAIN)gcc
+CPP		= $(TOOLCHAIN)cpp
+LD		= $(TOOLCHAIN)gcc
+AS		= $(TOOLCHAIN)as
+OBJCOPY	= $(TOOLCHAIN)objcopy
+OBJDUMP	= $(TOOLCHAIN)objdump
+
+CFLAGS	= -Wundef -marm -march=armv5te -nostdlib -mfpu=fpa -O0 -c
+#ASFLAGS	= -mcpu=arm926ej-s
+
+OBJS	= crt0.o main.o irq.o
+LDSCRIPT= stage2.lds
+
+#LIBDIRS	= -L../arm/lib/gcc/arm-elf/4.1.0/ -L../lib
+#LIBS	= -lgcc
+LIBS    =
+LDFLAGS	= -Wundef -marm -march=armv5te -T$(LDSCRIPT) -nostartfiles  \
+	-mfpu=fpa -nostdlib -Xlinker -Map=$(TARGET).map
+
+all		: $(TARGET).bin
+		ls -ls $(TARGET).bin
+
+%.o		: %.c
+		$(CC) $(CPPFLAGS) $(CFLAGS) $(INCDIRS) $< -o $@
+
+%.o		: %.S
+		$(CC) $(CFLAGS) -c $< -o $@
+
+$(TARGET).elf : $(OBJS)
+		$(LD) $(LDFLAGS) $(OBJS) $(LIBDIRS) $(LIBS) -o $(TARGET).elf
+
+$(TARGET).bin : $(TARGET).elf
+		$(OBJCOPY) -O binary $(TARGET).elf $(TARGET).bin
+
+dasm	: $(TARGET).bin
+		$(OBJDUMP) -m arm -D $(TARGET).elf | cat > $(TARGET).asm
+
+clean	:
+		rm -f $(OBJS)
+		rm -f $(TARGET).elf
+		rm -f $(TARGET).bin
+		rm -f $(TARGET).asm
+		rm -f $(TARGET).map
diff --git a/utils/rk27utils/rk27load/stage2/crt0.S b/utils/rk27utils/rk27load/stage2/crt0.S
new file mode 100644
index 0000000..c854775
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2/crt0.S
@@ -0,0 +1,55 @@
+//
+// startup code
+//
+//
+
+#define PSR_MODE	0x0000001f
+#define PSR_USR_MODE	0x00000010
+#define PSR_IRQ_MODE	0x00000012
+#define PSR_SVC_MODE	0x00000013
+
+#define PSR_INT_MASK	0x000000c0
+#define PSR_FIQ_DIS	0x00000040
+#define PSR_IRQ_DIS	0x00000080
+
+.section .init.text,"ax",%progbits
+.global start
+.extern _interrupt_disable
+
+// -----------------------------------------------------
+//  startup code (setup stacks, branch to main)
+// -----------------------------------------------------
+start:
+	// setup IRQ stack
+	mov		r0, #(PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
+	msr		cpsr, r0
+	ldr		sp,=irqstackend
+
+	// setup SVC stack
+	mov		r0, #(PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
+	msr		cpsr, r0
+	ldr		sp,=stackend
+
+	// disbale interrupts
+        mrs             r0, cpsr
+        orr             r0, r0, #0xc0
+        msr             cpsr_c, r0
+
+        // remap
+        mov             r0, #0x18000000
+        add             r0, r0, #0x1C000
+        ldr             r1,=0xdeadbeef
+        str             r1, [r0, #4]
+
+        // relocate itself
+        ldr		r0,=_relocstart
+        ldr		r1,=_relocend
+	ldr		r2,=0x0
+1:
+	cmp		r1,r0
+        ldrhi		r3,[r0],#4
+	strhi		r3,[r2],#4
+	bhi		1b
+	
+	// continue running in SVC (supervisor mode)
+	ldr		pc,=0x0
diff --git a/utils/rk27utils/rk27load/stage2/irq.S b/utils/rk27utils/rk27load/stage2/irq.S
new file mode 100644
index 0000000..043bf18
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2/irq.S
@@ -0,0 +1,103 @@
+	.section .text
+	.align 4
+
+	.global irq_handler
+	#define         BUFF_ADDR       0x60800000
+
+irq_handler:
+        stmfd sp!, {r0-r7, ip, lr}
+
+        // get interrupt number
+        mov             r4, #0x18000000
+        add             r4, r4, #0x80000
+        ldr             r5, [r4, #0x104]
+        and             r5, r5, #0x1f
+        cmp             r5, #0x10 // UDC interrupt
+
+        bleq udc_irq
+
+        // clear pending interrupt
+        mov             r3, #1
+        mov             r2, r3, LSL r5
+        str             r2, [r4, #0x118]
+
+        ldmfd sp!, {r0-r7, ip, lr}
+        subs  pc, lr, #4
+
+udc_irq:
+        stmfd   sp!, {r4-r8, lr}
+
+        // handle usb interrupt
+        ldr             r4,=0x180A0000
+        ldr             r5, [r4, #0x18]         // UDC_INTFLAG
+
+        // ep0 in intr
+        tst             r5, #0x04
+        beq             bulk_recv_intr
+
+        // write_reg32(UDC_TX0STAT, read_reg32(UDC_TX0STAT) & ~0x7FF);
+        ldr             r5, [r4, #0x40]
+        mov             r5, r5, lsr #10
+        mov             r5, r5, lsl #10         // clear clower 10 bits
+        str             r5, [r4, #0x40]
+
+        // write_reg32(UDC_DMA0LM_OADDR, (uint32_t)(state.ctrlep_data));
+        mov             r5, #0x60000000
+        str             r5, [r4, #0x3c]
+
+        // write_reg32(UDC_DMA0CTLO, read_reg32(UDC_DMA0CTLO) | ENP_DMA_START);
+        mov             r5, #1
+        str             r5, [r4, #0x38]
+
+        ldmfd   sp!, {r4-r8, pc}
+
+// bulk out interrupt
+bulk_recv_intr:
+        tst             r5, #0x100
+        ldmeqfd sp!, {r4-r8, pc}
+
+        // read UDC_RX1STAT
+        ldr             r5, [r4, #0x54]
+        mov             r5, r5, lsl #21
+        mov             r5, r5, lsr #21         // r5 = length
+
+        ldr             r6,=usb_sz
+        ldr             r6, [r6]
+        ldr             r7, [r6]                        // r7 = total_code_length expected
+
+        subs            r7, r7, r5
+        bne             usb_bulk_out1_recv
+
+        // copy from buff to the begining of the ram
+        ldr             r0,=BUFF_ADDR
+        ldr		r1,[r0,#-4]     // size
+
+	ldr r1,=0x800000 // buffer size
+
+        add             r1,r1,r0        // end address
+        ldr             r2,=0x60000000  // destination
+1:
+        cmp             r1,r0
+        ldrhi           r3,[r0],#4
+        strhi           r3,[r2],#4
+        bhi             1b
+
+        // execute user code
+        ldr             r0,=0x60000000
+        bx              r0              // jump to 0x60000000
+
+usb_bulk_out1_recv:
+        str             r7, [r6]                        // size = size - received
+
+        ldr             r6,=usb_write_addr
+        ldr             r7, [r6]
+
+        add             r7, r7, r5
+        str             r7, [r6]                        // usb_write_addr += length
+
+        str             r7, [r4, #0x60]         // DMA1LM_OADDR = usb_write_addr
+
+        mov             r5, #1
+        str             r5, [r4, #0x5c]         // DMA1_CTL0 = ENP_DMA_START
+
+        ldmfd   sp!, {r4-r8, pc}
diff --git a/utils/rk27utils/rk27load/stage2/main.S b/utils/rk27utils/rk27load/stage2/main.S
new file mode 100644
index 0000000..c8474b0
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2/main.S
@@ -0,0 +1,89 @@
+
+	.section	.text
+	.align		4
+	
+	.arm
+	
+	.global		main
+        .global         _interrupt_disable
+	.global		_interrupt_enable
+
+        .global		usb_write_addr
+	.global		usb_sz
+
+	#define		BUFF_ADDR	0x60800000
+
+// -----------------------------------------------------
+//  vector table
+// -----------------------------------------------------
+    ldr pc, =main
+    ldr pc, =main
+    ldr pc, =main
+    ldr pc, =main
+    ldr pc, =main
+    ldr pc, =main
+    ldr pc, =irq_handler
+    ldr pc, =main
+
+// -----------------------------------------------------
+//  main
+// -----------------------------------------------------
+main:
+	// turn on usb interrupts
+	mov		r0, #0x18000000
+	add		r0, r0, #0x80000
+	ldr		r1, [r0, #0x10c]
+	orr		r1, r1, #0x10000
+	str		r1, [r0, #0x10c]
+
+	// enable usb-bulk
+	add		r0, r0, #0x20000	// R0 = 0x180A0000 (UDC_BASE)
+
+	// enable EP1, write_reg32(UDC_RX1CON, (0x1 << 8) | RxACKINTEN | RxEPEN);
+	mov		r1, #0x190	// bits 8,7,4 -> 0x190
+	str		r1, [r0, #0x58]
+	
+	// setup receive buffer (must be aligned on dword boundary)
+	ldr		r1,=usb_write_addr	// write_reg32(UDC_DMA1LM_OADDR, (uint32_t)rx_buff);
+	ldr		r1, [r1]
+	str		r1, [r0, #0x60]		// UDC_DMA1LM_OADDR = usb_write_addr
+	
+	// write_reg32(UDC_DMA1CTRLO, read_reg32(UDC_DMA1CTRLO) | ENP_DMA_START);
+	ldr		r1, [r0, #0x5c]
+	orr		r1, r1, #2
+	str		r1, [r0, #0x5c]
+
+	// enable bulk_out1 interrupt
+	ldr		r1, [r0, #0x14]		// UDC_ENINT
+	orr		r1, r1, #0x100		// EN_BOUT1_INTR
+	str		r1, [r0, #0x14]
+
+	bl		_interrupt_enable
+idle:
+	b		idle
+
+// -----------------------------------------------------
+// _interrupt_enable - enables interrupts
+// -----------------------------------------------------
+_interrupt_enable:
+        mrs             r0, cpsr
+        bic             r0, r0, #0x80
+        msr             cpsr_c, r0
+        mov             pc, lr
+
+// -----------------------------------------------------
+// _interrupt_disable - disables interrupts
+// -----------------------------------------------------
+_interrupt_disable:
+        mrs             r0, cpsr
+        orr             r0, r0, #0xc0
+        msr             cpsr_c, r0
+        mov             pc, lr
+
+
+	.section .data
+usb_write_addr:
+	.word	(BUFF_ADDR-4)
+
+usb_sz:
+	.word	(BUFF_ADDR-4)
diff --git a/utils/rk27utils/rk27load/stage2/stage2.lds b/utils/rk27utils/rk27load/stage2/stage2.lds
new file mode 100644
index 0000000..2c07b20
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2/stage2.lds
@@ -0,0 +1,40 @@
+ENTRY(start)
+OUTPUT_FORMAT(elf32-littlearm)
+OUTPUT_ARCH(arm)
+/* STARTUP(crt0.o) */
+
+MEMORY
+{
+    DRAM  : ORIGIN = 0x60000000, LENGTH = 0x01000000
+    IRAM  : ORIGIN = 0x00000000, LENGTH = 0x00002000
+}
+
+SECTIONS
+{
+    .init.text : {
+      *(.init.text)
+    } > DRAM
+
+    .text : {
+      *(.text*)
+      *(.glue_7*)
+    } > IRAM AT > DRAM
+
+    .data : {
+      *(.data*)
+    } > IRAM AT > DRAM
+
+    _relocstart = LOADADDR(.text);
+    _relocend = LOADADDR(.data) + SIZEOF(.data);
+
+    .stack (NOLOAD) : {
+      . = ALIGN(0x100);
+      *(.stack)
+      stackbegin = .;
+      . += 0x200;
+      stackend = .;
+      irqstackbegin = .;
+      . += 0x200;
+      irqstackend = .;
+    } > IRAM
+}
diff --git a/utils/rk27utils/rk27load/stage2_upload.c b/utils/rk27utils/rk27load/stage2_upload.c
new file mode 100644
index 0000000..820ad44
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2_upload.c
@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+
+#include "rk27load.h"
+#include "common.h"
+#include "scramble.h"
+#include "checksum.h"
+#include "stage2_upload.h"
+
+int upload_stage2_code(libusb_device_handle *hdev, char *fn_stage2,
+                       bool do_scramble)
+{
+    FILE *f;
+    uint32_t codesize;
+    uint8_t *code;
+    uint16_t cks;
+    int ret;
+
+    if ((f = fopen(fn_stage2, "rb")) == NULL)
+    {
+        fprintf(stderr, "[error]: Could not open file \"%s\"\n", fn_stage2);
+        return -21;
+    }
+
+    codesize = filesize(f);
+
+    fprintf(stderr, "[stage1]: Loading %d bytes (%s) of code... ", codesize, fn_stage2);
+
+    code = (uint8_t *) malloc(codesize + 0x400);
+    if (code == NULL)
+    {
+        fprintf(stderr, "\n[error]: Out of memory\n");
+        fclose(f);
+        return -22;
+
+    }
+
+    memset(code, 0, codesize + 0x400);
+
+    if (fread(code, 1, codesize, f) != codesize)
+    {
+        fprintf(stderr, "\n[error]: I/O error\n");
+        fclose(f);
+        free(code);
+        return -23;
+    }
+    fprintf(stderr, "done\n");
+    fclose(f);
+
+    codesize = ((codesize + 0x201) & 0xfffffe00) - 2;
+
+    if (do_scramble)
+    {
+        /* encode data if its user code */
+        fprintf(stderr, "[stage2]: Encoding %d bytes data... ", codesize);
+        scramble(code, code, codesize);
+        fprintf(stderr, "done\n");
+    }
+
+    fprintf(stderr, "[stage2]: Calculating checksum... ");
+    cks = checksum(code, codesize);
+    code[codesize + 0] = (cks >> 8) & 0xff;
+    code[codesize + 1] = cks & 0xff;
+    codesize += 2;
+    fprintf(stderr, "0x%04x\n", cks);
+
+    fprintf(stderr, "[stage2]: Uploading code (%d bytes)... ", codesize);
+
+    ret = libusb_control_transfer(hdev,              /* device handle */
+                                  USB_EP0,           /* bmRequestType */
+                                  VCMD_UPLOAD,       /* bRequest */
+                                  0,                 /* wValue */
+                                  VCMD_INDEX_STAGE2, /* wIndex */
+                                  code,              /* data */
+                                  codesize,          /* wLength */
+                                  USB_TIMEOUT        /* timeout */
+                                 );
+
+    if (ret < 0)
+    {
+        fprintf(stderr, "\n[error]: Code upload request failed (ret=%d)\n", ret);
+        free(code);
+        return -24;
+    }
+
+    if (ret != (int)codesize)
+    {
+        fprintf(stderr, "[error]: Sent %d of %d total\n", ret, codesize);
+        free(code);
+        return -25;
+    }
+
+    fprintf(stderr, "done\n");
+
+    free(code);
+    return 0;
+}
+
diff --git a/utils/rk27utils/rk27load/stage2_upload.h b/utils/rk27utils/rk27load/stage2_upload.h
new file mode 100644
index 0000000..852d17a
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage2_upload.h
@@ -0,0 +1,3 @@
+int upload_stage2_code(libusb_device_handle * hdev, char *fn_stage2,
+                       bool do_scramble);
+
diff --git a/utils/rk27utils/rk27load/stage3_upload.c b/utils/rk27utils/rk27load/stage3_upload.c
new file mode 100644
index 0000000..6f10a7c
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage3_upload.c
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libusb.h>
+
+#include "rk27load.h"
+#include "common.h"
+#include "scramble.h"
+#include "checksum.h"
+#include "stage3_upload.h"
+
+int upload_stage3_code(libusb_device_handle *hdev, char *fn_stage3)
+{
+    FILE *f;
+    uint32_t codesize;
+    uint32_t remain;
+    uint8_t *code;
+    uint16_t send_size = 0x200;
+    uint32_t i = 0;
+    int ret, transfered;
+
+    if ((f = fopen(fn_stage3, "rb")) == NULL)
+    {
+        fprintf(stderr, "[error]: Could not open file \"%s\"\n", fn_stage3);
+        return -31;
+    }
+
+    codesize = filesize(f);
+
+    fprintf(stderr, "[stage3]: Loading user code (%d bytes)... ", codesize);
+
+    /* allocate buffer */
+    code = (uint8_t *) malloc(codesize + 0x204);
+    if (code == NULL)
+    {
+        fprintf(stderr, "\n[error]: Out of memory\n");
+        fclose(f);
+        return -32;
+    }
+
+    memset(code, 0, codesize + 0x204);
+    /* read usercode into buffer */
+    if (fread(&code[4], 1, codesize, f) != codesize)
+    {
+        fprintf(stderr, "\n[error]: I/O error\n");
+        fclose(f);
+        free(f);
+        return -33;
+    }
+    fprintf(stderr, "done\n");
+
+    fclose(f);
+
+    /* put code size at the first 4 bytes */
+    codesize += 4;
+    code[0] = codesize & 0xff;
+    code[1] = (codesize >> 8) & 0xff;
+    code[2] = (codesize >> 16) & 0xff;
+    code[3] = (codesize >> 24) & 0xff;
+
+    fprintf(stderr, "[stage3]: Uploading user code (%d bytes)... ", codesize);
+
+    remain = codesize;
+
+    while (remain > 0)
+    {
+        if (remain < 0x200)
+            send_size = remain;
+
+        ret = libusb_bulk_transfer(hdev,                           /* handle */
+                                   1,                              /* EP */
+                                   &code[i * 0x200],               /* data */
+                                   send_size,                      /* length */
+                                   &transfered,                    /* xfered */
+                                   USB_TIMEOUT                     /* timeout */
+                                  );
+
+        if (ret != LIBUSB_SUCCESS)
+        {
+            fprintf(stderr, "\n[error]: Bulk transfer error (%d, %d)\n", ret, i);
+            free(code);
+            return -34;
+        }
+
+        remain -= send_size;
+        i++;
+    }
+
+    fprintf(stderr,"done (sent %d blocks)\n", i);
+    return 0;
+}
+
diff --git a/utils/rk27utils/rk27load/stage3_upload.h b/utils/rk27utils/rk27load/stage3_upload.h
new file mode 100644
index 0000000..03f9f0e
--- /dev/null
+++ b/utils/rk27utils/rk27load/stage3_upload.h
@@ -0,0 +1 @@
+int upload_stage3_code(libusb_device_handle *hdev, char *fn_stage3);
diff --git a/utils/rk27utils/rkboottool/Makefile b/utils/rk27utils/rkboottool/Makefile
new file mode 100644
index 0000000..895dfc8
--- /dev/null
+++ b/utils/rk27utils/rkboottool/Makefile
@@ -0,0 +1,7 @@
+all: rkboottool
+
+rkboottool: rkboottool.c
+	gcc -g -std=c99 -o $@ -W -Wall $^
+
+clean:
+	rm -fr rkboottool
diff --git a/utils/rk27utils/rkboottool/rkboottool.c b/utils/rk27utils/rkboottool/rkboottool.c
new file mode 100644
index 0000000..ad08b0b
--- /dev/null
+++ b/utils/rk27utils/rkboottool/rkboottool.c
@@ -0,0 +1,360 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define VERSION "v0.3"
+
+/* time field stucture */
+struct rktime_t
+{
+    uint16_t year;
+    uint16_t month;
+    uint16_t day;
+    uint16_t hour;
+    uint16_t minute;
+    uint16_t second;
+};
+
+/* Rock27Boot.bin header structure */
+struct rkboot_info_t
+{
+    char     sign[32];
+    uint8_t  check_values[16];
+    struct   rktime_t time;
+    uint32_t ui_master_version;
+    uint32_t ui_slave_version;
+    uint32_t s1_offset;
+    int32_t  s1_len;
+    uint32_t s2_offset;
+    int32_t  s2_len;
+    uint32_t s3_offset;
+    int32_t  s3_len;
+    uint32_t s4_offset;
+    int32_t  s4_len;
+    uint32_t version_flag;
+};
+
+/* actions */
+enum {
+      NONE = 0,
+      INFO = 1,
+      EXTRACT = 2,
+      SCRAMBLE = 4
+};
+
+/* scramble mode */
+enum {
+      CONTINOUS_ENC, /* scramble whole block at once */
+      PAGE_ENC       /* nand bootloader is scrambled in 0x200 chunks */
+};
+
+/* scrambling/descrambling reverse engineered by AleMaxx */
+static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size)
+{
+
+uint8_t key[] = {
+        0x7C, 0x4E, 0x03, 0x04,
+        0x55, 0x05, 0x09, 0x07,
+        0x2D, 0x2C, 0x7B, 0x38,
+        0x17, 0x0D, 0x17, 0x11
+};
+        int i, i3, x, val, idx;
+
+        uint8_t key1[0x100];
+        uint8_t key2[0x100];
+
+        for (i=0; i<0x100; i++) {
+                key1[i] = i;
+                key2[i] = key[i&0xf];
+        }
+
+        i3 = 0;
+        for (i=0; i<0x100; i++) {
+                x = key1[i];
+                i3 = key1[i] + i3;
+                i3 += key2[i];
+                i3 &= 0xff;
+                key1[i] = key1[i3];
+                key1[i3] = x;
+        }
+
+        idx = 0;
+        for (i=0; i<size; i++) {
+                x = key1[(i+1) & 0xff];
+                val = x;
+                idx = (x + idx) & 0xff;
+                key1[(i+1) & 0xff] = key1[idx];
+                key1[idx] = (x & 0xff);
+                val = (key1[(i+1)&0xff] + x) & 0xff;
+                val = key1[val];
+                outpg[i] = val ^ inpg[i];
+        }
+}
+
+static void *binary_extract(FILE *fp, uint32_t offset, uint32_t len, int descramble, int encode_mode)
+{
+    void *buff, *buff_ptr;
+    uint32_t ret;
+
+    if ((fp == NULL) || len == 0)
+        return NULL;
+
+        /* allocate buff */
+        if ((buff = malloc(len)) == NULL)
+            return NULL;
+
+        /* seek to the begining of the data */
+        fseek(fp, offset, SEEK_SET);
+
+        /* read into the buffer */
+        ret = fread(buff, 1, len, fp);
+
+        if (ret != len)
+        {
+            free(buff);
+            return NULL;
+        }
+
+        /* descramble */
+        if ( descramble )
+        {
+            buff_ptr = buff;
+            if (encode_mode == PAGE_ENC)
+            {
+                while (len >= 0x200)
+                {
+                    encode_page((uint8_t *)buff_ptr,
+                                (uint8_t *)buff_ptr,
+                                0x200);
+
+                    buff_ptr += 0x200;
+                    len -= 0x200;
+                }
+            }
+            encode_page((uint8_t *)buff_ptr, (uint8_t *)buff_ptr, len);
+        }
+
+    return buff;
+}
+
+static void usage(void)
+{
+    printf("Usage: rkboottool [options] Rock27Boot.bin\n");
+    printf("-h|--help         This help message\n");
+    printf("-e|--extract      Extract binary images from Rock27Boot.bin file\n");
+    printf("-d|--descramble   Descramble extracted binary images\n");
+    printf("-i|--info         Print info about Rock27Boot.bin file\n");
+    printf("\n");
+    printf("Usually you would like to use -d -e together to obtain raw binary\n");
+    printf("(out files rkboot_s1.bin, rkboot_s2.bin, rkboot_s3.bin, rkboot_s4.bin)\n");
+}
+
+int main (int argc, char **argv)
+{
+    struct rkboot_info_t rkboot_info;
+    FILE *fp_in, *fp_out;
+    int32_t i = 0, action = NONE;
+    int32_t ret;
+    void *buff;
+    char *in_filename = NULL;
+
+    if ( argc < 2 )
+    {
+        usage();
+        return -1;
+    }
+
+    /* print banner */
+    fprintf(stderr,"rkboottool " VERSION "\n");
+    fprintf(stderr,"(C) Marcin Bukat 2011\n");
+    fprintf(stderr,"This is free software; see the source for copying conditions.  There is NO\n");
+    fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+
+    /* arguments handling */
+    while (i < argc)
+    {
+        if ((strcmp(argv[i],"-i")==0) || (strcmp(argv[i],"--info")==0))
+        {
+            action |= INFO;
+        }
+        else if ((strcmp(argv[i],"-e")==0) || (strcmp(argv[i],"--extract")==0))
+        {
+            action |= EXTRACT;
+        }
+        else if ((strcmp(argv[i],"-d")==0) || (strcmp(argv[i],"--descramble")==0))
+        {
+            action |= SCRAMBLE;
+        }
+        else if ((strcmp(argv[i],"-h")==0) || (strcmp(argv[i],"--help")==0))
+        {
+            usage();
+            return 0;
+        }
+        else if ( argv[i][0] != '-' )
+        {
+            /* file argument */
+            in_filename = argv[i];
+        }
+        i++;
+    }
+
+    if ( (fp_in = fopen(in_filename, "rb")) == NULL )
+    {
+        fprintf(stderr, "error: can't open %s file for reading\n", in_filename);
+        return -1;
+    }
+
+    ret = fread(&rkboot_info, 1, sizeof(rkboot_info), fp_in);
+
+    if (ret != sizeof(rkboot_info))
+    {
+        fclose(fp_in);
+        fprintf(stderr, "error: can't read %s file header\n", in_filename);
+        fprintf(stderr, "read %d, expected %d\n", ret, sizeof(rkboot_info));
+        return -2;
+    }
+
+    if (action & INFO)
+    {
+        printf("file: %s\n", in_filename);
+        printf("signature: %s\n", rkboot_info.sign);
+        printf("check bytes: ");
+        for (i = 0; i < 16; i++)
+            printf("0x%0x ", rkboot_info.check_values[i]);
+
+        printf("\n");
+        printf("timestamp %d.%d.%d %d:%d:%d\n", rkboot_info.time.day,
+                                                rkboot_info.time.month,
+                                                rkboot_info.time.year,
+                                                rkboot_info.time.hour,
+                                                rkboot_info.time.minute,
+                                                rkboot_info.time.second);
+        printf("UI master version: 0x%0x\n", rkboot_info.ui_master_version);
+        printf("UI slave version: 0x%0x\n", rkboot_info.ui_slave_version);
+        printf("s1 data offset: 0x%0x\n", rkboot_info.s1_offset);
+        printf("s1 data len: 0x%0x\n", rkboot_info.s1_len);
+        printf("s2 offset: 0x%0x\n", rkboot_info.s2_offset);
+        printf("s2 len: 0x%0x\n", rkboot_info.s2_len);
+        printf("s3 offset: 0x%0x\n", rkboot_info.s3_offset);
+        printf("s3 len: 0x%0x\n", rkboot_info.s3_len);
+        printf("s4 offset: 0x%0x\n", rkboot_info.s4_offset);
+        printf("s4 len: 0x%0x\n", rkboot_info.s4_len);
+        printf("UI version flag: 0x%0x\n", rkboot_info.version_flag);
+    }
+
+    if (action & EXTRACT)
+    {
+        /* first stage */
+        buff = binary_extract(fp_in, rkboot_info.s1_offset,
+                                     rkboot_info.s1_len,
+                                     action & SCRAMBLE,
+                                     CONTINOUS_ENC);
+
+        if ( buff == NULL )
+        {
+            fclose(fp_in);
+            fprintf(stderr, "error: can't extract image\n");
+            return -2;
+        }
+
+        /* output */
+        if ((fp_out = fopen("rkboot_s1.bin", "wb")) == NULL)
+        {
+            free(buff);
+            fclose(fp_in);
+            fprintf(stderr, "[error]: can't open rkboot_s1.bin for writing\n");
+            return -3;
+        }
+
+        fwrite(buff, 1, rkboot_info.s1_len, fp_out);
+ 
+        fprintf(stderr, "[info]: extracted rkboot_s1.bin file\n");
+        free(buff);
+        fclose(fp_out);
+
+        /* second stage */
+        buff = binary_extract(fp_in, rkboot_info.s2_offset,
+                                     rkboot_info.s2_len,
+                                     action & SCRAMBLE,
+                                     CONTINOUS_ENC);
+
+        if ( buff == NULL )
+        {
+            fclose(fp_in);
+            fprintf(stderr, "error: can't extract image\n");
+            return -2;
+        }
+
+        if ((fp_out = fopen("rkboot_s2.bin", "wb")) == NULL)
+        {
+            free(buff);
+            fclose(fp_in);
+            fprintf(stderr, "[error]: can't open rkboot_s2.bin for writing\n");
+            return -4;
+        }
+
+        fwrite(buff, 1, rkboot_info.s2_len, fp_out);
+
+        fprintf(stderr, "[info]: extracted rkboot_s2.bin file\n");
+        free(buff);
+        fclose(fp_out);
+
+        /* third stage */
+        buff = binary_extract(fp_in, rkboot_info.s3_offset,
+                                     rkboot_info.s3_len,
+                                     action & SCRAMBLE,
+                                     PAGE_ENC);
+        if ( buff == NULL )
+        {
+            fclose(fp_in);
+            fprintf(stderr, "[error]: can't extract image.\n");
+            return -2;
+        }
+
+        if ((fp_out = fopen("rkboot_s3.bin", "wb")) == NULL)
+        {
+            free(buff);
+            fclose(fp_in);
+            fprintf(stderr, "[error]: can't open rkboot_s3.bin for writing\n");
+            return -4;
+        }
+
+        fwrite(buff, 1, rkboot_info.s3_len, fp_out);
+
+        fprintf(stderr, "[info]: extracted rkboot_s3.bin file\n");
+        free(buff);
+        fclose(fp_out);
+
+        /* forth stage */
+        buff = binary_extract(fp_in, rkboot_info.s4_offset,
+                                     rkboot_info.s4_len,
+                                     action & SCRAMBLE,
+                                     CONTINOUS_ENC);
+        if ( buff == NULL )
+        {
+            fclose(fp_in);
+            fprintf(stderr, "[error]: can't extract image\n");
+            return -2;
+        }
+
+        if ((fp_out = fopen("rkboot_s4.bin", "wb")) == NULL)
+        {
+            free(buff);
+            fclose(fp_in);
+            fprintf(stderr, "[error]: can't open rkboot_s4.bin for writing\n");
+            return -4;
+        }
+
+        fwrite(buff, 1, rkboot_info.s4_len, fp_out);
+
+        fprintf(stderr, "[info]: extracted rkboot_s4.bin file\n");
+        free(buff);
+        fclose(fp_out);
+    }
+
+    fclose(fp_in);
+    return 0;
+}
+
diff --git a/utils/rk27utils/rkusbtool/Makefile b/utils/rk27utils/rkusbtool/Makefile
new file mode 100644
index 0000000..785a09a
--- /dev/null
+++ b/utils/rk27utils/rkusbtool/Makefile
@@ -0,0 +1,7 @@
+all: rkusbtool
+
+rkusbtool: rkusbtool.c
+	gcc -g -std=c99 -o $@ -W -Wall -lusb-1.0 -I/usr/include/libusb-1.0/ $^
+
+clean:
+	rm -fr rkusbtool
diff --git a/utils/rk27utils/rkusbtool/rkusbtool.c b/utils/rk27utils/rkusbtool/rkusbtool.c
new file mode 100644
index 0000000..06fb7e8
--- /dev/null
+++ b/utils/rk27utils/rkusbtool/rkusbtool.c
@@ -0,0 +1,388 @@
+/* on ubuntu compile with gcc -W rkusbtool.c -o rkusbtool -lusb-1.0 -I/usr/include/libusb-1.0/ */
+#include <libusb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#define VERSION "v0.1"
+
+#define RETRY_MAX 5
+#define USB_TIMEOUT 512
+#define VENDORID  0x071b
+#define PRODUCTID 0x3203
+
+#define OUT_EP  0x01
+#define IN_EP   0x82
+
+#define CBW_SIGNATURE 0x43425355
+#define CSW_SIGNATURE 0x53425355
+#define SCSICMD_READ_12 0xa8
+
+/* rockchip specific commands */
+#define RK_CMD            0xe0
+#define RK_GET_VERSION    0xffffffff
+#define RK_SWITCH_ROCKUSB 0xfeffffff
+#define RK_CHECK_USB      0xfdffffff
+#define RK_OPEN_SYSDISK   0xfcffffff
+
+enum {
+      NONE = 0,
+      INFO = 1,
+      RKUSB = 2,
+      SYSDISK = 4,
+      CHECKUSB = 8
+};
+
+enum {
+      COMMAND_PASSED = 0,
+      COMMAND_FAILED = 1,
+      PHASE_ERROR    = 2
+};
+
+struct CBWCB_t
+{
+    uint8_t  cbCode;
+    uint8_t  cbLun;
+    uint32_t LBA;
+    uint32_t cbLen;
+    uint8_t  reseved;
+    uint8_t  control;
+} __attribute__((__packed__));
+
+struct CBW_t
+{
+    uint32_t dCBWSignature;
+    uint32_t dCBWTag;
+    uint32_t dCBWDataTransferLength;
+    uint8_t  bmCBWFlags;
+    uint8_t  bCBWLUN;
+    uint8_t  bCBWCBLength;
+    uint8_t  CBWCB[16];
+} __attribute__((__packed__));
+
+struct CSW_t
+{
+    uint32_t dCSWSignature;
+    uint32_t dCSWTag;
+    uint32_t dCSWDataResidue;
+    uint8_t  bCSWStatus;
+} __attribute__((__packed__));
+
+static int send_msc_cmd(libusb_device_handle *hdev, struct CBWCB_t *cbwcb, uint32_t data_len, uint32_t *reftag)
+{
+    struct CBW_t cbw;
+    int ret, repeat, transferred;
+    static uint32_t tag = 0xdaefbc01;
+
+    memset(&cbw, 0, sizeof(cbw));
+    cbw.dCBWSignature = CBW_SIGNATURE;
+    cbw.dCBWTag = tag++;
+    cbw.dCBWDataTransferLength = data_len;
+    cbw.bmCBWFlags = 0x80; /* device to host */
+    cbw.bCBWLUN = 0;
+    cbw.bCBWCBLength = sizeof(struct CBWCB_t);
+    memcpy(cbw.CBWCB, cbwcb, sizeof(struct CBWCB_t));
+
+    *reftag = cbw.dCBWTag;
+    do
+    {
+        /* transfer command to the device */
+        ret = libusb_bulk_transfer(hdev, OUT_EP, (unsigned char*)&cbw, 31, &transferred, USB_TIMEOUT);
+        if (ret == LIBUSB_ERROR_PIPE)
+        {
+            libusb_clear_halt(hdev, OUT_EP);
+        }
+        repeat++;
+    } while ((ret == LIBUSB_ERROR_PIPE) && (repeat < RETRY_MAX));
+
+    if (ret != LIBUSB_SUCCESS)
+    {
+        printf("error: command transfer error\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int get_msc_csw(libusb_device_handle *hdev, uint32_t reftag)
+{
+    struct CSW_t csw;
+    int ret, repeat, transferred;
+
+    /* get CSW response from device */
+    repeat = 0;
+    do
+    {
+        ret = libusb_bulk_transfer(hdev, IN_EP, (unsigned char *)&csw, 13, &transferred, USB_TIMEOUT);
+        if (ret == LIBUSB_ERROR_PIPE)
+        {
+            libusb_clear_halt(hdev, IN_EP);
+        }
+        repeat++;
+    } while ((ret == LIBUSB_ERROR_PIPE) && (repeat < RETRY_MAX));
+
+    if (ret != LIBUSB_SUCCESS)
+    {
+        printf("error reading CSW\n");
+        return -3;
+    }
+
+    if (transferred != 13)
+    {
+        printf("error wrong size of CSW packet\n");
+        return -4;
+    }
+
+    if (csw.dCSWSignature != CSW_SIGNATURE)
+    {
+        printf("error: wrong CSW signature.\n");
+        return -5;
+    }
+
+    if (csw.dCSWTag != reftag)
+    {
+        printf("error: CSW dCSWTag mismatch\n");
+        return -6;
+    }
+
+    if (csw.bCSWStatus)
+    {
+        /* In case of CSW indicating error dump the content of the packet */
+        printf ("dCSWSignature: 0x%0x\n", csw.dCSWSignature);
+        printf ("dCSWTag: 0x%0x\n", csw.dCSWTag);
+        printf ("dCSWDataResidue: 0x%0x\n", csw.dCSWDataResidue);
+        printf ("bCSWStatus: 0x%0x\n", csw.bCSWStatus);
+    }
+
+    return csw.bCSWStatus;
+}
+
+static int rk_cmd(libusb_device_handle *hdev, uint32_t command, uint8_t *buf, uint8_t len)
+{
+    struct CBWCB_t cbwcb;
+    int ret, transferred;
+    uint32_t reftag;
+
+    /* enter command */
+    memset(&cbwcb, 0, sizeof(cbwcb));
+    cbwcb.cbCode = SCSICMD_READ_12;
+    cbwcb.cbLun = RK_CMD;
+    cbwcb.LBA = command; /* RK_GET_VERSION, RK_OPEN_SYSDISK, RK_SWITCH_ROCKUSB */
+    cbwcb.cbLen = len;   /* size of transfer in response to this command */
+
+    ret = send_msc_cmd(hdev, &cbwcb, len, &reftag);
+
+    /* get the response */
+    if (len > 0)
+    {
+        ret = libusb_bulk_transfer(hdev, IN_EP, buf, len, &transferred, USB_TIMEOUT);
+        if (ret != LIBUSB_SUCCESS || transferred != len)
+        {
+            printf("error: reading response data failed\n");
+            return -2;
+        }
+    }
+
+    return get_msc_csw(hdev, reftag);
+}
+
+static int get_sense(libusb_device_handle *hdev)
+{
+    struct CBWCB_t cbwcb;
+    unsigned char sense[0x12];
+    int size, ret;
+    uint32_t reftag;
+
+    memset(&cbwcb, 0, sizeof(cbwcb));
+    cbwcb.cbCode = 0x03;
+    cbwcb.cbLun = 0;
+    cbwcb.LBA = 0;
+    cbwcb.cbLen = 0x12;
+
+    ret = send_msc_cmd(hdev, &cbwcb, 0x12, &reftag);
+    libusb_bulk_transfer(hdev, IN_EP, (unsigned char*)&sense, 0x12, &size, USB_TIMEOUT);
+
+    return get_msc_csw(hdev, reftag);
+}
+
+static void usage(void)
+{
+    printf("Usage: rkusbtool [options]\n");
+    printf("-h|--help         This help message\n");
+    printf("-i|--info         Get version string from the device\n");
+    printf("-d|--dfu          Put device into DFU mode\n");
+    printf("-s|--sysdisk      Open system disk\n");
+    printf("-c|--checkusb     Check if dev is in System or Loader USB mode\n");
+}
+
+int main (int argc, char **argv)
+{
+    libusb_device_handle *hdev;
+    int ret;
+    int i = 0, action = NONE;
+    uint32_t ver[3];
+
+    if (argc < 2)
+    {
+        usage();
+        return 1;
+    }
+    
+    /* print banner */
+    fprintf(stderr,"rkusbtool " VERSION "\n");
+    fprintf(stderr,"(C) Marcin Bukat 2011\n");
+    fprintf(stderr,"This is free software; see the source for copying conditions.  There is NO\n");
+    fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+
+    /* arguments handling */
+    while (i < argc)
+    {
+        if ((strcmp(argv[i],"-i")==0) || (strcmp(argv[i],"--info")==0))
+        {
+            action |= INFO;
+        }
+        else if ((strcmp(argv[i],"-d")==0) || (strcmp(argv[i],"--dfu")==0))
+        {
+            action |= RKUSB;
+        }
+        else if ((strcmp(argv[i],"-s")==0) || (strcmp(argv[i],"--sysdisk")==0))
+        {
+            action |= SYSDISK;
+        }
+        else if ((strcmp(argv[i],"-c")==0) || (strcmp(argv[i],"--checkusb")==0))
+        {
+            action |= CHECKUSB;
+        }
+        else if ((strcmp(argv[i],"-h")==0) || (strcmp(argv[i],"--help")==0))
+        {
+            usage();
+            return 0;
+        }
+        i++;
+    }
+
+    /* initialize libusb */
+    libusb_init(NULL);
+    /* usb_set_debug(2); */
+
+    hdev = libusb_open_device_with_vid_pid(NULL, VENDORID, PRODUCTID);
+    if (hdev == NULL)
+    {
+        printf("error: can't open device\n");
+        return -10;
+    }
+
+    ret = libusb_kernel_driver_active(hdev, 0);
+
+    if (ret < 0)
+    {
+        printf ("error checking kernel driver active\n");
+        libusb_close(hdev);
+        return -3;
+    }
+    else
+    {
+        if (ret)
+            libusb_detach_kernel_driver(hdev, 0);
+    }
+
+    ret = libusb_set_configuration(hdev, 1);
+    if (ret < 0)
+    {
+        printf("error: could not select configuration (1)\n");
+        libusb_close(hdev);
+        return -3;
+    }
+
+    ret = libusb_claim_interface(hdev, 0);
+    if (ret < 0)
+    {
+            printf("error: could not claim interface #0\n");
+            libusb_close(hdev);
+            return -11;
+    }
+
+    ret = libusb_set_interface_alt_setting(hdev, 0, 0);
+    if ( ret != LIBUSB_SUCCESS)
+    {
+        printf("error: could not set alt setting for interface #0\n");
+        libusb_close(hdev);
+        return -11;
+    }
+    
+    /* BulkOnly reset */
+    //ret = libusb_control_transfer(hdev, 0x21, 0xff, 0, 0, NULL, 0, USB_TIMEOUT);
+
+   /* BulkOnly get max lun */
+   //ret = libusb_control_transfer(hdev, 0xa1, 0xfe, 0, 0, &maxlun, 1, USB_TIMEOUT);
+
+    /* Devices that do not support multiple LUNs may STALL this command. */
+   //if (ret == 0)
+   //   maxlun = -1;
+ 
+    //printf("MAXLUN: %d\n", maxlun);
+
+    get_sense(hdev);
+
+    if (action & INFO)
+    {
+        ret = rk_cmd(hdev, RK_GET_VERSION, (uint8_t *)ver, 12);
+
+        if (ret)
+        {
+            printf("error sending RK_GET_VERSION command. Err 0x%0x\n", ret);
+            libusb_close(hdev);
+            return ret;
+        }
+
+        printf("Rockchip device info:\n");
+        printf("loader ver: %x.%x\n", (ver[0]>>16)&0xff, ver[0]&0xff);
+        printf("kernel ver: %x.%x\n", (ver[1]>>16)&0xff, ver[1]&0xff);
+        printf("sdk ver:    %x.%x\n", (ver[2]>>16)&0xff, ver[2]&0xff);
+    }
+
+    if (action & CHECKUSB)
+    {
+        printf("Checking USB mode...\n");
+        ret = rk_cmd(hdev, RK_CHECK_USB, (uint8_t *)ver, 1);
+
+        //if (ret)
+        //{
+        //    libusb_close(hdev);
+        //    return ret;
+        //}
+
+        if (*(char *)ver)
+            printf("The device is in Loader USB mode\n");
+        else
+            printf("The device is in System USB mode\n");
+    }
+
+    if (action & SYSDISK)
+    {
+        printf("Opening system disk...\n");
+        ret = rk_cmd(hdev, RK_OPEN_SYSDISK, NULL, 0);
+
+        if (ret)
+        {
+            libusb_close(hdev);
+            return ret;
+        }
+    }
+
+    if (action & RKUSB)
+    {
+        printf("Switching into rk DFU mode...\n");
+        ret = rk_cmd(hdev, RK_SWITCH_ROCKUSB, NULL, 0);
+
+        if (ret)
+        {
+            libusb_close(hdev);
+            return ret;
+        }
+    }
+
+    libusb_close(hdev);
+    libusb_exit(NULL);
+    return 0;
+}
