aboutsummaryrefslogtreecommitdiffstats
path: root/package/boot/uboot-xburst
diff options
context:
space:
mode:
Diffstat (limited to 'package/boot/uboot-xburst')
-rw-r--r--package/boot/uboot-xburst/Makefile88
-rw-r--r--package/boot/uboot-xburst/patches/0001-qi_lb60-add-nand-spl-support.patch894
-rw-r--r--package/boot/uboot-xburst/patches/0002-qi_lb60-add-software-usbboot-support.patch916
-rw-r--r--package/boot/uboot-xburst/patches/0003-add-mmc-support.patch1664
-rw-r--r--package/boot/uboot-xburst/patches/0004-add-more-boot-options-F1-F2-F3-F4-M-S.patch200
-rw-r--r--package/boot/uboot-xburst/patches/0005-add-nanonote-lcd-support.patch847
-rw-r--r--package/boot/uboot-xburst/patches/0006-enable-silent-console.patch60
7 files changed, 4669 insertions, 0 deletions
diff --git a/package/boot/uboot-xburst/Makefile b/package/boot/uboot-xburst/Makefile
new file mode 100644
index 0000000..4a701f8
--- /dev/null
+++ b/package/boot/uboot-xburst/Makefile
@@ -0,0 +1,88 @@
+#
+# Copyright (C) 2010 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=u-boot
+PKG_VERSION:=2012.10-rc2
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
+PKG_SOURCE_URL:= \
+ http://mirror2.openwrt.org/sources \
+ ftp://ftp.denx.de/pub/u-boot
+PKG_MD5SUM:=451c07271940016cec6f5ad8a155263b
+PKG_TARGETS:=bin
+
+PKG_LICENSE:=GPL-2.0 GPL-2.0+
+PKG_LICENSE_FILES:=Licenses/README
+
+include $(INCLUDE_DIR)/package.mk
+
+define uboot/Default
+ TITLE:=
+ CONFIG:=
+ IMAGE:=
+endef
+
+define uboot/qi_lb60
+ TITLE:=U-boot for the qi_lb60 board
+endef
+
+UBOOTS:=qi_lb60
+
+define Package/uboot/template
+define Package/uboot-xburst-$(1)
+ SECTION:=boot
+ CATEGORY:=Boot Loaders
+ DEPENDS:=@TARGET_xburst
+ TITLE:=$(2)
+ URL:=http://www.denx.de/wiki/UBoot/WebHome
+ VARIANT:=$(1)
+endef
+endef
+
+define BuildUbootPackage
+ $(eval $(uboot/Default))
+ $(eval $(uboot/$(1)))
+ $(call Package/uboot/template,$(1),$(TITLE))
+endef
+
+
+ifdef BUILD_VARIANT
+$(eval $(call uboot/$(BUILD_VARIANT)))
+UBOOT_CONFIG:=$(if $(CONFIG),$(CONFIG),$(BUILD_VARIANT))
+UBOOT_IMAGE:=$(if $(IMAGE),$(IMAGE),openwrt-$(BOARD)-$(BUILD_VARIANT)-u-boot.bin)
+endif
+
+define Build/Configure
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ $(UBOOT_CONFIG)_config
+endef
+
+define Build/Compile
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ CROSS_COMPILE=$(TARGET_CROSS)
+endef
+
+define Package/uboot/install/template
+define Package/uboot-xburst-$(1)/install
+ $(CP) $(PKG_BUILD_DIR)/u-boot-xburst.bin $(BIN_DIR)/$(2)
+ rmdir $$(1)
+endef
+endef
+
+$(foreach u,$(UBOOTS), \
+ $(eval $(call Package/uboot/install/template,$(u),openwrt-$(BOARD)-$(u)-u-boot.bin)) \
+)
+
+$(foreach u,$(UBOOTS), \
+ $(eval $(call BuildUbootPackage,$(u))) \
+ $(eval $(call BuildPackage,uboot-xburst-$(u))) \
+)
diff --git a/package/boot/uboot-xburst/patches/0001-qi_lb60-add-nand-spl-support.patch b/package/boot/uboot-xburst/patches/0001-qi_lb60-add-nand-spl-support.patch
new file mode 100644
index 0000000..e770243
--- /dev/null
+++ b/package/boot/uboot-xburst/patches/0001-qi_lb60-add-nand-spl-support.patch
@@ -0,0 +1,894 @@
+From 0329cf7965956a5a7044827e0ce88ae8d5150e54 Mon Sep 17 00:00:00 2001
+From: Xiangfu <xiangfu@openmobilefree.net>
+Date: Fri, 12 Oct 2012 09:46:58 +0800
+Subject: [PATCH 1/6] qi_lb60: add nand spl support
+
+ The JZ4740 CPU can load 8KB from two different addresses:
+ 1. the normal area up to 8KB starting from NAND flash address 0x00000000
+ 2. the backup area up to 8KB starting from NAND flash address 0x00002000
+
+Signed-off-by: Xiangfu <xiangfu@openmobilefree.net>
+---
+ Makefile | 12 +++
+ arch/mips/cpu/xburst/Makefile | 7 +-
+ arch/mips/cpu/xburst/cpu.c | 4 +
+ arch/mips/cpu/xburst/jz4740.c | 82 +++++++----------
+ arch/mips/cpu/xburst/spl/Makefile | 47 ++++++++++
+ arch/mips/cpu/xburst/spl/start.S | 63 +++++++++++++
+ board/qi/qi_lb60/Makefile | 4 +
+ board/qi/qi_lb60/qi_lb60-spl.c | 30 +++++++
+ board/qi/qi_lb60/qi_lb60.c | 8 +-
+ board/qi/qi_lb60/u-boot-spl.lds | 61 +++++++++++++
+ drivers/mtd/nand/jz4740_nand.c | 39 ++++++++-
+ include/configs/qi_lb60.h | 175 ++++++++++++++++++-------------------
+ 12 files changed, 386 insertions(+), 146 deletions(-)
+ create mode 100644 arch/mips/cpu/xburst/spl/Makefile
+ create mode 100644 arch/mips/cpu/xburst/spl/start.S
+ create mode 100644 board/qi/qi_lb60/qi_lb60-spl.c
+ create mode 100644 board/qi/qi_lb60/u-boot-spl.lds
+
+diff --git a/Makefile b/Makefile
+index 34d9075..a22778e 100644
+--- a/Makefile
++++ b/Makefile
+@@ -393,6 +393,10 @@ ALL-y += $(obj)u-boot-nodtb-tegra.bin
+ endif
+ endif
+
++ifeq ($(CPU),xburst)
++ALL-y += $(obj)u-boot-xburst.bin
++endif
++
+ all: $(ALL-y) $(SUBDIR_EXAMPLES)
+
+ $(obj)u-boot.dtb: $(obj)u-boot
+@@ -506,6 +510,14 @@ $(obj)u-boot-nodtb-tegra.bin: $(obj)spl/u-boot-spl.bin $(obj)u-boot.bin
+ endif
+ endif
+
++ifeq ($(CPU),xburst)
++$(obj)u-boot-xburst.bin: $(obj)spl/u-boot-spl.bin $(obj)u-boot.bin
++ dd if=$(obj)spl/u-boot-spl.bin of=$(obj)spl/u-boot-pad.bin conv=sync bs=8192 count=1
++ dd if=$(obj)spl/u-boot-spl.bin of=$(obj)spl/u-boot-pad.bin conv=sync,notrunc oflag=append bs=8192 count=1
++ tr '\0' '\377' < /dev/zero | dd of=$(obj)spl/u-boot-pad.bin conv=sync,notrunc oflag=append bs=16384 count=1
++ cat $(obj)spl/u-boot-pad.bin u-boot.bin > $@
++endif
++
+ ifeq ($(CONFIG_SANDBOX),y)
+ GEN_UBOOT = \
+ cd $(LNDIR) && $(CC) $(SYMS) -T $(obj)u-boot.lds \
+diff --git a/arch/mips/cpu/xburst/Makefile b/arch/mips/cpu/xburst/Makefile
+index b1f2ae4..ec35e55 100644
+--- a/arch/mips/cpu/xburst/Makefile
++++ b/arch/mips/cpu/xburst/Makefile
+@@ -24,9 +24,12 @@ include $(TOPDIR)/config.mk
+
+ LIB = $(obj)lib$(CPU).o
+
++COBJS-y = cpu.o jz_serial.o
++
++ifneq ($(CONFIG_SPL_BUILD),y)
+ START = start.o
+-SOBJS-y =
+-COBJS-y = cpu.o timer.o jz_serial.o
++COBJS-y += timer.o
++endif
+
+ COBJS-$(CONFIG_JZ4740) += jz4740.o
+
+diff --git a/arch/mips/cpu/xburst/cpu.c b/arch/mips/cpu/xburst/cpu.c
+index ddcbfaa..1432838 100644
+--- a/arch/mips/cpu/xburst/cpu.c
++++ b/arch/mips/cpu/xburst/cpu.c
+@@ -42,6 +42,8 @@
+ : \
+ : "i" (op), "R" (*(unsigned char *)(addr)))
+
++#ifndef CONFIG_SPL_BUILD
++
+ void __attribute__((weak)) _machine_restart(void)
+ {
+ struct jz4740_wdt *wdt = (struct jz4740_wdt *)JZ4740_WDT_BASE;
+@@ -109,6 +111,8 @@ void invalidate_dcache_range(ulong start_addr, ulong stop)
+ cache_op(Hit_Invalidate_D, addr);
+ }
+
++#endif
++
+ void flush_icache_all(void)
+ {
+ u32 addr, t = 0;
+diff --git a/arch/mips/cpu/xburst/jz4740.c b/arch/mips/cpu/xburst/jz4740.c
+index c0b9817..8816aa3 100644
+--- a/arch/mips/cpu/xburst/jz4740.c
++++ b/arch/mips/cpu/xburst/jz4740.c
+@@ -32,31 +32,19 @@ int disable_interrupts(void)
+ return 0;
+ }
+
+-/*
+- * PLL output clock = EXTAL * NF / (NR * NO)
+- * NF = FD + 2, NR = RD + 2
+- * NO = 1 (if OD = 0), NO = 2 (if OD = 1 or 2), NO = 4 (if OD = 3)
+- */
+ void pll_init(void)
+ {
+ struct jz4740_cpm *cpm = (struct jz4740_cpm *)JZ4740_CPM_BASE;
+
+- register unsigned int cfcr, plcr1;
+- int n2FR[33] = {
+- 0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0,
+- 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
+- 9
+- };
+- int div[5] = {1, 3, 3, 3, 3}; /* divisors of I:S:P:L:M */
+- int nf, pllout2;
++ register unsigned int cfcr, plcr;
++ unsigned int nf, pllout2;
+
+ cfcr = CPM_CPCCR_CLKOEN |
+- CPM_CPCCR_PCS |
+- (n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) |
+- (n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) |
+- (n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) |
+- (n2FR[div[3]] << CPM_CPCCR_MDIV_BIT) |
+- (n2FR[div[4]] << CPM_CPCCR_LDIV_BIT);
++ (0 << CPM_CPCCR_CDIV_BIT) |
++ (2 << CPM_CPCCR_HDIV_BIT) |
++ (2 << CPM_CPCCR_PDIV_BIT) |
++ (2 << CPM_CPCCR_MDIV_BIT) |
++ (2 << CPM_CPCCR_LDIV_BIT);
+
+ pllout2 = (cfcr & CPM_CPCCR_PCS) ?
+ CONFIG_SYS_CPU_SPEED : (CONFIG_SYS_CPU_SPEED / 2);
+@@ -65,15 +53,18 @@ void pll_init(void)
+ writel(pllout2 / 48000000 - 1, &cpm->uhccdr);
+
+ nf = CONFIG_SYS_CPU_SPEED * 2 / CONFIG_SYS_EXTAL;
+- plcr1 = ((nf - 2) << CPM_CPPCR_PLLM_BIT) | /* FD */
++ plcr = ((nf - 2) << CPM_CPPCR_PLLM_BIT) | /* FD */
+ (0 << CPM_CPPCR_PLLN_BIT) | /* RD=0, NR=2 */
+ (0 << CPM_CPPCR_PLLOD_BIT) | /* OD=0, NO=1 */
+- (0x20 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */
++ (0x32 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */
+ CPM_CPPCR_PLLEN; /* enable PLL */
+
+ /* init PLL */
+ writel(cfcr, &cpm->cpccr);
+- writel(plcr1, &cpm->cppcr);
++ writel(plcr, &cpm->cppcr);
++
++ while (!(readl(&cpm->cppcr) & CPM_CPPCR_PLLS))
++ ;
+ }
+
+ void sdram_init(void)
+@@ -92,26 +83,12 @@ void sdram_init(void)
+ 2 << EMC_DMCR_TCL_BIT /* CAS latency is 3 */
+ };
+
+- int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
+-
+ cpu_clk = CONFIG_SYS_CPU_SPEED;
+- mem_clk = cpu_clk * div[__cpm_get_cdiv()] / div[__cpm_get_mdiv()];
++ mem_clk = 84000000;
+
+ writel(0, &emc->bcr); /* Disable bus release */
+ writew(0, &emc->rtcsr); /* Disable clock for counting */
+
+- /* Fault DMCR value for mode register setting*/
+-#define SDRAM_ROW0 11
+-#define SDRAM_COL0 8
+-#define SDRAM_BANK40 0
+-
+- dmcr0 = ((SDRAM_ROW0 - 11) << EMC_DMCR_RA_BIT) |
+- ((SDRAM_COL0 - 8) << EMC_DMCR_CA_BIT) |
+- (SDRAM_BANK40 << EMC_DMCR_BA_BIT) |
+- (SDRAM_BW16 << EMC_DMCR_BW_BIT) |
+- EMC_DMCR_EPIN |
+- cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)];
+-
+ /* Basic DMCR value */
+ dmcr = ((SDRAM_ROW - 11) << EMC_DMCR_RA_BIT) |
+ ((SDRAM_COL - 8) << EMC_DMCR_CA_BIT) |
+@@ -128,31 +105,31 @@ void sdram_init(void)
+ if (tmp > 11)
+ tmp = 11;
+ dmcr |= (tmp - 4) << EMC_DMCR_TRAS_BIT;
+- tmp = SDRAM_RCD / ns;
+
++ tmp = SDRAM_RCD / ns;
+ if (tmp > 3)
+ tmp = 3;
+ dmcr |= tmp << EMC_DMCR_RCD_BIT;
+- tmp = SDRAM_TPC / ns;
+
++ tmp = SDRAM_TPC / ns;
+ if (tmp > 7)
+ tmp = 7;
+ dmcr |= tmp << EMC_DMCR_TPC_BIT;
+- tmp = SDRAM_TRWL / ns;
+
++ tmp = SDRAM_TRWL / ns;
+ if (tmp > 3)
+ tmp = 3;
+ dmcr |= tmp << EMC_DMCR_TRWL_BIT;
+- tmp = (SDRAM_TRAS + SDRAM_TPC) / ns;
+
++ tmp = (SDRAM_TRAS + SDRAM_TPC) / ns;
+ if (tmp > 14)
+ tmp = 14;
+ dmcr |= ((tmp + 1) >> 1) << EMC_DMCR_TRC_BIT;
+
+ /* SDRAM mode value */
+- sdmode = EMC_SDMR_BT_SEQ |
+- EMC_SDMR_OM_NORMAL |
+- EMC_SDMR_BL_4 |
++ sdmode = EMC_SDMR_BT_SEQ |
++ EMC_SDMR_OM_NORMAL |
++ EMC_SDMR_BL_4 |
+ cas_latency_sdmr[((SDRAM_CASL == 3) ? 1 : 0)];
+
+ /* Stage 1. Precharge all banks by writing SDMR with DMCR.MRSET=0 */
+@@ -172,8 +149,8 @@ void sdram_init(void)
+ if (tmp > 0xff)
+ tmp = 0xff;
+ writew(tmp, &emc->rtcor);
++
+ writew(0, &emc->rtcnt);
+- /* Divisor is 64, CKO/64 */
+ writew(EMC_RTCSR_CKS_64, &emc->rtcsr);
+
+ /* Wait for number of auto-refresh cycles */
+@@ -182,13 +159,17 @@ void sdram_init(void)
+ ;
+
+ /* Stage 3. Mode Register Set */
++ dmcr0 = (11 << EMC_DMCR_RA_BIT) |
++ (8 << EMC_DMCR_CA_BIT) |
++ (0 << EMC_DMCR_BA_BIT) |
++ EMC_DMCR_EPIN |
++ (SDRAM_BW16 << EMC_DMCR_BW_BIT) |
++ cas_latency_dmcr[((SDRAM_CASL == 3) ? 1 : 0)];
+ writel(dmcr0 | EMC_DMCR_RFSH | EMC_DMCR_MRSET, &emc->dmcr);
+ writeb(0, JZ4740_EMC_SDMR0 | sdmode);
+
+ /* Set back to basic DMCR value */
+ writel(dmcr | EMC_DMCR_RFSH | EMC_DMCR_MRSET, &emc->dmcr);
+-
+- /* everything is ok now */
+ }
+
+ DECLARE_GLOBAL_DATA_PTR;
+@@ -232,9 +213,10 @@ void rtc_init(void)
+ phys_size_t initdram(int board_type)
+ {
+ struct jz4740_emc *emc = (struct jz4740_emc *)JZ4740_EMC_BASE;
+- u32 dmcr;
+- u32 rows, cols, dw, banks;
+- ulong size;
++
++ unsigned int dmcr;
++ unsigned int rows, cols, dw, banks;
++ unsigned long size;
+
+ dmcr = readl(&emc->dmcr);
+ rows = 11 + ((dmcr & EMC_DMCR_RA_MASK) >> EMC_DMCR_RA_BIT);
+diff --git a/arch/mips/cpu/xburst/spl/Makefile b/arch/mips/cpu/xburst/spl/Makefile
+new file mode 100644
+index 0000000..f45e8c8
+--- /dev/null
++++ b/arch/mips/cpu/xburst/spl/Makefile
+@@ -0,0 +1,47 @@
++#
++# Copyright (C) 2011 Xiangfu Liu <xiangfu@openmobilefree.net>
++#
++# See file CREDITS for list of people who contributed to this
++# project.
++#
++# 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 program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++# MA 02111-1307 USA
++#
++
++include $(TOPDIR)/config.mk
++
++LIB = $(obj)lib$(CPU).o
++
++START = start.o
++SOBJS-y =
++COBJS-y =
++
++SRCS := $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
++OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
++START := $(addprefix $(obj),$(START))
++
++all: $(obj).depend $(START) $(LIB)
++
++$(LIB): $(OBJS)
++ $(call cmd_link_o_target, $(OBJS))
++
++#########################################################################
++
++# defines $(obj).depend target
++include $(SRCTREE)/rules.mk
++
++sinclude $(obj).depend
++
++#########################################################################
+diff --git a/arch/mips/cpu/xburst/spl/start.S b/arch/mips/cpu/xburst/spl/start.S
+new file mode 100644
+index 0000000..e31c4c8
+--- /dev/null
++++ b/arch/mips/cpu/xburst/spl/start.S
+@@ -0,0 +1,63 @@
++/*
++ * Copyright (c) 2010 Xiangfu Liu <xiangfu@openmobilefree.net>
++ *
++ * See file CREDITS for list of people who contributed to this
++ * project.
++ *
++ * 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 3 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include <config.h>
++#include <version.h>
++#include <asm/regdef.h>
++#include <asm/mipsregs.h>
++#include <asm/addrspace.h>
++#include <asm/cacheops.h>
++
++#include <asm/jz4740.h>
++
++ .set noreorder
++
++ .globl _start
++ .text
++_start:
++ .word JZ4740_NANDBOOT_CFG /* fetched during NAND Boot */
++reset:
++ /*
++ * STATUS register
++ * CU0=UM=EXL=IE=0, BEV=ERL=1, IP2~7=1
++ */
++ li t0, 0x0040FC04
++ mtc0 t0, CP0_STATUS
++ /*
++ * CAUSE register
++ * IV=1, use the specical interrupt vector (0x200)
++ */
++ li t1, 0x00800000
++ mtc0 t1, CP0_CAUSE
++
++ bal 1f
++ nop
++ .word _GLOBAL_OFFSET_TABLE_
++1:
++ move gp, ra
++ lw t1, 0(ra)
++ move gp, t1
++
++ la sp, 0x80004000
++ la t9, nand_spl_boot
++ j t9
++ nop
+diff --git a/board/qi/qi_lb60/Makefile b/board/qi/qi_lb60/Makefile
+index 5dae11b..e399246 100644
+--- a/board/qi/qi_lb60/Makefile
++++ b/board/qi/qi_lb60/Makefile
+@@ -22,7 +22,11 @@ include $(TOPDIR)/config.mk
+
+ LIB = $(obj)lib$(BOARD).o
+
++ifeq ($(CONFIG_SPL_BUILD),y)
++COBJS := $(BOARD)-spl.o
++else
+ COBJS := $(BOARD).o
++endif
+
+ SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
+ OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+diff --git a/board/qi/qi_lb60/qi_lb60-spl.c b/board/qi/qi_lb60/qi_lb60-spl.c
+new file mode 100644
+index 0000000..3fe3fa3
+--- /dev/null
++++ b/board/qi/qi_lb60/qi_lb60-spl.c
+@@ -0,0 +1,30 @@
++/*
++ * Authors: Xiangfu Liu <xiangfu@openmobilefree.cc>
++ *
++ * 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
++ * 3 of the License, or (at your option) any later version.
++ */
++
++#include <common.h>
++#include <nand.h>
++#include <asm/io.h>
++#include <asm/jz4740.h>
++
++void nand_spl_boot(void)
++{
++ __gpio_as_sdram_16bit_4720();
++ __gpio_as_uart0();
++ __gpio_jtag_to_uart0();
++
++ serial_init();
++
++ pll_init();
++ sdram_init();
++
++ nand_init();
++
++ puts("\nQi LB60 SPL: Starting U-Boot ...\n");
++ nand_boot();
++}
+diff --git a/board/qi/qi_lb60/qi_lb60.c b/board/qi/qi_lb60/qi_lb60.c
+index d975209..3bd4e2f 100644
+--- a/board/qi/qi_lb60/qi_lb60.c
++++ b/board/qi/qi_lb60/qi_lb60.c
+@@ -1,5 +1,5 @@
+ /*
+- * Authors: Xiangfu Liu <xiangfu@sharism.cc>
++ * Authors: Xiangfu Liu <xiangfu@openmobilefree.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -97,8 +97,10 @@ int board_early_init_f(void)
+ /* U-Boot common routines */
+ int checkboard(void)
+ {
+- printf("Board: Qi LB60 (Ingenic XBurst Jz4740 SoC, Speed %ld MHz)\n",
+- gd->cpu_clk / 1000000);
++ printf("Board: Qi LB60 (Ingenic XBurst Jz4740 SoC)\n");
++ printf(" CPU: %ld\n", gd->cpu_clk);
++ printf(" MEM: %ld\n", gd->mem_clk);
++ printf(" DEV: %ld\n", gd->dev_clk);
+
+ return 0;
+ }
+diff --git a/board/qi/qi_lb60/u-boot-spl.lds b/board/qi/qi_lb60/u-boot-spl.lds
+new file mode 100644
+index 0000000..930537f
+--- /dev/null
++++ b/board/qi/qi_lb60/u-boot-spl.lds
+@@ -0,0 +1,61 @@
++/*
++ * (C) Copyright 2012 Xiangfu Liu <xiangfu@openmobilefree.net>
++ *
++ * 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 program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++OUTPUT_FORMAT("elf32-tradlittlemips", "elf32-tradlittlemips", "elf32-tradlittlemips")
++
++OUTPUT_ARCH(mips)
++ENTRY(_start)
++SECTIONS
++{
++ . = 0x80000000;
++ . = ALIGN(4);
++ .text :
++ {
++ *(.text)
++ }
++
++ . = ALIGN(4);
++ .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
++
++ . = ALIGN(4);
++ .data : { *(.data) }
++
++ . = ALIGN(4);
++ .sdata : { *(.sdata) }
++
++ _gp = ALIGN(16);
++
++ __got_start = .;
++ .got : { *(.got) }
++ __got_end = .;
++
++ . = .;
++ __u_boot_cmd_start = .;
++ .u_boot_cmd : { *(.u_boot_cmd) }
++ __u_boot_cmd_end = .;
++
++ uboot_end_data = .;
++ num_got_entries = (__got_end - __got_start) >> 2;
++
++ . = ALIGN(4);
++ .sbss : { *(.sbss) }
++ .bss : { *(.bss) }
++ uboot_end = .;
++}
++ASSERT(uboot_end <= 0x80002000, "NAND bootstrap too big");
+diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
+index 3ec34f3..24a4921 100644
+--- a/drivers/mtd/nand/jz4740_nand.c
++++ b/drivers/mtd/nand/jz4740_nand.c
+@@ -15,6 +15,9 @@
+ #include <asm/io.h>
+ #include <asm/jz4740.h>
+
++#ifdef CONFIG_SPL_BUILD
++#define printf(s) puts(s)
++#endif
+ #define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000)
+ #define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000)
+ #define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000)
+@@ -176,7 +179,7 @@ static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat,
+ for (k = 0; k < 9; k++)
+ writeb(read_ecc[k], &emc->nfpar[k]);
+ }
+- /* Set PRDY */
++
+ writel(readl(&emc->nfecr) | EMC_NFECR_PRDY, &emc->nfecr);
+
+ /* Wait for completion */
+@@ -184,7 +187,7 @@ static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat,
+ status = readl(&emc->nfints);
+ } while (!(status & EMC_NFINTS_DECF));
+
+- /* disable ecc */
++ /* Disable ECC */
+ writel(readl(&emc->nfecr) & ~EMC_NFECR_ECCE, &emc->nfecr);
+
+ /* Check decoding */
+@@ -192,7 +195,7 @@ static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat,
+ return 0;
+
+ if (status & EMC_NFINTS_UNCOR) {
+- printf("uncorrectable ecc\n");
++ printf("JZ4740 uncorrectable ECC\n");
+ return -1;
+ }
+
+@@ -230,6 +233,32 @@ static int jz_nand_rs_correct_data(struct mtd_info *mtd, u_char *dat,
+ return errcnt;
+ }
+
++#ifdef CONFIG_SPL_BUILD
++static void jz_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++ int i;
++ struct nand_chip *this = mtd->priv;
++
++#if (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B16R3) || \
++ (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B16R2)
++ for (i = 0; i < len; i += 2)
++ buf[i] = readw(this->IO_ADDR_R);
++#elif (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B8R3) || \
++ (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B8R2)
++ for (i = 0; i < len; i++)
++ buf[i] = readb(this->IO_ADDR_R);
++#else
++ #error JZ4740_NANDBOOT_CFG not defined or wrong
++#endif
++}
++
++static uint8_t jz_nand_read_byte(struct mtd_info *mtd)
++{
++ struct nand_chip *this = mtd->priv;
++ return readb(this->IO_ADDR_R);
++}
++#endif
++
+ /*
+ * Main initialization routine
+ */
+@@ -254,6 +283,10 @@ int board_nand_init(struct nand_chip *nand)
+ nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
+ nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
+ nand->ecc.layout = &qi_lb60_ecclayout_2gb;
++#ifdef CONFIG_SPL_BUILD
++ nand->read_byte = jz_nand_read_byte;
++ nand->read_buf = jz_nand_read_buf;
++#endif
+ nand->chip_delay = 50;
+ nand->options = NAND_USE_FLASH_BBT;
+
+diff --git a/include/configs/qi_lb60.h b/include/configs/qi_lb60.h
+index 4bb5bbc..7bff444 100644
+--- a/include/configs/qi_lb60.h
++++ b/include/configs/qi_lb60.h
+@@ -1,5 +1,5 @@
+ /*
+- * Authors: Xiangfu Liu <xiangfu.z@gmail.com>
++ * Authors: Xiangfu Liu <xiangfu@openmobilefree.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -14,7 +14,6 @@
+ #define CONFIG_SYS_LITTLE_ENDIAN
+ #define CONFIG_JZSOC /* Jz SoC */
+ #define CONFIG_JZ4740 /* Jz4740 SoC */
+-#define CONFIG_NAND_JZ4740
+
+ #define CONFIG_SYS_CPU_SPEED 336000000 /* CPU clock: 336 MHz */
+ #define CONFIG_SYS_EXTAL 12000000 /* EXTAL freq: 12 MHz */
+@@ -24,24 +23,43 @@
+ #define CONFIG_SYS_UART_BASE JZ4740_UART0_BASE /* Base of the UART channel */
+ #define CONFIG_BAUDRATE 57600
+
++#define CONFIG_BOOTP_MASK (CONFIG_BOOTP_DEFAUL)
++#define CONFIG_BOOTDELAY 0
++#define CONFIG_BOOTARGS "mem=32M console=tty0 console=ttyS0,57600n8 ubi.mtd=2 rootfstype=ubifs root=ubi0:rootfs rw rootwait"
++#define CONFIG_BOOTCOMMAND "nand read 0x80600000 0x400000 0x280000;bootm"
++
++/*
++ * Miscellaneous configurable options
++ */
++#define CONFIG_SYS_SDRAM_BASE 0x80000000 /* Cached addr */
++#define CONFIG_SYS_INIT_SP_OFFSET 0x400000
++#define CONFIG_SYS_LOAD_ADDR 0x80600000
++#define CONFIG_SYS_MEMTEST_START 0x80100000
++#define CONFIG_SYS_MEMTEST_END 0x80A00000
++#define CONFIG_SYS_TEXT_BASE 0x80100000
++#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE
++
++#define CONFIG_SYS_MALLOC_LEN (4 * 1024 * 1024)
++#define CONFIG_SYS_BOOTPARAMS_LEN (128 * 1024)
++
++#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */
++#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16)
++
++#define CONFIG_SYS_LONGHELP
++#define CONFIG_SYS_MAXARGS 16
++#define CONFIG_SYS_PROMPT "NanoNote# "
++
+ #define CONFIG_SKIP_LOWLEVEL_INIT
+ #define CONFIG_BOARD_EARLY_INIT_F
+ #define CONFIG_SYS_NO_FLASH
+ #define CONFIG_SYS_FLASH_BASE 0 /* init flash_base as 0 */
+-#define CONFIG_ENV_OVERWRITE
+-
+-#define CONFIG_BOOTP_MASK (CONFIG_BOOTP_DEFAUL)
+-#define CONFIG_BOOTDELAY 0
+-#define CONFIG_BOOTARGS "mem=32M console=tty0 console=ttyS0,57600n8 ubi.mtd=2 rootfstype=ubifs root=ubi0:rootfs rw rootwait"
+-#define CONFIG_BOOTCOMMAND "nand read 0x80600000 0x400000 0x200000;bootm"
+
+ /*
+- * Command line configuration.
++ * Command line configuration
+ */
+ #define CONFIG_CMD_BOOTD /* bootd */
+ #define CONFIG_CMD_CONSOLE /* coninfo */
+ #define CONFIG_CMD_ECHO /* echo arguments */
+-
+ #define CONFIG_CMD_LOADB /* loadb */
+ #define CONFIG_CMD_LOADS /* loads */
+ #define CONFIG_CMD_MEMORY /* md mm nm mw cp cmp crc base loop mtest */
+@@ -58,45 +76,16 @@
+ #define CONFIG_LOADS_ECHO 1 /* echo on for serial download */
+
+ /*
+- * Miscellaneous configurable options
+- */
+-#define CONFIG_SYS_MAXARGS 16
+-#define CONFIG_SYS_LONGHELP
+-#define CONFIG_SYS_PROMPT "NanoNote# "
+-#define CONFIG_SYS_CBSIZE 256 /* Console I/O Buffer Size */
+-#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + sizeof(CONFIG_SYS_PROMPT) + 16)
+-
+-#define CONFIG_SYS_MALLOC_LEN (4 * 1024 * 1024)
+-#define CONFIG_SYS_BOOTPARAMS_LEN (128 * 1024)
+-
+-#define CONFIG_SYS_SDRAM_BASE 0x80000000 /* Cached addr */
+-#define CONFIG_SYS_INIT_SP_OFFSET 0x400000
+-#define CONFIG_SYS_LOAD_ADDR 0x80600000
+-#define CONFIG_SYS_MEMTEST_START 0x80100000
+-#define CONFIG_SYS_MEMTEST_END 0x80800000
+-
+-/*
+- * Environment
++ * NAND driver configuration
+ */
+-#define CONFIG_ENV_IS_IN_NAND /* use NAND for environment vars */
+-
+-#define CONFIG_SYS_NAND_5_ADDR_CYCLE
+-/*
+- * if board nand flash is 1GB, set to 1
+- * if board nand flash is 2GB, set to 2
+- * for change the PAGE_SIZE and BLOCK_SIZE
+- * will delete when there is no 1GB flash
+- */
+-#define NANONOTE_NAND_SIZE 2
+-
+-#define CONFIG_SYS_NAND_PAGE_SIZE (2048 * NANONOTE_NAND_SIZE)
+-#define CONFIG_SYS_NAND_BLOCK_SIZE (256 * NANONOTE_NAND_SIZE << 10)
+-/* nand bad block was marked at this page in a block, start from 0 */
++#define CONFIG_NAND_JZ4740
++#define CONFIG_SYS_NAND_PAGE_SIZE 4096
++#define CONFIG_SYS_NAND_BLOCK_SIZE (512 << 10)
++/* NAND bad block was marked at this page in a block, start from 0 */
+ #define CONFIG_SYS_NAND_BADBLOCK_PAGE 127
+ #define CONFIG_SYS_NAND_PAGE_COUNT 128
+ #define CONFIG_SYS_NAND_BAD_BLOCK_POS 0
+-/* ECC offset position in oob area, default value is 6 if it isn't defined */
+-#define CONFIG_SYS_NAND_ECC_POS (6 * NANONOTE_NAND_SIZE)
++#define CONFIG_SYS_NAND_ECC_POS 12
+ #define CONFIG_SYS_NAND_ECCSIZE 512
+ #define CONFIG_SYS_NAND_ECCBYTES 9
+ #define CONFIG_SYS_NAND_ECCPOS \
+@@ -115,10 +104,9 @@
+ #define CONFIG_SYS_ONENAND_BASE CONFIG_SYS_NAND_BASE
+ #define CONFIG_SYS_MAX_NAND_DEVICE 1
+ #define CONFIG_SYS_NAND_SELECT_DEVICE 1 /* nand driver supports mutipl.*/
+-#define CONFIG_NAND_SPL_TEXT_BASE 0x80000000
+
+ /*
+- * IPL (Initial Program Loader, integrated inside CPU)
++ * IPL (Initial Program Loader, integrated inside Ingenic Xburst JZ4740 CPU)
+ * Will load first 8k from NAND (SPL) into cache and execute it from there.
+ *
+ * SPL (Secondary Program Loader)
+@@ -130,77 +118,88 @@
+ * NUB (NAND U-Boot)
+ * This NAND U-Boot (NUB) is a special U-Boot version which can be started
+ * from RAM. Therefore it mustn't (re-)configure the SDRAM controller.
+- *
+ */
++
++/*
++ * NAND SPL configuration
++ */
++#define CONFIG_SPL
++#define CONFIG_SPL_LIBGENERIC_SUPPORT
++#define CONFIG_SPL_LIBCOMMON_SUPPORT
++#define CONFIG_SPL_NAND_LOAD
++#define CONFIG_SPL_NAND_SIMPLE
++#define CONFIG_SPL_NAND_SUPPORT
++#define CONFIG_SPL_TEXT_BASE 0x80000000
++#define CONFIG_SPL_START_S_PATH "arch/mips/cpu/xburst/spl"
++
++#define CONFIG_SYS_NAND_5_ADDR_CYCLE
++#define CONFIG_SYS_NAND_HW_ECC_OOBFIRST
++#define JZ4740_NANDBOOT_CFG JZ4740_NANDBOOT_B8R3
++
+ #define CONFIG_SYS_NAND_U_BOOT_DST 0x80100000 /* Load NUB to this addr */
+ #define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_NAND_U_BOOT_DST
+-/* Start NUB from this addr*/
++ /* Start NUB from this addr */
++#define CONFIG_SYS_NAND_U_BOOT_OFFS (32 << 10) /* Offset of NUB */
++#define CONFIG_SYS_NAND_U_BOOT_SIZE (256 << 10) /* Size of NUB */
+
+ /*
+- * Define the partitioning of the NAND chip (only RAM U-Boot is needed here)
++ * Environment configuration
+ */
+-#define CONFIG_SYS_NAND_U_BOOT_OFFS (256 << 10) /* Offset to RAM U-Boot image */
+-#define CONFIG_SYS_NAND_U_BOOT_SIZE (512 << 10) /* Size of RAM U-Boot image */
+-
++#define CONFIG_ENV_OVERWRITE
++#define CONFIG_ENV_IS_IN_NAND
+ #define CONFIG_ENV_SIZE (4 << 10)
+ #define CONFIG_ENV_OFFSET \
+ (CONFIG_SYS_NAND_BLOCK_SIZE + CONFIG_SYS_NAND_U_BOOT_SIZE)
+ #define CONFIG_ENV_OFFSET_REDUND \
+ (CONFIG_ENV_OFFSET + CONFIG_SYS_NAND_BLOCK_SIZE)
+
+-#define CONFIG_SYS_TEXT_BASE 0x80100000
+-#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE
+-
+ /*
+- * SDRAM Info.
++ * CPU cache configuration
+ */
+-#define CONFIG_NR_DRAM_BANKS 1
++#define CONFIG_SYS_DCACHE_SIZE 16384
++#define CONFIG_SYS_ICACHE_SIZE 16384
++#define CONFIG_SYS_CACHELINE_SIZE 32
+
+ /*
+- * Cache Configuration
++ * SDRAM configuration
+ */
+-#define CONFIG_SYS_DCACHE_SIZE 16384
+-#define CONFIG_SYS_ICACHE_SIZE 16384
+-#define CONFIG_SYS_CACHELINE_SIZE 32
++#define CONFIG_NR_DRAM_BANKS 1
++
++#define SDRAM_BW16 1 /* Data bus width: 0-32bit, 1-16bit */
++#define SDRAM_BANK4 1 /* Banks each chip: 0-2bank, 1-4bank */
++#define SDRAM_ROW 13 /* Row address: 11 to 13 */
++#define SDRAM_COL 9 /* Column address: 8 to 12 */
++#define SDRAM_CASL 2 /* CAS latency: 2 or 3 */
++#define SDRAM_TRAS 45 /* RAS# Active Time */
++#define SDRAM_RCD 20 /* RAS# to CAS# Delay */
++#define SDRAM_TPC 20 /* RAS# Precharge Time */
++#define SDRAM_TRWL 7 /* Write Latency Time */
++#define SDRAM_TREF 15625 /* Refresh period: 8192 cycles/64ms */
+
+ /*
+- * GPIO definition
++ * GPIO configuration
+ */
+-#define GPIO_LCD_CS (2 * 32 + 21)
+-#define GPIO_AMP_EN (3 * 32 + 4)
++#define GPIO_LCD_CS (2 * 32 + 21)
++#define GPIO_AMP_EN (3 * 32 + 4)
+
+-#define GPIO_SDPW_EN (3 * 32 + 2)
+-#define GPIO_SD_DETECT (3 * 32 + 0)
++#define GPIO_SDPW_EN (3 * 32 + 2)
++#define GPIO_SD_DETECT (3 * 32 + 0)
+
+-#define GPIO_BUZZ_PWM (3 * 32 + 27)
+-#define GPIO_USB_DETECT (3 * 32 + 28)
++#define GPIO_BUZZ_PWM (3 * 32 + 27)
++#define GPIO_USB_DETECT (3 * 32 + 28)
+
+-#define GPIO_AUDIO_POP (1 * 32 + 29)
+-#define GPIO_COB_TEST (1 * 32 + 30)
++#define GPIO_AUDIO_POP (1 * 32 + 29)
++#define GPIO_COB_TEST (1 * 32 + 30)
+
+ #define GPIO_KEYOUT_BASE (2 * 32 + 10)
+-#define GPIO_KEYIN_BASE (3 * 32 + 18)
+-#define GPIO_KEYIN_8 (3 * 32 + 26)
++#define GPIO_KEYIN_BASE (3 * 32 + 18)
++#define GPIO_KEYIN_8 (3 * 32 + 26)
+
+-#define GPIO_SD_CD_N GPIO_SD_DETECT /* SD Card insert detect */
++#define GPIO_SD_CD_N GPIO_SD_DETECT /* SD Card insert detect */
+ #define GPIO_SD_VCC_EN_N GPIO_SDPW_EN /* SD Card Power Enable */
+
+ #define SPEN GPIO_LCD_CS /* LCDCS :Serial command enable */
+ #define SPDA (2 * 32 + 22) /* LCDSCL:Serial command clock input */
+ #define SPCK (2 * 32 + 23) /* LCDSDA:Serial command data input */
+
+-/* SDRAM paramters */
+-#define SDRAM_BW16 1 /* Data bus width: 0-32bit, 1-16bit */
+-#define SDRAM_BANK4 1 /* Banks each chip: 0-2bank, 1-4bank */
+-#define SDRAM_ROW 13 /* Row address: 11 to 13 */
+-#define SDRAM_COL 9 /* Column address: 8 to 12 */
+-#define SDRAM_CASL 2 /* CAS latency: 2 or 3 */
+-
+-/* SDRAM Timings, unit: ns */
+-#define SDRAM_TRAS 45 /* RAS# Active Time */
+-#define SDRAM_RCD 20 /* RAS# to CAS# Delay */
+-#define SDRAM_TPC 20 /* RAS# Precharge Time */
+-#define SDRAM_TRWL 7 /* Write Latency Time */
+-#define SDRAM_TREF 15625 /* Refresh period: 8192 cycles/64ms */
+-
+ #endif
+--
+1.7.9.5
+
diff --git a/package/boot/uboot-xburst/patches/0002-qi_lb60-add-software-usbboot-support.patch b/package/boot/uboot-xburst/patches/0002-qi_lb60-add-software-usbboot-support.patch
new file mode 100644
index 0000000..feaf297
--- /dev/null
+++ b/package/boot/uboot-xburst/patches/0002-qi_lb60-add-software-usbboot-support.patch
@@ -0,0 +1,916 @@
+From fa51192b912d296b8eec10f7d44c6c17eb1dd368 Mon Sep 17 00:00:00 2001
+From: Xiangfu <xiangfu@openmobilefree.net>
+Date: Fri, 12 Oct 2012 09:47:39 +0800
+Subject: [PATCH 2/6] qi_lb60: add software usbboot support
+
+ JZ4740 CPU have a internal ROM have such kind of code, that make
+ JZ4740 can boot from USB
+
+ usbboot.S can downloads user program from the USB port to internal
+ SRAM and branches to the internal SRAM to execute the program
+
+Signed-off-by: Xiangfu <xiangfu@openmobilefree.net>
+---
+ board/qi/qi_lb60/Makefile | 1 +
+ board/qi/qi_lb60/qi_lb60-spl.c | 20 +
+ board/qi/qi_lb60/usbboot.S | 838 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 859 insertions(+)
+ create mode 100644 board/qi/qi_lb60/usbboot.S
+
+diff --git a/board/qi/qi_lb60/Makefile b/board/qi/qi_lb60/Makefile
+index e399246..6dd8c6f 100644
+--- a/board/qi/qi_lb60/Makefile
++++ b/board/qi/qi_lb60/Makefile
+@@ -23,6 +23,7 @@ include $(TOPDIR)/config.mk
+ LIB = $(obj)lib$(BOARD).o
+
+ ifeq ($(CONFIG_SPL_BUILD),y)
++SOBJS := usbboot.o
+ COBJS := $(BOARD)-spl.o
+ else
+ COBJS := $(BOARD).o
+diff --git a/board/qi/qi_lb60/qi_lb60-spl.c b/board/qi/qi_lb60/qi_lb60-spl.c
+index 3fe3fa3..aea459c 100644
+--- a/board/qi/qi_lb60/qi_lb60-spl.c
++++ b/board/qi/qi_lb60/qi_lb60-spl.c
+@@ -12,6 +12,24 @@
+ #include <asm/io.h>
+ #include <asm/jz4740.h>
+
++#define KEY_U_OUT (32 * 2 + 16)
++#define KEY_U_IN (32 * 3 + 19)
++
++extern void usb_boot(void);
++
++static void check_usb_boot(void)
++{
++ __gpio_as_input(KEY_U_IN);
++ __gpio_enable_pull(KEY_U_IN);
++ __gpio_as_output(KEY_U_OUT);
++ __gpio_clear_pin(KEY_U_OUT);
++
++ if (!__gpio_get_pin(KEY_U_IN)) {
++ puts("[U] pressed, goto USBBOOT mode\n");
++ usb_boot();
++ }
++}
++
+ void nand_spl_boot(void)
+ {
+ __gpio_as_sdram_16bit_4720();
+@@ -23,6 +41,8 @@ void nand_spl_boot(void)
+ pll_init();
+ sdram_init();
+
++ check_usb_boot();
++
+ nand_init();
+
+ puts("\nQi LB60 SPL: Starting U-Boot ...\n");
+diff --git a/board/qi/qi_lb60/usbboot.S b/board/qi/qi_lb60/usbboot.S
+new file mode 100644
+index 0000000..c872266
+--- /dev/null
++++ b/board/qi/qi_lb60/usbboot.S
+@@ -0,0 +1,838 @@
++/*
++ * for jz4740 usb boot
++ *
++ * Copyright (c) 2009 Author: <jlwei@ingenic.cn>
++ *
++ * See file CREDITS for list of people who contributed to this
++ * project.
++ *
++ * 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 program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++ .set noreorder
++ .globl usb_boot
++ .text
++
++/*
++ * Both NAND and USB boot load data to D-Cache first, then transfer
++ * data from D-Cache to I-Cache, and jump to execute the code in I-Cache.
++ * So init caches first and then dispatch to a proper boot routine.
++ */
++
++.macro load_addr reg addr
++ li \reg, 0x80000000
++ addiu \reg, \reg, \addr
++ la $2, usbboot_begin
++ subu \reg, \reg, $2
++.endm
++
++usb_boot:
++ /* Initialize PLL: set ICLK to 84MHz and HCLK to 42MHz. */
++ la $9, 0xB0000000 /* CPCCR: Clock Control Register */
++ la $8, 0x42041110 /* I:S:M:P=1:2:2:2 */
++ sw $8, 0($9)
++
++ la $9, 0xB0000010 /* CPPCR: PLL Control Register */
++ la $8, 0x06000120 /* M=12 N=0 D=0 CLK=12*(M+2)/(N+2) */
++ sw $8, 0($9)
++
++ mtc0 $0, $26 /* CP0_ERRCTL, restore WST reset state */
++ nop
++
++ mtc0 $0, $16 /* CP0_CONFIG */
++ nop
++
++ /* Relocate code to beginning of the ram */
++
++ la $2, usbboot_begin
++ la $3, usbboot_end
++ li $4, 0x80000000
++
++1:
++ lw $5, 0($2)
++ sw $5, 0($4)
++ addiu $2, $2, 4
++ bne $2, $3, 1b
++ addiu $4, $4, 4
++
++ li $2, 0x80000000
++ ori $3, $2, 0
++ addiu $3, $3, usbboot_end
++ la $4, usbboot_begin
++ subu $3, $3, $4
++
++
++2:
++ cache 0x0, 0($2) /* Index_Invalidate_I */
++ cache 0x1, 0($2) /* Index_Writeback_Inv_D */
++ addiu $2, $2, 32
++ subu $4, $3, $2
++ bgtz $4, 2b
++ nop
++
++ load_addr $3, usb_boot_return
++
++ jr $3
++
++usbboot_begin:
++
++init_caches:
++ li $2, 3 /* cacheable for kseg0 access */
++ mtc0 $2, $16 /* CP0_CONFIG */
++ nop
++
++ li $2, 0x20000000 /* enable idx-store-data cache insn */
++ mtc0 $2, $26 /* CP0_ERRCTL */
++
++ ori $2, $28, 0 /* start address */
++ ori $3, $2, 0x3fe0 /* end address, total 16KB */
++ mtc0 $0, $28, 0 /* CP0_TAGLO */
++ mtc0 $0, $28, 1 /* CP0_DATALO */
++cache_clear_a_line:
++ cache 0x8, 0($2) /* Index_Store_Tag_I */
++ cache 0x9, 0($2) /* Index_Store_Tag_D */
++ bne $2, $3, cache_clear_a_line
++ addiu $2, $2, 32 /* increment CACHE_LINE_SIZE */
++
++ ori $2, $28, 0 /* start address */
++ ori $3, $2, 0x3fe0 /* end address, total 16KB */
++ la $4, 0x1ffff000 /* physical address and 4KB page mask */
++cache_alloc_a_line:
++ and $5, $2, $4
++ ori $5, $5, 1 /* V bit of the physical tag */
++ mtc0 $5, $28, 0 /* CP0_TAGLO */
++ cache 0x8, 0($2) /* Index_Store_Tag_I */
++ cache 0x9, 0($2) /* Index_Store_Tag_D */
++ bne $2, $3, cache_alloc_a_line
++ addiu $2, $2, 32 /* increment CACHE_LINE_SIZE */
++
++ nop
++ nop
++ nop
++ /*
++ * Transfer data from dcache to icache, then jump to icache.
++ * Input parameters:
++ * $19: data length in bytes
++ * $20: jump target address
++ */
++xfer_d2i:
++
++ ori $8, $20, 0
++ addu $9, $8, $19 /* total 16KB */
++
++1:
++ cache 0x0, 0($8) /* Index_Invalidate_I */
++ cache 0x1, 0($8) /* Index_Writeback_Inv_D */
++ bne $8, $9, 1b
++ addiu $8, $8, 32
++
++ /* flush write-buffer */
++ sync
++
++ /* Invalidate BTB */
++ mfc0 $8, $16, 7 /* CP0_CONFIG */
++ nop
++ ori $8, 2
++ mtc0 $8, $16, 7
++ nop
++
++ /* Overwrite config to disable ram initalisation */
++ li $2, 0xff
++ sb $2, 20($20)
++
++ jalr $20
++ nop
++
++icache_return:
++ /* User code can return to here after executing itself in
++ icache, by jumping to $31. */
++ b usb_boot_return
++ nop
++
++
++usb_boot_return:
++ /* Enable the USB PHY */
++ la $9, 0xB0000024 /* CPM_SCR */
++ lw $8, 0($9)
++ ori $8, 0x40 /* USBPHY_ENABLE */
++ sw $8, 0($9)
++
++ /* Initialize USB registers */
++ la $27, 0xb3040000 /* USB registers base address */
++
++ sb $0, 0x0b($27) /* INTRUSBE: disable common USB interrupts */
++ sh $0, 0x06($27) /* INTRINE: disable EPIN interrutps */
++ sh $0, 0x08($27) /* INTROUTE: disable EPOUT interrutps */
++
++ li $9, 0x61
++ sb $9, 0x01($27) /* POWER: HSENAB | SUSPENDM | SOFTCONN */
++
++ /* Initialize USB states */
++ li $22, 0 /* set EP0 to IDLE state */
++ li $23, 1 /* no data stage */
++
++ /* Main loop of polling the usb commands */
++usb_command_loop:
++ lbu $9, 0x0a($27) /* read INTRUSB */
++ andi $9, 0x04 /* check USB_INTR_RESET */
++ beqz $9, check_intr_ep0in
++ nop
++
++ /* 1. Handle USB reset interrupt */
++handle_reset_intr:
++ lbu $9, 0x01($27) /* read POWER */
++ andi $9, 0x10 /* test HS_MODE */
++ bnez $9, _usb_set_maxpktsize
++ li $9, 512 /* max packet size of HS mode */
++ li $9, 64 /* max packet size of FS mode */
++
++_usb_set_maxpktsize:
++ li $8, 1
++ sb $8, 0x0e($27) /* set INDEX 1 */
++
++ sh $9, 0x10($27) /* INMAXP */
++ sb $0, 0x13($27) /* INCSRH */
++ sh $9, 0x14($27) /* OUTMAXP */
++ sb $0, 0x17($27) /* OUTCSRH */
++
++_usb_flush_fifo:
++ li $8, 0x48 /* INCSR_CDT && INCSR_FF */
++ sb $8, 0x12($27) /* INCSR */
++ li $8, 0x90 /* OUTCSR_CDT && OUTCSR_FF */
++ sb $8, 0x16($27) /* OUTCSR */
++
++ li $22, 0 /* set EP0 to IDLE state */
++ li $23, 1 /* no data stage */
++
++ /* 2. Check and handle EP0 interrupt */
++check_intr_ep0in:
++ lhu $10, 0x02($27) /* read INTRIN */
++ andi $9, $10, 0x1 /* check EP0 interrupt */
++ beqz $9, check_intr_ep1in
++ nop
++
++handle_ep0_intr:
++ sb $0, 0x0e($27) /* set INDEX 0 */
++ lbu $11, 0x12($27) /* read CSR0 */
++
++ andi $9, $11, 0x04 /* check SENTSTALL */
++ beqz $9, _ep0_setupend
++ nop
++
++_ep0_sentstall:
++ andi $9, $11, 0xdb
++ sb $9, 0x12($27) /* clear SENDSTALL and SENTSTALL */
++ li $22, 0 /* set EP0 to IDLE state */
++
++_ep0_setupend:
++ andi $9, $11, 0x10 /* check SETUPEND */
++ beqz $9, ep0_idle_state
++ nop
++
++ ori $9, $11, 0x80
++ sb $9, 0x12($27) /* set SVDSETUPEND */
++ li $22, 0 /* set EP0 to IDLE state */
++
++ep0_idle_state:
++ bnez $22, ep0_tx_state
++ nop
++
++ /* 2.1 Handle EP0 IDLE state interrupt */
++ andi $9, $11, 0x01 /* check OUTPKTRDY */
++ beqz $9, check_intr_ep1in
++ nop
++
++ /* Read 8-bytes setup packet from the FIFO */
++ lw $25, 0x20($27) /* first word of setup packet */
++ lw $26, 0x20($27) /* second word of setup packet */
++
++ andi $9, $25, 0x60 /* bRequestType & USB_TYPE_MASK */
++ beqz $9, _ep0_std_req
++ nop
++
++ /* 2.1.1 Vendor-specific setup request */
++_ep0_vend_req:
++ li $22, 0 /* set EP0 to IDLE state */
++ li $23, 1 /* NoData = 1 */
++
++ andi $9, $25, 0xff00 /* check bRequest */
++ srl $9, $9, 8
++ beqz $9, __ep0_get_cpu_info
++ sub $8, $9, 0x1
++ beqz $8, __ep0_set_data_address
++ sub $8, $9, 0x2
++ beqz $8, __ep0_set_data_length
++ sub $8, $9, 0x3
++ beqz $8, __ep0_flush_caches
++ sub $8, $9, 0x4
++ beqz $8, __ep0_prog_start1
++ sub $8, $9, 0x5
++ beqz $8, __ep0_prog_start2
++ nop
++ b _ep0_idle_state_fini /* invalid request */
++ nop
++
++__ep0_get_cpu_info:
++ load_addr $20, cpu_info_data /* data pointer to transfer */
++ li $21, 8 /* bytes left to transfer */
++ li $22, 1 /* set EP0 to TX state */
++ li $23, 0 /* NoData = 0 */
++
++ b _ep0_idle_state_fini
++ nop
++
++__ep0_set_data_address:
++ li $9, 0xffff0000
++ and $9, $25, $9
++ andi $8, $26, 0xffff
++ or $20, $9, $8 /* data address of next transfer */
++
++ b _ep0_idle_state_fini
++ nop
++
++__ep0_set_data_length:
++ li $9, 0xffff0000
++ and $9, $25, $9
++ andi $8, $26, 0xffff
++ or $21, $9, $8 /* data length of next transfer */
++
++ li $9, 0x48 /* SVDOUTPKTRDY and DATAEND */
++ sb $9, 0x12($27) /* CSR0 */
++
++ /* We must write packet to FIFO before EP1-IN interrupt here. */
++ b handle_epin1_intr
++ nop
++
++__ep0_flush_caches:
++ /* Flush dcache and invalidate icache. */
++ li $8, 0x80000000
++ addi $9, $8, 0x3fe0 /* total 16KB */
++
++1:
++ cache 0x0, 0($8) /* Index_Invalidate_I */
++ cache 0x1, 0($8) /* Index_Writeback_Inv_D */
++ bne $8, $9, 1b
++ addiu $8, $8, 32
++
++ /* flush write-buffer */
++ sync
++
++ /* Invalidate BTB */
++ mfc0 $8, $16, 7 /* CP0_CONFIG */
++ nop
++ ori $8, 2
++ mtc0 $8, $16, 7
++ nop
++
++ b _ep0_idle_state_fini
++ nop
++
++__ep0_prog_start1:
++ li $9, 0x48 /* SVDOUTPKTRDY and DATAEND */
++ sb $9, 0x12($27) /* CSR0 */
++
++ li $9, 0xffff0000
++ and $9, $25, $9
++ andi $8, $26, 0xffff
++ or $20, $9, $8 /* target address */
++
++ b xfer_d2i
++ li $19, 0x2000 /* 16KB data length */
++
++__ep0_prog_start2:
++ li $9, 0x48 /* SVDOUTPKTRDY and DATAEND */
++ sb $9, 0x12($27) /* CSR0 */
++
++ li $9, 0xffff0000
++ and $9, $25, $9
++ andi $8, $26, 0xffff
++ or $20, $9, $8 /* target address */
++
++ jalr $20 /* jump, and place the return address in $31 */
++ nop
++
++__ep0_prog_start2_return:
++/* User code can return to here after executing itself, by jumping to $31 */
++ b usb_boot_return
++ nop
++
++ /* 2.1.2 Standard setup request */
++_ep0_std_req:
++ andi $12, $25, 0xff00 /* check bRequest */
++ srl $12, $12, 8
++ sub $9, $12, 0x05 /* check USB_REQ_SET_ADDRESS */
++ bnez $9, __ep0_req_set_config
++ nop
++
++ /* Handle USB_REQ_SET_ADDRESS */
++__ep0_req_set_addr:
++ srl $9, $25, 16 /* get wValue */
++ sb $9, 0x0($27) /* set FADDR */
++ li $23, 1 /* NoData = 1 */
++ b _ep0_idle_state_fini
++ nop
++
++__ep0_req_set_config:
++ sub $9, $12, 0x09 /* check USB_REQ_SET_CONFIGURATION */
++ bnez $9, __ep0_req_get_desc
++ nop
++
++ /* Handle USB_REQ_SET_CONFIGURATION */
++ li $23, 1 /* NoData = 1 */
++ b _ep0_idle_state_fini
++ nop
++
++__ep0_req_get_desc:
++ sub $9, $12, 0x06 /* check USB_REQ_GET_DESCRIPTOR */
++ bnez $9, _ep0_idle_state_fini
++ li $23, 1 /* NoData = 1 */
++
++ /* Handle USB_REQ_GET_DESCRIPTOR */
++ li $23, 0 /* NoData = 0 */
++
++ srl $9, $25, 24 /* wValue >> 8 */
++ sub $8, $9, 0x01 /* check USB_DT_DEVICE */
++ beqz $8, ___ep0_get_dev_desc
++ srl $21, $26, 16 /* get wLength */
++ sub $8, $9, 0x02 /* check USB_DT_CONFIG */
++ beqz $8, ___ep0_get_conf_desc
++ sub $8, $9, 0x03 /* check USB_DT_STRING */
++ beqz $8, ___ep0_get_string_desc
++ sub $8, $9, 0x06 /* check USB_DT_DEVICE_QUALIFIER */
++ beqz $8, ___ep0_get_dev_qualifier
++ nop
++ b _ep0_idle_state_fini
++ nop
++
++___ep0_get_dev_desc:
++ load_addr $20, device_desc /* data pointer */
++ li $22, 1 /* set EP0 to TX state */
++ sub $8, $21, 18
++ blez $8, _ep0_idle_state_fini /* wLength <= 18 */
++ nop
++ li $21, 18 /* max length of device_desc */
++ b _ep0_idle_state_fini
++ nop
++
++___ep0_get_dev_qualifier:
++ load_addr $20, dev_qualifier /* data pointer */
++ li $22, 1 /* set EP0 to TX state */
++ sub $8, $21, 10
++ blez $8, _ep0_idle_state_fini /* wLength <= 10 */
++ nop
++ li $21, 10 /* max length of dev_qualifier */
++ b _ep0_idle_state_fini
++ nop
++
++___ep0_get_conf_desc:
++ load_addr $20, config_desc_fs /* data pointer of FS mode */
++ lbu $8, 0x01($27) /* read POWER */
++ andi $8, 0x10 /* test HS_MODE */
++ beqz $8, ___ep0_get_conf_desc2
++ nop
++ load_addr $20, config_desc_hs /* data pointer of HS mode */
++
++___ep0_get_conf_desc2:
++ li $22, 1 /* set EP0 to TX state */
++ sub $8, $21, 32
++ blez $8, _ep0_idle_state_fini /* wLength <= 32 */
++ nop
++ li $21, 32 /* max length of config_desc */
++ b _ep0_idle_state_fini
++ nop
++
++___ep0_get_string_desc:
++ li $22, 1 /* set EP0 to TX state */
++
++ srl $9, $25, 16 /* wValue & 0xff */
++ andi $9, 0xff
++
++ sub $8, $9, 1
++ beqz $8, ___ep0_get_string_manufacture
++ sub $8, $9, 2
++ beqz $8, ___ep0_get_string_product
++ nop
++
++___ep0_get_string_lang_ids:
++ load_addr $20, string_lang_ids /* data pointer */
++ b _ep0_idle_state_fini
++ li $21, 4 /* data length */
++
++___ep0_get_string_manufacture:
++ load_addr $20, string_manufacture /* data pointer */
++ b _ep0_idle_state_fini
++ li $21, 16 /* data length */
++
++___ep0_get_string_product:
++ load_addr $20, string_product /* data pointer */
++ b _ep0_idle_state_fini
++ li $21, 46 /* data length */
++
++_ep0_idle_state_fini:
++ li $9, 0x40 /* SVDOUTPKTRDY */
++ beqz $23, _ep0_idle_state_fini2
++ nop
++ ori $9, $9, 0x08 /* DATAEND */
++_ep0_idle_state_fini2:
++ sb $9, 0x12($27) /* CSR0 */
++ beqz $22, check_intr_ep1in
++ nop
++
++ /* 2.2 Handle EP0 TX state interrupt */
++ep0_tx_state:
++ sub $9, $22, 1
++ bnez $9, check_intr_ep1in
++ nop
++
++ sub $9, $21, 64 /* max packetsize */
++ blez $9, _ep0_tx_state2 /* data count <= 64 */
++ ori $19, $21, 0
++ li $19, 64
++
++_ep0_tx_state2:
++ beqz $19, _ep0_tx_state3 /* send ZLP */
++ ori $18, $19, 0 /* record bytes to be transferred */
++ sub $21, $21, $19 /* decrement data count */
++
++_ep0_fifo_write_loop:
++ lbu $9, 0($20) /* read data */
++ sb $9, 0x20($27) /* load FIFO */
++ sub $19, $19, 1 /* decrement counter */
++ bnez $19, _ep0_fifo_write_loop
++ addi $20, $20, 1 /* increment data pointer */
++
++ sub $9, $18, 64 /* max packetsize */
++ beqz $9, _ep0_tx_state4
++ nop
++
++_ep0_tx_state3:
++ /* transferred bytes < max packetsize */
++ li $9, 0x0a /* set INPKTRDY and DATAEND */
++ sb $9, 0x12($27) /* CSR0 */
++ li $22, 0 /* set EP0 to IDLE state */
++ b check_intr_ep1in
++ nop
++
++_ep0_tx_state4:
++ /* transferred bytes == max packetsize */
++ li $9, 0x02 /* set INPKTRDY */
++ sb $9, 0x12($27) /* CSR0 */
++ b check_intr_ep1in
++ nop
++
++ /* 3. Check and handle EP1 BULK-IN interrupt */
++check_intr_ep1in:
++ andi $9, $10, 0x2 /* check EP1 IN interrupt */
++ beqz $9, check_intr_ep1out
++ nop
++
++handle_epin1_intr:
++ li $9, 1
++ sb $9, 0x0e($27) /* set INDEX 1 */
++ lbu $9, 0x12($27) /* read INCSR */
++
++ andi $8, $9, 0x2 /* check INCSR_FFNOTEMPT */
++ bnez $8, _epin1_tx_state4
++ nop
++
++_epin1_write_fifo:
++ lhu $9, 0x10($27) /* get INMAXP */
++ sub $8, $21, $9
++ blez $8, _epin1_tx_state1 /* bytes left <= INMAXP */
++ ori $19, $21, 0
++ ori $19, $9, 0
++
++_epin1_tx_state1:
++ beqz $19, _epin1_tx_state4 /* No data */
++ nop
++
++ sub $21, $21, $19 /* decrement data count */
++
++ srl $5, $19, 2 /* # of word */
++ andi $6, $19, 0x3 /* # of byte */
++ beqz $5, _epin1_tx_state2
++ nop
++
++_epin1_fifo_write_word:
++ lw $9, 0($20) /* read data from source address */
++ sw $9, 0x24($27) /* write FIFO */
++ sub $5, $5, 1 /* decrement counter */
++ bnez $5, _epin1_fifo_write_word
++ addiu $20, $20, 4 /* increment dest address */
++
++_epin1_tx_state2:
++ beqz $6, _epin1_tx_state3
++ nop
++
++_epin1_fifo_write_byte:
++ lbu $9, 0($20) /* read data from source address */
++ sb $9, 0x24($27) /* write FIFO */
++ sub $6, $6, 1 /* decrement counter */
++ bnez $6, _epin1_fifo_write_byte
++ addiu $20, $20, 1 /* increment dest address */
++
++_epin1_tx_state3:
++ li $9, 0x1
++ sb $9, 0x12($27) /* INCSR, set INPKTRDY */
++
++_epin1_tx_state4:
++ /* 4. Check and handle EP1 BULK-OUT interrupt */
++check_intr_ep1out:
++ lhu $9, 0x04($27) /* read INTROUT */
++ andi $9, 0x2
++ beqz $9, check_status_next
++ nop
++
++handle_epout1_intr:
++ li $9, 1
++ sb $9, 0x0e($27) /* set INDEX 1 */
++
++ lbu $9, 0x16($27) /* read OUTCSR */
++ andi $9, 0x1 /* check OUTPKTRDY */
++ beqz $9, check_status_next
++ nop
++
++_epout1_read_fifo:
++ lhu $19, 0x18($27) /* read OUTCOUNT */
++ srl $5, $19, 2 /* # of word */
++ andi $6, $19, 0x3 /* # of byte */
++ beqz $5, _epout1_rx_state1
++ nop
++
++_epout1_fifo_read_word:
++ lw $9, 0x24($27) /* read FIFO */
++ sw $9, 0($20) /* store to dest address */
++ sub $5, $5, 1 /* decrement counter */
++ bnez $5, _epout1_fifo_read_word
++ addiu $20, $20, 4 /* increment dest address */
++
++_epout1_rx_state1:
++ beqz $6, _epout1_rx_state2
++ nop
++
++_epout1_fifo_read_byte:
++ lbu $9, 0x24($27) /* read FIFO */
++ sb $9, 0($20) /* store to dest address */
++ sub $6, $6, 1 /* decrement counter */
++ bnez $6, _epout1_fifo_read_byte
++ addiu $20, $20, 1 /* increment dest address */
++
++_epout1_rx_state2:
++ sb $0, 0x16($27) /* clear OUTPKTRDY */
++
++check_status_next:
++ b usb_command_loop
++ nop
++
++/* Device/Configuration/Interface/Endpoint/String Descriptors */
++
++ .align 2
++device_desc:
++ .byte 0x12 /* bLength */
++ .byte 0x01 /* bDescriptorType */
++ .byte 0x00 /* bcdUSB */
++ .byte 0x02 /* bcdUSB */
++ .byte 0x00 /* bDeviceClass */
++ .byte 0x00 /* bDeviceSubClass */
++ .byte 0x00 /* bDeviceProtocol */
++ .byte 0x40 /* bMaxPacketSize0 */
++ .byte 0x1a /* idVendor */
++ .byte 0x60 /* idVendor */
++ .byte 0x40 /* idProduct */
++ .byte 0x47 /* idProduct */
++ .byte 0x00 /* bcdDevice */
++ .byte 0x01 /* bcdDevice */
++ .byte 0x01 /* iManufacturer */
++ .byte 0x02 /* iProduct */
++ .byte 0x00 /* iSerialNumber */
++ .byte 0x01 /* bNumConfigurations */
++
++ .align 2
++dev_qualifier:
++ .byte 0x0a /* bLength */
++ .byte 0x06 /* bDescriptorType */
++ .byte 0x00 /* bcdUSB */
++ .byte 0x02 /* bcdUSB */
++ .byte 0x00 /* bDeviceClass */
++ .byte 0x00 /* bDeviceSubClass */
++ .byte 0x00 /* bDeviceProtocol */
++ .byte 0x40 /* bMaxPacketSize0 */
++ .byte 0x01 /* bNumConfigurations */
++ .byte 0x00 /* bRESERVED */
++
++ .align 2
++config_desc_hs:
++ .byte 0x09 /* bLength */
++ .byte 0x02 /* bDescriptorType */
++ .byte 0x20 /* wTotalLength */
++ .byte 0x00 /* wTotalLength */
++ .byte 0x01 /* bNumInterfaces */
++ .byte 0x01 /* bConfigurationValue */
++ .byte 0x00 /* iConfiguration */
++ .byte 0xc0 /* bmAttributes */
++ .byte 0x01 /* MaxPower */
++intf_desc_hs:
++ .byte 0x09 /* bLength */
++ .byte 0x04 /* bDescriptorType */
++ .byte 0x00 /* bInterfaceNumber */
++ .byte 0x00 /* bAlternateSetting */
++ .byte 0x02 /* bNumEndpoints */
++ .byte 0xff /* bInterfaceClass */
++ .byte 0x00 /* bInterfaceSubClass */
++ .byte 0x50 /* bInterfaceProtocol */
++ .byte 0x00 /* iInterface */
++ep1_desc_hs:
++ .byte 0x07 /* bLength */
++ .byte 0x05 /* bDescriptorType */
++ .byte 0x01 /* bEndpointAddress */
++ .byte 0x02 /* bmAttributes */
++ .byte 0x00 /* wMaxPacketSize */
++ .byte 0x02 /* wMaxPacketSize */
++ .byte 0x00 /* bInterval */
++ep2_desc_hs:
++ .byte 0x07 /* bLength */
++ .byte 0x05 /* bDescriptorType */
++ .byte 0x81 /* bEndpointAddress */
++ .byte 0x02 /* bmAttributes */
++ .byte 0x00 /* wMaxPacketSize */
++ .byte 0x02 /* wMaxPacketSize */
++ .byte 0x00 /* bInterval */
++
++ .align 2
++config_desc_fs:
++ .byte 0x09 /* bLength */
++ .byte 0x02 /* bDescriptorType */
++ .byte 0x20 /* wTotalLength */
++ .byte 0x00 /* wTotalLength */
++ .byte 0x01 /* bNumInterfaces */
++ .byte 0x01 /* bConfigurationValue */
++ .byte 0x00 /* iConfiguration */
++ .byte 0xc0 /* bmAttributes */
++ .byte 0x01 /* MaxPower */
++intf_desc_fs:
++ .byte 0x09 /* bLength */
++ .byte 0x04 /* bDescriptorType */
++ .byte 0x00 /* bInterfaceNumber */
++ .byte 0x00 /* bAlternateSetting */
++ .byte 0x02 /* bNumEndpoints */
++ .byte 0xff /* bInterfaceClass */
++ .byte 0x00 /* bInterfaceSubClass */
++ .byte 0x50 /* bInterfaceProtocol */
++ .byte 0x00 /* iInterface */
++ep1_desc_fs:
++ .byte 0x07 /* bLength */
++ .byte 0x05 /* bDescriptorType */
++ .byte 0x01 /* bEndpointAddress */
++ .byte 0x02 /* bmAttributes */
++ .byte 0x40 /* wMaxPacketSize */
++ .byte 0x00 /* wMaxPacketSize */
++ .byte 0x00 /* bInterval */
++ep2_desc_fs:
++ .byte 0x07 /* bLength */
++ .byte 0x05 /* bDescriptorType */
++ .byte 0x81 /* bEndpointAddress */
++ .byte 0x02 /* bmAttributes */
++ .byte 0x40 /* wMaxPacketSize */
++ .byte 0x00 /* wMaxPacketSize */
++ .byte 0x00 /* bInterval */
++
++ .align 2
++string_lang_ids:
++ .byte 0x04
++ .byte 0x03
++ .byte 0x09
++ .byte 0x04
++
++ .align 2
++string_manufacture:
++ .byte 0x10
++ .byte 0x03
++ .byte 0x49
++ .byte 0x00
++ .byte 0x6e
++ .byte 0x00
++ .byte 0x67
++ .byte 0x00
++ .byte 0x65
++ .byte 0x00
++ .byte 0x6e
++ .byte 0x00
++ .byte 0x69
++ .byte 0x00
++ .byte 0x63
++ .byte 0x00
++
++ .align 2
++string_product:
++ .byte 0x2e
++ .byte 0x03
++ .byte 0x4a
++ .byte 0x00
++ .byte 0x5a
++ .byte 0x00
++ .byte 0x34
++ .byte 0x00
++ .byte 0x37
++ .byte 0x00
++ .byte 0x34
++ .byte 0x00
++ .byte 0x30
++ .byte 0x00
++ .byte 0x20
++ .byte 0x00
++ .byte 0x55
++ .byte 0x00
++ .byte 0x53
++ .byte 0x00
++ .byte 0x42
++ .byte 0x00
++ .byte 0x20
++ .byte 0x00
++ .byte 0x42
++ .byte 0x00
++ .byte 0x6f
++ .byte 0x00
++ .byte 0x6f
++ .byte 0x00
++ .byte 0x74
++ .byte 0x00
++ .byte 0x20
++ .byte 0x00
++ .byte 0x44
++ .byte 0x00
++ .byte 0x65
++ .byte 0x00
++ .byte 0x76
++ .byte 0x00
++ .byte 0x69
++ .byte 0x00
++ .byte 0x63
++ .byte 0x00
++ .byte 0x65
++ .byte 0x00
++
++ .align 2
++cpu_info_data:
++ .byte 0x4a
++ .byte 0x5a
++ .byte 0x34
++ .byte 0x37
++ .byte 0x34
++ .byte 0x30
++ .byte 0x56
++ .byte 0x31
++usbboot_end:
++
++ .set reorder
+--
+1.7.9.5
+
diff --git a/package/boot/uboot-xburst/patches/0003-add-mmc-support.patch b/package/boot/uboot-xburst/patches/0003-add-mmc-support.patch
new file mode 100644
index 0000000..e9baa7c
--- /dev/null
+++ b/package/boot/uboot-xburst/patches/0003-add-mmc-support.patch
@@ -0,0 +1,1664 @@
+From bd36739e77669e8df45c38f6acfe2cea511534d9 Mon Sep 17 00:00:00 2001
+From: Xiangfu <xiangfu@openmobilefree.net>
+Date: Wed, 10 Oct 2012 18:19:41 +0800
+Subject: [PATCH 3/6] add mmc support
+
+---
+ arch/mips/include/asm/jz4740.h | 166 ++++++
+ board/qi/qi_lb60/qi_lb60.c | 9 +-
+ drivers/mmc/Makefile | 1 +
+ drivers/mmc/jz_mmc.c | 1179 ++++++++++++++++++++++++++++++++++++++++
+ drivers/mmc/jz_mmc.h | 176 ++++++
+ include/configs/qi_lb60.h | 9 +
+ include/mmc.h | 40 ++
+ 7 files changed, 1578 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/mmc/jz_mmc.c
+ create mode 100644 drivers/mmc/jz_mmc.h
+
+diff --git a/arch/mips/include/asm/jz4740.h b/arch/mips/include/asm/jz4740.h
+index 7a7cfff..68287fb 100644
+--- a/arch/mips/include/asm/jz4740.h
++++ b/arch/mips/include/asm/jz4740.h
+@@ -1146,5 +1146,171 @@ extern void sdram_init(void);
+ extern void calc_clocks(void);
+ extern void rtc_init(void);
+
++/*************************************************************************
++ * MSC
++ *************************************************************************/
++#define REG8(addr) *((volatile u8 *)(addr))
++#define REG16(addr) *((volatile u16 *)(addr))
++#define REG32(addr) *((volatile u32 *)(addr))
++
++#define CPM_BASE 0xB0000000
++#define CPM_CPCCR (CPM_BASE+0x00)
++#define CPM_MSCCDR (CPM_BASE+0x68)
++#define REG_CPM_MSCCDR REG32(CPM_MSCCDR)
++#define REG_CPM_CPCCR REG32(CPM_CPCCR)
++
++#define MSC_BASE 0xB0021000
++
++#define MSC_STRPCL (MSC_BASE + 0x000)
++#define MSC_STAT (MSC_BASE + 0x004)
++#define MSC_CLKRT (MSC_BASE + 0x008)
++#define MSC_CMDAT (MSC_BASE + 0x00C)
++#define MSC_RESTO (MSC_BASE + 0x010)
++#define MSC_RDTO (MSC_BASE + 0x014)
++#define MSC_BLKLEN (MSC_BASE + 0x018)
++#define MSC_NOB (MSC_BASE + 0x01C)
++#define MSC_SNOB (MSC_BASE + 0x020)
++#define MSC_IMASK (MSC_BASE + 0x024)
++#define MSC_IREG (MSC_BASE + 0x028)
++#define MSC_CMD (MSC_BASE + 0x02C)
++#define MSC_ARG (MSC_BASE + 0x030)
++#define MSC_RES (MSC_BASE + 0x034)
++#define MSC_RXFIFO (MSC_BASE + 0x038)
++#define MSC_TXFIFO (MSC_BASE + 0x03C)
++
++#define REG_MSC_STRPCL REG16(MSC_STRPCL)
++#define REG_MSC_STAT REG32(MSC_STAT)
++#define REG_MSC_CLKRT REG16(MSC_CLKRT)
++#define REG_MSC_CMDAT REG32(MSC_CMDAT)
++#define REG_MSC_RESTO REG16(MSC_RESTO)
++#define REG_MSC_RDTO REG16(MSC_RDTO)
++#define REG_MSC_BLKLEN REG16(MSC_BLKLEN)
++#define REG_MSC_NOB REG16(MSC_NOB)
++#define REG_MSC_SNOB REG16(MSC_SNOB)
++#define REG_MSC_IMASK REG16(MSC_IMASK)
++#define REG_MSC_IREG REG16(MSC_IREG)
++#define REG_MSC_CMD REG8(MSC_CMD)
++#define REG_MSC_ARG REG32(MSC_ARG)
++#define REG_MSC_RES REG16(MSC_RES)
++#define REG_MSC_RXFIFO REG32(MSC_RXFIFO)
++#define REG_MSC_TXFIFO REG32(MSC_TXFIFO)
++
++/* MSC Clock and Control Register (MSC_STRPCL) */
++
++#define MSC_STRPCL_EXIT_MULTIPLE (1 << 7)
++#define MSC_STRPCL_EXIT_TRANSFER (1 << 6)
++#define MSC_STRPCL_START_READWAIT (1 << 5)
++#define MSC_STRPCL_STOP_READWAIT (1 << 4)
++#define MSC_STRPCL_RESET (1 << 3)
++#define MSC_STRPCL_START_OP (1 << 2)
++#define MSC_STRPCL_CLOCK_CONTROL_BIT 0
++#define MSC_STRPCL_CLOCK_CONTROL_MASK (0x3 << MSC_STRPCL_CLOCK_CONTROL_BIT)
++ #define MSC_STRPCL_CLOCK_CONTROL_STOP (0x1 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Stop MMC/SD clock */
++ #define MSC_STRPCL_CLOCK_CONTROL_START (0x2 << MSC_STRPCL_CLOCK_CONTROL_BIT) /* Start MMC/SD clock */
++
++/* MSC Status Register (MSC_STAT) */
++
++#define MSC_STAT_IS_RESETTING (1 << 15)
++#define MSC_STAT_SDIO_INT_ACTIVE (1 << 14)
++#define MSC_STAT_PRG_DONE (1 << 13)
++#define MSC_STAT_DATA_TRAN_DONE (1 << 12)
++#define MSC_STAT_END_CMD_RES (1 << 11)
++#define MSC_STAT_DATA_FIFO_AFULL (1 << 10)
++#define MSC_STAT_IS_READWAIT (1 << 9)
++#define MSC_STAT_CLK_EN (1 << 8)
++#define MSC_STAT_DATA_FIFO_FULL (1 << 7)
++#define MSC_STAT_DATA_FIFO_EMPTY (1 << 6)
++#define MSC_STAT_CRC_RES_ERR (1 << 5)
++#define MSC_STAT_CRC_READ_ERROR (1 << 4)
++#define MSC_STAT_CRC_WRITE_ERROR_BIT 2
++#define MSC_STAT_CRC_WRITE_ERROR_MASK (0x3 << MSC_STAT_CRC_WRITE_ERROR_BIT)
++ #define MSC_STAT_CRC_WRITE_ERROR_NO (0 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No error on transmission of data */
++ #define MSC_STAT_CRC_WRITE_ERROR (1 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* Card observed erroneous transmission of data */
++ #define MSC_STAT_CRC_WRITE_ERROR_NOSTS (2 << MSC_STAT_CRC_WRITE_ERROR_BIT) /* No CRC status is sent back */
++#define MSC_STAT_TIME_OUT_RES (1 << 1)
++#define MSC_STAT_TIME_OUT_READ (1 << 0)
++
++/* MSC Bus Clock Control Register (MSC_CLKRT) */
++
++#define MSC_CLKRT_CLK_RATE_BIT 0
++#define MSC_CLKRT_CLK_RATE_MASK (0x7 << MSC_CLKRT_CLK_RATE_BIT)
++ #define MSC_CLKRT_CLK_RATE_DIV_1 (0x0 << MSC_CLKRT_CLK_RATE_BIT) /* CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_2 (0x1 << MSC_CLKRT_CLK_RATE_BIT) /* 1/2 of CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_4 (0x2 << MSC_CLKRT_CLK_RATE_BIT) /* 1/4 of CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_8 (0x3 << MSC_CLKRT_CLK_RATE_BIT) /* 1/8 of CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_16 (0x4 << MSC_CLKRT_CLK_RATE_BIT) /* 1/16 of CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_32 (0x5 << MSC_CLKRT_CLK_RATE_BIT) /* 1/32 of CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_64 (0x6 << MSC_CLKRT_CLK_RATE_BIT) /* 1/64 of CLK_SRC */
++ #define MSC_CLKRT_CLK_RATE_DIV_128 (0x7 << MSC_CLKRT_CLK_RATE_BIT) /* 1/128 of CLK_SRC */
++
++/* MSC Command Sequence Control Register (MSC_CMDAT) */
++
++#define MSC_CMDAT_IO_ABORT (1 << 11)
++#define MSC_CMDAT_BUS_WIDTH_BIT 9
++#define MSC_CMDAT_BUS_WIDTH_MASK (0x3 << MSC_CMDAT_BUS_WIDTH_BIT)
++#define MSC_CMDAT_BUS_WIDTH_1BIT (0x0 << MSC_CMDAT_BUS_WIDTH_BIT)
++#define MSC_CMDAT_BUS_WIDTH_4BIT (0x2 << MSC_CMDAT_BUS_WIDTH_BIT)
++#define MSC_CMDAT_DMA_EN (1 << 8)
++#define MSC_CMDAT_INIT (1 << 7)
++#define MSC_CMDAT_BUSY (1 << 6)
++#define MSC_CMDAT_STREAM_BLOCK (1 << 5)
++#define MSC_CMDAT_WRITE (1 << 4)
++#define MSC_CMDAT_READ (0 << 4)
++#define MSC_CMDAT_DATA_EN (1 << 3)
++#define MSC_CMDAT_RESPONSE_BIT 0
++#define MSC_CMDAT_RESPONSE_MASK (0x7 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_NONE (0x0 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_R1 (0x1 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_R2 (0x2 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_R3 (0x3 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_R4 (0x4 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_R5 (0x5 << MSC_CMDAT_RESPONSE_BIT)
++#define MSC_CMDAT_RESPONSE_R6 (0x6 << MSC_CMDAT_RESPONSE_BIT)
++
++/* MSC Interrupts Mask Register (MSC_IMASK) */
++#define MSC_IMASK_SDIO (1 << 7)
++#define MSC_IMASK_TXFIFO_WR_REQ (1 << 6)
++#define MSC_IMASK_RXFIFO_RD_REQ (1 << 5)
++#define MSC_IMASK_END_CMD_RES (1 << 2)
++#define MSC_IMASK_PRG_DONE (1 << 1)
++#define MSC_IMASK_DATA_TRAN_DONE (1 << 0)
++
++
++/* MSC Interrupts Status Register (MSC_IREG) */
++#define MSC_IREG_SDIO (1 << 7)
++#define MSC_IREG_TXFIFO_WR_REQ (1 << 6)
++#define MSC_IREG_RXFIFO_RD_REQ (1 << 5)
++#define MSC_IREG_END_CMD_RES (1 << 2)
++#define MSC_IREG_PRG_DONE (1 << 1)
++#define MSC_IREG_DATA_TRAN_DONE (1 << 0)
++
++static __inline__ unsigned int __cpm_get_pllout2(void)
++{
++ if (REG_CPM_CPCCR & CPM_CPCCR_PCS)
++ return __cpm_get_pllout();
++ else
++ return __cpm_get_pllout()/2;
++}
++
++static inline void __cpm_select_msc_clk(int sd)
++{
++ unsigned int pllout2 = __cpm_get_pllout2();
++ unsigned int div = 0;
++
++ if (sd) {
++ div = pllout2 / 24000000;
++ }
++ else {
++ div = pllout2 / 16000000;
++ }
++
++ REG_CPM_MSCCDR = div - 1;
++}
++#define __msc_reset() \
++do { \
++ REG_MSC_STRPCL = MSC_STRPCL_RESET; \
++ while (REG_MSC_STAT & MSC_STAT_IS_RESETTING); \
++} while (0)
++
+ #endif /* !__ASSEMBLY__ */
+ #endif /* __JZ4740_H__ */
+diff --git a/board/qi/qi_lb60/qi_lb60.c b/board/qi/qi_lb60/qi_lb60.c
+index 3bd4e2f..a2ba648 100644
+--- a/board/qi/qi_lb60/qi_lb60.c
++++ b/board/qi/qi_lb60/qi_lb60.c
+@@ -40,8 +40,13 @@ static void gpio_init(void)
+ __gpio_clear_pin(GPIO_KEYOUT_BASE + i);
+ }
+
+- __gpio_as_input(GPIO_KEYIN_8);
+- __gpio_enable_pull(GPIO_KEYIN_8);
++ if (__gpio_get_pin(GPIO_KEYIN_BASE + 2) == 0){
++ printf("[S] pressed, enable UART0\n");
++ __gpio_as_uart0();
++ } else {
++ __gpio_as_input(GPIO_KEYIN_8);
++ __gpio_enable_pull(GPIO_KEYIN_8);
++ }
+
+ /* enable the TP4, TP5 as UART0 */
+ __gpio_jtag_to_uart0();
+diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
+index 565ba6a..3c717b1 100644
+--- a/drivers/mmc/Makefile
++++ b/drivers/mmc/Makefile
+@@ -47,6 +47,7 @@ COBJS-$(CONFIG_SDHCI) += sdhci.o
+ COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
+ COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o
+ COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o
++COBJS-$(CONFIG_JZ4740_MMC) += jz_mmc.o
+
+ COBJS := $(COBJS-y)
+ SRCS := $(COBJS:.o=.c)
+diff --git a/drivers/mmc/jz_mmc.c b/drivers/mmc/jz_mmc.c
+new file mode 100644
+index 0000000..642cecc
+--- /dev/null
++++ b/drivers/mmc/jz_mmc.c
+@@ -0,0 +1,1179 @@
++/*
++ * (C) Copyright 2003
++ * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
++ *
++ * See file CREDITS for list of people who contributed to this
++ * project.
++ *
++ * 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 program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include <config.h>
++#include <common.h>
++#include <part.h>
++#include <mmc.h>
++
++#include <asm/io.h>
++#include <asm/jz4740.h>
++#include "jz_mmc.h"
++
++static int sd2_0 = 0;
++static int mmc_ready = 0;
++static int use_4bit; /* Use 4-bit data bus */
++/*
++ * MMC Events
++ */
++#define MMC_EVENT_NONE 0x00 /* No events */
++#define MMC_EVENT_RX_DATA_DONE 0x01 /* Rx data done */
++#define MMC_EVENT_TX_DATA_DONE 0x02 /* Tx data done */
++#define MMC_EVENT_PROG_DONE 0x04 /* Programming is done */
++
++
++#define MMC_IRQ_MASK() \
++do { \
++ REG_MSC_IMASK = 0xffff; \
++ REG_MSC_IREG = 0xffff; \
++} while (0)
++
++/*
++ * GPIO definition
++ */
++#if defined(CONFIG_SAKC)
++
++#define __msc_init_io() \
++do { \
++ __gpio_as_input(GPIO_SD_CD_N); \
++} while (0)
++
++#else
++#define __msc_init_io() \
++do { \
++ __gpio_as_output(GPIO_SD_VCC_EN_N); \
++ __gpio_as_input(GPIO_SD_CD_N); \
++} while (0)
++
++#define __msc_enable_power() \
++do { \
++ __gpio_clear_pin(GPIO_SD_VCC_EN_N); \
++} while (0)
++
++#define __msc_disable_power() \
++do { \
++ __gpio_set_pin(GPIO_SD_VCC_EN_N); \
++} while (0)
++
++#endif /* CONFIG_SAKE */
++
++#define __msc_card_detected() \
++({ \
++ int detected = 1; \
++ __gpio_as_input(GPIO_SD_CD_N); \
++ __gpio_disable_pull(GPIO_SD_CD_N); \
++ if (!__gpio_get_pin(GPIO_SD_CD_N)) \
++ detected = 0; \
++ detected; \
++})
++
++/*
++ * Local functions
++ */
++
++extern int
++fat_register_device(block_dev_desc_t *dev_desc, int part_no);
++
++static block_dev_desc_t mmc_dev;
++
++block_dev_desc_t * mmc_get_dev(int dev)
++{
++ return ((block_dev_desc_t *)&mmc_dev);
++}
++
++/* Stop the MMC clock and wait while it happens */
++static inline int jz_mmc_stop_clock(void)
++{
++ int timeout = 1000;
++
++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_STOP;
++
++ while (timeout && (REG_MSC_STAT & MSC_STAT_CLK_EN)) {
++ timeout--;
++ if (timeout == 0)
++ return MMC_ERROR_TIMEOUT;
++ udelay(1);
++ }
++ return MMC_NO_ERROR;
++}
++
++/* Start the MMC clock and operation */
++static inline int jz_mmc_start_clock(void)
++{
++ REG_MSC_STRPCL = MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP;
++ return MMC_NO_ERROR;
++}
++
++static inline u32 jz_mmc_calc_clkrt(int is_sd, u32 rate)
++{
++ u32 clkrt = 0;
++ u32 clk_src = is_sd ? 24000000 : 16000000;
++
++ while (rate < clk_src) {
++ clkrt ++;
++ clk_src >>= 1;
++ }
++
++ return clkrt;
++}
++
++/* Set the MMC clock frequency */
++void jz_mmc_set_clock(int sd, u32 rate)
++{
++ jz_mmc_stop_clock();
++
++ /* Select clock source of MSC */
++ __cpm_select_msc_clk(sd);
++
++ /* Set clock dividor of MSC */
++ REG_MSC_CLKRT = jz_mmc_calc_clkrt(sd, rate);
++}
++
++static int jz_mmc_check_status(struct mmc_request *request)
++{
++ u32 status = REG_MSC_STAT;
++
++ /* Checking for response or data timeout */
++ if (status & (MSC_STAT_TIME_OUT_RES | MSC_STAT_TIME_OUT_READ)) {
++ printf("MMC/SD timeout, MMC_STAT 0x%x CMD %d\n", status, request->cmd);
++ return MMC_ERROR_TIMEOUT;
++ }
++
++ /* Checking for CRC error */
++ if (status & (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR | MSC_STAT_CRC_RES_ERR)) {
++ printf("MMC/CD CRC error, MMC_STAT 0x%x\n", status);
++ return MMC_ERROR_CRC;
++ }
++
++ return MMC_NO_ERROR;
++}
++
++/* Obtain response to the command and store it to response buffer */
++static void jz_mmc_get_response(struct mmc_request *request)
++{
++ int i;
++ u8 *buf;
++ u32 data;
++
++ debug("fetch response for request %d, cmd %d\n",
++ request->rtype, request->cmd);
++
++ buf = request->response;
++ request->result = MMC_NO_ERROR;
++
++ switch (request->rtype) {
++ case RESPONSE_R1: case RESPONSE_R1B: case RESPONSE_R6:
++ case RESPONSE_R3: case RESPONSE_R4: case RESPONSE_R5:
++ {
++ data = REG_MSC_RES;
++ buf[0] = (data >> 8) & 0xff;
++ buf[1] = data & 0xff;
++ data = REG_MSC_RES;
++ buf[2] = (data >> 8) & 0xff;
++ buf[3] = data & 0xff;
++ data = REG_MSC_RES;
++ buf[4] = data & 0xff;
++
++ debug("request %d, response [%02x %02x %02x %02x %02x]\n",
++ request->rtype, buf[0], buf[1], buf[2], buf[3], buf[4]);
++ break;
++ }
++ case RESPONSE_R2_CID: case RESPONSE_R2_CSD:
++ {
++ for (i = 0; i < 16; i += 2) {
++ data = REG_MSC_RES;
++ buf[i] = (data >> 8) & 0xff;
++ buf[i+1] = data & 0xff;
++ }
++ debug("request %d, response [", request->rtype);
++#if CONFIG_MMC_DEBUG_VERBOSE > 2
++ if (g_mmc_debug >= 3) {
++ int n;
++ for (n = 0; n < 17; n++)
++ printk("%02x ", buf[n]);
++ printk("]\n");
++ }
++#endif
++ break;
++ }
++ case RESPONSE_NONE:
++ debug("No response\n");
++ break;
++
++ default:
++ debug("unhandled response type for request %d\n", request->rtype);
++ break;
++ }
++}
++
++static int jz_mmc_receive_data(struct mmc_request *req)
++{
++ u32 stat, timeout, data, cnt;
++ u8 *buf = req->buffer;
++ u32 wblocklen = (u32)(req->block_len + 3) >> 2; /* length in word */
++
++ timeout = 0x3ffffff;
++
++ while (timeout) {
++ timeout--;
++ stat = REG_MSC_STAT;
++
++ if (stat & MSC_STAT_TIME_OUT_READ)
++ return MMC_ERROR_TIMEOUT;
++ else if (stat & MSC_STAT_CRC_READ_ERROR)
++ return MMC_ERROR_CRC;
++ else if (!(stat & MSC_STAT_DATA_FIFO_EMPTY)
++ || (stat & MSC_STAT_DATA_FIFO_AFULL)) {
++ /* Ready to read data */
++ break;
++ }
++ udelay(1);
++ }
++ if (!timeout)
++ return MMC_ERROR_TIMEOUT;
++
++ /* Read data from RXFIFO. It could be FULL or PARTIAL FULL */
++ cnt = wblocklen;
++ while (cnt) {
++ data = REG_MSC_RXFIFO;
++ {
++ *buf++ = (u8)(data >> 0);
++ *buf++ = (u8)(data >> 8);
++ *buf++ = (u8)(data >> 16);
++ *buf++ = (u8)(data >> 24);
++ }
++ cnt --;
++ while (cnt && (REG_MSC_STAT & MSC_STAT_DATA_FIFO_EMPTY))
++ ;
++ }
++ return MMC_NO_ERROR;
++}
++
++static int jz_mmc_transmit_data(struct mmc_request *req)
++{
++#if 0
++ u32 nob = req->nob;
++ u32 wblocklen = (u32)(req->block_len + 3) >> 2; /* length in word */
++ u8 *buf = req->buffer;
++ u32 *wbuf = (u32 *)buf;
++ u32 waligned = (((u32)buf & 0x3) == 0); /* word aligned ? */
++ u32 stat, timeout, data, cnt;
++
++ for (nob; nob >= 1; nob--) {
++ timeout = 0x3FFFFFF;
++
++ while (timeout) {
++ timeout--;
++ stat = REG_MSC_STAT;
++
++ if (stat & (MSC_STAT_CRC_WRITE_ERROR | MSC_STAT_CRC_WRITE_ERROR_NOSTS))
++ return MMC_ERROR_CRC;
++ else if (!(stat & MSC_STAT_DATA_FIFO_FULL)) {
++ /* Ready to write data */
++ break;
++ }
++
++ udelay(1);
++ }
++
++ if (!timeout)
++ return MMC_ERROR_TIMEOUT;
++
++ /* Write data to TXFIFO */
++ cnt = wblocklen;
++ while (cnt) {
++ while (REG_MSC_STAT & MSC_STAT_DATA_FIFO_FULL)
++ ;
++
++ if (waligned) {
++ REG_MSC_TXFIFO = *wbuf++;
++ }
++ else {
++ data = *buf++ | (*buf++ << 8) | (*buf++ << 16) | (*buf++ << 24);
++ REG_MSC_TXFIFO = data;
++ }
++
++ cnt--;
++ }
++ }
++#endif
++ return MMC_NO_ERROR;
++}
++
++
++/*
++ * Name: int jz_mmc_exec_cmd()
++ * Function: send command to the card, and get a response
++ * Input: struct mmc_request *req : MMC/SD request
++ * Output: 0: right >0: error code
++ */
++int jz_mmc_exec_cmd(struct mmc_request *request)
++{
++ u32 cmdat = 0, events = 0;
++ int retval, timeout = 0x3fffff;
++
++ /* Indicate we have no result yet */
++ request->result = MMC_NO_RESPONSE;
++ if (request->cmd == MMC_CIM_RESET) {
++ /* On reset, 1-bit bus width */
++ use_4bit = 0;
++
++ /* Reset MMC/SD controller */
++ __msc_reset();
++
++ /* On reset, drop MMC clock down */
++ jz_mmc_set_clock(0, MMC_CLOCK_SLOW);
++
++ /* On reset, stop MMC clock */
++ jz_mmc_stop_clock();
++ }
++ if (request->cmd == MMC_CMD_SEND_OP_COND) {
++ debug("Have an MMC card\n");
++ /* always use 1bit for MMC */
++ use_4bit = 0;
++ }
++ if (request->cmd == SET_BUS_WIDTH) {
++ if (request->arg == 0x2) {
++ printf("Use 4-bit bus width\n");
++ use_4bit = 1;
++ } else {
++ printf("Use 1-bit bus width\n");
++ use_4bit = 0;
++ }
++ }
++
++ /* stop clock */
++ jz_mmc_stop_clock();
++
++ /* mask all interrupts */
++ REG_MSC_IMASK = 0xffff;
++
++ /* clear status */
++ REG_MSC_IREG = 0xffff;
++
++ /* use 4-bit bus width when possible */
++ if (use_4bit)
++ cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT;
++
++ /* Set command type and events */
++ switch (request->cmd) {
++ /* MMC core extra command */
++ case MMC_CIM_RESET:
++ cmdat |= MSC_CMDAT_INIT; /* Initialization sequence sent prior to command */
++ break;
++
++ /* bc - broadcast - no response */
++ case MMC_CMD_GO_IDLE_STATE:
++ case MMC_CMD_SET_DSR:
++ break;
++
++ /* bcr - broadcast with response */
++ case MMC_CMD_SEND_OP_COND:
++ case MMC_CMD_ALL_SEND_CID:
++ case MMC_GO_IRQ_STATE:
++ break;
++
++ /* adtc - addressed with data transfer */
++ case MMC_READ_DAT_UNTIL_STOP:
++ case MMC_CMD_READ_SINGLE_BLOCK:
++ case MMC_CMD_READ_MULTIPLE_BLOCK:
++ case SD_CMD_APP_SEND_SCR:
++ cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ;
++ events = MMC_EVENT_RX_DATA_DONE;
++ break;
++
++ case MMC_WRITE_DAT_UNTIL_STOP:
++ case MMC_CMD_WRITE_SINGLE_BLOCK:
++ case MMC_CMD_WRITE_MULTIPLE_BLOCK:
++ case MMC_PROGRAM_CID:
++ case MMC_PROGRAM_CSD:
++ case MMC_SEND_WRITE_PROT:
++ case MMC_GEN_CMD:
++ case MMC_LOCK_UNLOCK:
++ cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE;
++ events = MMC_EVENT_TX_DATA_DONE | MMC_EVENT_PROG_DONE;
++
++ break;
++
++ case MMC_CMD_STOP_TRANSMISSION:
++ events = MMC_EVENT_PROG_DONE;
++ break;
++
++ /* ac - no data transfer */
++ default:
++ break;
++ }
++
++ /* Set response type */
++ switch (request->rtype) {
++ case RESPONSE_NONE:
++ break;
++
++ case RESPONSE_R1B:
++ cmdat |= MSC_CMDAT_BUSY;
++ /*FALLTHRU*/
++ case RESPONSE_R1:
++ cmdat |= MSC_CMDAT_RESPONSE_R1;
++ break;
++ case RESPONSE_R2_CID:
++ case RESPONSE_R2_CSD:
++ cmdat |= MSC_CMDAT_RESPONSE_R2;
++ break;
++ case RESPONSE_R3:
++ cmdat |= MSC_CMDAT_RESPONSE_R3;
++ break;
++ case RESPONSE_R4:
++ cmdat |= MSC_CMDAT_RESPONSE_R4;
++ break;
++ case RESPONSE_R5:
++ cmdat |= MSC_CMDAT_RESPONSE_R5;
++ break;
++ case RESPONSE_R6:
++ cmdat |= MSC_CMDAT_RESPONSE_R6;
++ break;
++ default:
++ break;
++ }
++
++ /* Set command index */
++ if (request->cmd == MMC_CIM_RESET) {
++ REG_MSC_CMD = MMC_CMD_GO_IDLE_STATE;
++ } else {
++ REG_MSC_CMD = request->cmd;
++ }
++
++ /* Set argument */
++ REG_MSC_ARG = request->arg;
++
++ /* Set block length and nob */
++ if (request->cmd == SD_CMD_APP_SEND_SCR) { /* get SCR from DataFIFO */
++ REG_MSC_BLKLEN = 8;
++ REG_MSC_NOB = 1;
++ } else {
++ REG_MSC_BLKLEN = request->block_len;
++ REG_MSC_NOB = request->nob;
++ }
++
++ /* Set command */
++ REG_MSC_CMDAT = cmdat;
++
++ debug("Send cmd %d cmdat: %x arg: %x resp %d\n", request->cmd,
++ cmdat, request->arg, request->rtype);
++
++ /* Start MMC/SD clock and send command to card */
++ jz_mmc_start_clock();
++
++ /* Wait for command completion */
++ while (timeout-- && !(REG_MSC_STAT & MSC_STAT_END_CMD_RES))
++ ;
++
++ if (timeout == 0)
++ return MMC_ERROR_TIMEOUT;
++
++ REG_MSC_IREG = MSC_IREG_END_CMD_RES; /* clear flag */
++
++ /* Check for status */
++ retval = jz_mmc_check_status(request);
++ if (retval) {
++ return retval;
++ }
++
++ /* Complete command with no response */
++ if (request->rtype == RESPONSE_NONE) {
++ return MMC_NO_ERROR;
++ }
++
++ /* Get response */
++ jz_mmc_get_response(request);
++
++ /* Start data operation */
++ if (events & (MMC_EVENT_RX_DATA_DONE | MMC_EVENT_TX_DATA_DONE)) {
++ if (events & MMC_EVENT_RX_DATA_DONE) {
++ if (request->cmd == SD_CMD_APP_SEND_SCR) {
++ /* SD card returns SCR register as data.
++ MMC core expect it in the response buffer,
++ after normal response. */
++ request->buffer = (u8 *)((u32)request->response + 5);
++ }
++ jz_mmc_receive_data(request);
++ }
++
++ if (events & MMC_EVENT_TX_DATA_DONE) {
++ jz_mmc_transmit_data(request);
++ }
++
++ /* Wait for Data Done */
++ while (!(REG_MSC_IREG & MSC_IREG_DATA_TRAN_DONE))
++ ;
++ REG_MSC_IREG = MSC_IREG_DATA_TRAN_DONE; /* clear status */
++ }
++
++ /* Wait for Prog Done event */
++ if (events & MMC_EVENT_PROG_DONE) {
++ while (!(REG_MSC_IREG & MSC_IREG_PRG_DONE))
++ ;
++ REG_MSC_IREG = MSC_IREG_PRG_DONE; /* clear status */
++ }
++
++ /* Command completed */
++
++ return MMC_NO_ERROR; /* return successfully */
++}
++
++int mmc_block_read(u8 *dst, ulong src, ulong len)
++{
++
++ struct mmc_request request;
++ struct mmc_response_r1 r1;
++ int retval = 0;
++
++ if (len == 0)
++ goto exit;
++
++ mmc_simple_cmd(&request, MMC_CMD_SEND_STATUS, mmcinfo.rca, RESPONSE_R1);
++ retval = mmc_unpack_r1(&request, &r1, 0);
++ if (retval && (retval != MMC_ERROR_STATE_MISMATCH))
++ goto exit;
++
++ mmc_simple_cmd(&request, MMC_CMD_SET_BLOCKLEN, len, RESPONSE_R1);
++ if (retval = mmc_unpack_r1(&request, &r1, 0))
++ goto exit;
++
++ if (!sd2_0)
++ src *= mmcinfo.block_len;
++
++ mmc_send_cmd(&request, MMC_CMD_READ_SINGLE_BLOCK, src, 1, len, RESPONSE_R1, dst);
++ if (retval = mmc_unpack_r1(&request, &r1, 0))
++ goto exit;
++
++exit:
++ return retval;
++}
++
++ulong mmc_bread(int dev_num, ulong blkstart, ulong blkcnt, ulong *dst)
++{
++ if (!mmc_ready) {
++ printf("Please initial the MMC first\n");
++ return -1;
++ }
++
++ int i = 0;
++ ulong dst_tmp = dst;
++
++ for (i = 0; i < blkcnt; i++) {
++ if ((mmc_block_read((uchar *)(dst_tmp), blkstart, mmcinfo.block_len)) < 0)
++ return -1;
++
++ dst_tmp += mmcinfo.block_len;
++ blkstart++;
++ }
++
++ return i;
++}
++
++int mmc_select_card(void)
++{
++ struct mmc_request request;
++ struct mmc_response_r1 r1;
++ int retval;
++
++ mmc_simple_cmd(&request, MMC_CMD_SELECT_CARD, mmcinfo.rca, RESPONSE_R1B);
++ retval = mmc_unpack_r1(&request, &r1, 0);
++ if (retval) {
++ return retval;
++ }
++
++ if (mmcinfo.sd) {
++ mmc_simple_cmd(&request, MMC_CMD_APP_CMD, mmcinfo.rca, RESPONSE_R1);
++ retval = mmc_unpack_r1(&request,&r1,0);
++ if (retval) {
++ return retval;
++ }
++#if defined(MMC_BUS_WIDTH_1BIT)
++ mmc_simple_cmd(&request, SET_BUS_WIDTH, 1, RESPONSE_R1);
++#else
++ mmc_simple_cmd(&request, SET_BUS_WIDTH, 2, RESPONSE_R1);
++#endif
++ retval = mmc_unpack_r1(&request,&r1,0);
++ if (retval) {
++ return retval;
++ }
++ }
++ return 0;
++}
++
++/*
++ * Configure card
++ */
++static void mmc_configure_card(void)
++{
++ u32 rate;
++
++ /* Get card info */
++ if (sd2_0)
++ mmcinfo.block_num = (mmcinfo.csd.c_size + 1) << 10;
++ else
++ mmcinfo.block_num = (mmcinfo.csd.c_size + 1) * (1 << (mmcinfo.csd.c_size_mult + 2));
++
++ mmcinfo.block_len = 1 << mmcinfo.csd.read_bl_len;
++
++ mmc_dev.if_type = IF_TYPE_SD;
++ mmc_dev.part_type = PART_TYPE_DOS;
++ mmc_dev.dev = 0;
++ mmc_dev.lun = 0;
++ mmc_dev.type = 0;
++ mmc_dev.blksz = mmcinfo.block_len;
++ mmc_dev.lba = mmcinfo.block_num;
++ mmc_dev.removable = 0;
++
++ printf("%s Detected: %lu blocks of %lu bytes\n",
++ sd2_0 == 1 ? "SDHC" : "SD",
++ mmc_dev.lba,
++ mmc_dev.blksz);
++
++ /* Fix the clock rate */
++ rate = mmc_tran_speed(mmcinfo.csd.tran_speed);
++ if (rate < MMC_CLOCK_SLOW)
++ rate = MMC_CLOCK_SLOW;
++ if ((mmcinfo.sd == 0) && (rate > MMC_CLOCK_FAST))
++ rate = MMC_CLOCK_FAST;
++ if ((mmcinfo.sd) && (rate > SD_CLOCK_FAST))
++ rate = SD_CLOCK_FAST;
++
++ debug("%s: block_len=%d block_num=%d rate=%d\n",
++ __func__, mmcinfo.block_len, mmcinfo.block_num, rate);
++
++ jz_mmc_set_clock(mmcinfo.sd, rate);
++}
++
++/*
++ * State machine routines to initialize card(s)
++ */
++
++/*
++ CIM_SINGLE_CARD_ACQ (frequency at 400 kHz)
++ --- Must enter from GO_IDLE_STATE ---
++ 1. SD_SEND_OP_COND (SD Card) [CMD55] + [CMD41]
++ 2. SEND_OP_COND (Full Range) [CMD1] {optional}
++ 3. SEND_OP_COND (Set Range ) [CMD1]
++ If busy, delay and repeat step 2
++ 4. ALL_SEND_CID [CMD2]
++ If timeout, set an error (no cards found)
++ 5. SET_RELATIVE_ADDR [CMD3]
++ 6. SEND_CSD [CMD9]
++ 7. SET_DSR [CMD4] Only call this if (csd.dsr_imp).
++ 8. Set clock frequency (check available in csd.tran_speed)
++ */
++
++#define MMC_INIT_DOING 0
++#define MMC_INIT_PASSED 1
++#define MMC_INIT_FAILED 2
++
++static int mmc_init_card_state(struct mmc_request *request)
++{
++ struct mmc_response_r1 r1;
++ struct mmc_response_r3 r3;
++ int retval;
++ int ocr = 0x40300000;
++ int limit_41 = 0;
++
++ switch (request->cmd) {
++ case MMC_CMD_GO_IDLE_STATE: /* No response to parse */
++ if (mmcinfo.sd)
++ mmc_simple_cmd(request, 8, 0x1aa, RESPONSE_R1);
++ else
++ mmc_simple_cmd(request, MMC_CMD_SEND_OP_COND, MMC_OCR_ARG, RESPONSE_R3);
++ break;
++
++ case 8:
++ retval = mmc_unpack_r1(request,&r1,mmcinfo.state);
++ mmc_simple_cmd(request, MMC_CMD_APP_CMD, 0, RESPONSE_R1);
++ break;
++
++ case MMC_CMD_APP_CMD:
++ retval = mmc_unpack_r1(request,&r1,mmcinfo.state);
++ if (retval & (limit_41 < 100)) {
++ debug("%s: unable to MMC_APP_CMD error=%d (%s)\n",
++ __func__, retval, mmc_result_to_string(retval));
++ limit_41++;
++ mmc_simple_cmd(request, SD_CMD_APP_SEND_OP_COND, ocr, RESPONSE_R3);
++ } else if (limit_41 < 100) {
++ limit_41++;
++ mmc_simple_cmd(request, SD_CMD_APP_SEND_OP_COND, ocr, RESPONSE_R3);
++ } else{
++ /* reset the card to idle*/
++ mmc_simple_cmd(request, MMC_CMD_GO_IDLE_STATE, 0, RESPONSE_NONE);
++ mmcinfo.sd = 0;
++ }
++ break;
++
++ case SD_CMD_APP_SEND_OP_COND:
++ retval = mmc_unpack_r3(request, &r3);
++ if (retval) {
++ debug("%s: try MMC card\n", __func__);
++ mmc_simple_cmd(request, SD_CMD_APP_SEND_OP_COND, MMC_OCR_ARG, RESPONSE_R3);
++ break;
++ }
++
++ debug("%s: read ocr value = 0x%08x\n", __func__, r3.ocr);
++
++ if(!(r3.ocr & MMC_CARD_BUSY || ocr == 0)){
++ udelay(50000);
++ mmc_simple_cmd(request, MMC_CMD_APP_CMD, 0, RESPONSE_R1);
++ } else {
++ mmcinfo.sd = 1; /* SD Card ready */
++ mmcinfo.state = CARD_STATE_READY;
++ mmc_simple_cmd(request, MMC_CMD_ALL_SEND_CID, 0, RESPONSE_R2_CID);
++ }
++ break;
++
++ case MMC_CMD_SEND_OP_COND:
++ retval = mmc_unpack_r3(request, &r3);
++ if (retval) {
++ debug("%s: failed SEND_OP_COND error=%d (%s)\n",
++ __func__, retval, mmc_result_to_string(retval));
++ return MMC_INIT_FAILED;
++ }
++
++ debug("%s: read ocr value = 0x%08x\n", __func__, r3.ocr);
++ if (!(r3.ocr & MMC_CARD_BUSY)) {
++ mmc_simple_cmd(request, MMC_CMD_SEND_OP_COND, MMC_OCR_ARG, RESPONSE_R3);
++ } else {
++ mmcinfo.sd = 0; /* MMC Card ready */
++ mmcinfo.state = CARD_STATE_READY;
++ mmc_simple_cmd(request, MMC_CMD_ALL_SEND_CID, 0, RESPONSE_R2_CID);
++ }
++ break;
++
++ case MMC_CMD_ALL_SEND_CID:
++ retval = mmc_unpack_cid( request, &mmcinfo.cid );
++ /*FIXME:ignore CRC error for CMD2/CMD9/CMD10 */
++ if ( retval && (retval != MMC_ERROR_CRC)) {
++ debug("mmc_init_card_state: unable to ALL_SEND_CID error=%d (%s)\n",
++ retval, mmc_result_to_string(retval));
++ return MMC_INIT_FAILED;
++ }
++ mmcinfo.state = CARD_STATE_IDENT;
++ if(mmcinfo.sd)
++ mmc_simple_cmd(request, MMC_CMD_SET_RELATIVE_ADDR, 0, RESPONSE_R6);
++ else
++ mmc_simple_cmd(request, MMC_CMD_SET_RELATIVE_ADDR, ID_TO_RCA(mmcinfo.id) << 16, RESPONSE_R1);
++ break;
++
++ case MMC_CMD_SET_RELATIVE_ADDR:
++ if (mmcinfo.sd) {
++ retval = mmc_unpack_r6(request, &r1, mmcinfo.state, &mmcinfo.rca);
++ mmcinfo.rca = mmcinfo.rca << 16;
++ debug("%s: Get RCA from SD: 0x%04x Status: %x\n",
++ __func__, mmcinfo.rca, r1.status);
++ } else {
++ retval = mmc_unpack_r1(request,&r1,mmcinfo.state);
++ mmcinfo.rca = ID_TO_RCA(mmcinfo.id) << 16;
++ }
++ if (retval) {
++ debug("%s: unable to SET_RELATIVE_ADDR error=%d (%s)\n",
++ __func__, retval, mmc_result_to_string(retval));
++ return MMC_INIT_FAILED;
++ }
++
++ mmcinfo.state = CARD_STATE_STBY;
++ mmc_simple_cmd(request, MMC_CMD_SEND_CSD, mmcinfo.rca, RESPONSE_R2_CSD);
++
++ break;
++
++ case MMC_CMD_SEND_CSD:
++ retval = mmc_unpack_csd(request, &mmcinfo.csd);
++ mmc_ready = 1;
++ /*FIXME:ignore CRC error for CMD2/CMD9/CMD10 */
++ if (retval && (retval != MMC_ERROR_CRC)) {
++ debug("%s: unable to SEND_CSD error=%d (%s)\n",
++ __func__, retval, mmc_result_to_string(retval));
++ return MMC_INIT_FAILED;
++ }
++ if (mmcinfo.csd.dsr_imp) {
++ debug("%s: driver doesn't support setting DSR\n", __func__);
++ }
++ mmc_configure_card();
++ return MMC_INIT_PASSED;
++
++ default:
++ debug("%s: error! Illegal last cmd %d\n", __func__, request->cmd);
++ return MMC_INIT_FAILED;
++ }
++
++ return MMC_INIT_DOING;
++}
++
++int mmc_init_card(void)
++{
++ struct mmc_request request;
++ int retval;
++
++ mmc_simple_cmd(&request, MMC_CIM_RESET, 0, RESPONSE_NONE); /* reset card */
++ mmc_simple_cmd(&request, MMC_CMD_GO_IDLE_STATE, 0, RESPONSE_NONE);
++ mmcinfo.sd = 1; /* assuming a SD card */
++
++ while ((retval = mmc_init_card_state(&request)) == MMC_INIT_DOING)
++ ;
++
++ if (retval == MMC_INIT_PASSED)
++ return MMC_NO_ERROR;
++ else
++ return MMC_NO_RESPONSE;
++}
++
++int mmc_legacy_init(int verbose)
++{
++ if (!__msc_card_detected())
++ return 1;
++
++ /* Step-1: init GPIO */
++ __gpio_as_msc();
++ __msc_init_io();
++
++ /* Step-2: turn on power of card */
++#if !defined(CONFIG_SAKC)
++ __msc_enable_power();
++#endif
++
++ /* Step-3: Reset MSC Controller. */
++ __msc_reset();
++
++ /* Step-3: mask all IRQs. */
++ MMC_IRQ_MASK();
++
++ /* Step-4: stop MMC/SD clock */
++ jz_mmc_stop_clock();
++ mmc_init_card();
++ mmc_select_card();
++
++ mmc_dev.block_read = mmc_bread;
++ fat_register_device(&mmc_dev,1); /* partitions start counting with 1 */
++
++ return 0;
++}
++
++/*
++ * Debugging functions
++ */
++static char * mmc_result_strings[] = {
++ "NO_RESPONSE",
++ "NO_ERROR",
++ "ERROR_OUT_OF_RANGE",
++ "ERROR_ADDRESS",
++ "ERROR_BLOCK_LEN",
++ "ERROR_ERASE_SEQ",
++ "ERROR_ERASE_PARAM",
++ "ERROR_WP_VIOLATION",
++ "ERROR_CARD_IS_LOCKED",
++ "ERROR_LOCK_UNLOCK_FAILED",
++ "ERROR_COM_CRC",
++ "ERROR_ILLEGAL_COMMAND",
++ "ERROR_CARD_ECC_FAILED",
++ "ERROR_CC",
++ "ERROR_GENERAL",
++ "ERROR_UNDERRUN",
++ "ERROR_OVERRUN",
++ "ERROR_CID_CSD_OVERWRITE",
++ "ERROR_STATE_MISMATCH",
++ "ERROR_HEADER_MISMATCH",
++ "ERROR_TIMEOUT",
++ "ERROR_CRC",
++ "ERROR_DRIVER_FAILURE",
++};
++
++char * mmc_result_to_string(int i)
++{
++ return mmc_result_strings[i+1];
++}
++
++static char * card_state_strings[] = {
++ "empty",
++ "idle",
++ "ready",
++ "ident",
++ "stby",
++ "tran",
++ "data",
++ "rcv",
++ "prg",
++ "dis",
++};
++
++static inline char * card_state_to_string(int i)
++{
++ return card_state_strings[i+1];
++}
++
++/*
++ * Utility functions
++ */
++
++#define PARSE_U32(_buf,_index) \
++ (((u32)_buf[_index]) << 24) | (((u32)_buf[_index+1]) << 16) | \
++ (((u32)_buf[_index+2]) << 8) | ((u32)_buf[_index+3]);
++
++#define PARSE_U16(_buf,_index) \
++ (((u16)_buf[_index]) << 8) | ((u16)_buf[_index+1]);
++
++int mmc_unpack_csd(struct mmc_request *request, struct mmc_csd *csd)
++{
++ u8 *buf = request->response;
++ int num = 0;
++
++ if (request->result)
++ return request->result;
++
++ if (buf[0] != 0x3f)
++ return MMC_ERROR_HEADER_MISMATCH;
++
++ csd->csd_structure = (buf[1] & 0xc0) >> 6;
++ if (csd->csd_structure)
++ sd2_0 = 1;
++ else
++ sd2_0 = 0;
++
++ switch (csd->csd_structure) {
++ case 0 :/* Version 1.01-1.10
++ * Version 2.00/Standard Capacity */
++ csd->taac = buf[2];
++ csd->nsac = buf[3];
++ csd->tran_speed = buf[4];
++ csd->ccc = (((u16)buf[5]) << 4) | ((buf[6] & 0xf0) >> 4);
++ csd->read_bl_len = buf[6] & 0x0f;
++ /* for support 2GB card*/
++ if (csd->read_bl_len >= 10)
++ {
++ num = csd->read_bl_len - 9;
++ csd->read_bl_len = 9;
++ }
++
++ csd->read_bl_partial = (buf[7] & 0x80) ? 1 : 0;
++ csd->write_blk_misalign = (buf[7] & 0x40) ? 1 : 0;
++ csd->read_blk_misalign = (buf[7] & 0x20) ? 1 : 0;
++ csd->dsr_imp = (buf[7] & 0x10) ? 1 : 0;
++ csd->c_size = ((((u16)buf[7]) & 0x03) << 10) | (((u16)buf[8]) << 2) | (((u16)buf[9]) & 0xc0) >> 6;
++
++ if (num)
++ csd->c_size = csd->c_size << num;
++
++
++ csd->vdd_r_curr_min = (buf[9] & 0x38) >> 3;
++ csd->vdd_r_curr_max = buf[9] & 0x07;
++ csd->vdd_w_curr_min = (buf[10] & 0xe0) >> 5;
++ csd->vdd_w_curr_max = (buf[10] & 0x1c) >> 2;
++ csd->c_size_mult = ((buf[10] & 0x03) << 1) | ((buf[11] & 0x80) >> 7);
++ csd->sector_size = (buf[11] & 0x7c) >> 2;
++ csd->erase_grp_size = ((buf[11] & 0x03) << 3) | ((buf[12] & 0xe0) >> 5);
++ csd->wp_grp_size = buf[12] & 0x1f;
++ csd->wp_grp_enable = (buf[13] & 0x80) ? 1 : 0;
++ csd->default_ecc = (buf[13] & 0x60) >> 5;
++ csd->r2w_factor = (buf[13] & 0x1c) >> 2;
++ csd->write_bl_len = ((buf[13] & 0x03) << 2) | ((buf[14] & 0xc0) >> 6);
++ if (csd->write_bl_len >= 10)
++ csd->write_bl_len = 9;
++
++ csd->write_bl_partial = (buf[14] & 0x20) ? 1 : 0;
++ csd->file_format_grp = (buf[15] & 0x80) ? 1 : 0;
++ csd->copy = (buf[15] & 0x40) ? 1 : 0;
++ csd->perm_write_protect = (buf[15] & 0x20) ? 1 : 0;
++ csd->tmp_write_protect = (buf[15] & 0x10) ? 1 : 0;
++ csd->file_format = (buf[15] & 0x0c) >> 2;
++ csd->ecc = buf[15] & 0x03;
++ break;
++ case 1 : /* Version 2.00/High Capacity */
++ csd->taac = 0;
++ csd->nsac = 0;
++ csd->tran_speed = buf[4];
++ csd->ccc = (((u16)buf[5]) << 4) | ((buf[6] & 0xf0) >> 4);
++
++ csd->read_bl_len = 9;
++ csd->read_bl_partial = 0;
++ csd->write_blk_misalign = 0;
++ csd->read_blk_misalign = 0;
++ csd->dsr_imp = (buf[7] & 0x10) ? 1 : 0;
++ csd->c_size = ((((u16)buf[8]) & 0x3f) << 16) | (((u16)buf[9]) << 8) | ((u16)buf[10]) ;
++ csd->sector_size = 0x7f;
++ csd->erase_grp_size = 0;
++ csd->wp_grp_size = 0;
++ csd->wp_grp_enable = 0;
++ csd->default_ecc = (buf[13] & 0x60) >> 5;
++ csd->r2w_factor = 4;/* Unused */
++ csd->write_bl_len = 9;
++
++ csd->write_bl_partial = 0;
++ csd->file_format_grp = 0;
++ csd->copy = (buf[15] & 0x40) ? 1 : 0;
++ csd->perm_write_protect = (buf[15] & 0x20) ? 1 : 0;
++ csd->tmp_write_protect = (buf[15] & 0x10) ? 1 : 0;
++ csd->file_format = 0;
++ csd->ecc = buf[15] & 0x03;
++ }
++
++ return 0;
++}
++
++int mmc_unpack_r1(struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state)
++{
++ u8 *buf = request->response;
++
++ if (request->result)
++ return request->result;
++
++ r1->cmd = buf[0];
++ r1->status = PARSE_U32(buf,1);
++
++ debug("mmc_unpack_r1: cmd=%d status=%08x\n", r1->cmd, r1->status);
++
++ if (R1_STATUS(r1->status)) {
++ if (r1->status & R1_OUT_OF_RANGE) return MMC_ERROR_OUT_OF_RANGE;
++ if (r1->status & R1_ADDRESS_ERROR) return MMC_ERROR_ADDRESS;
++ if (r1->status & R1_BLOCK_LEN_ERROR) return MMC_ERROR_BLOCK_LEN;
++ if (r1->status & R1_ERASE_SEQ_ERROR) return MMC_ERROR_ERASE_SEQ;
++ if (r1->status & R1_ERASE_PARAM) return MMC_ERROR_ERASE_PARAM;
++ if (r1->status & R1_WP_VIOLATION) return MMC_ERROR_WP_VIOLATION;
++ /*if (r1->status & R1_CARD_IS_LOCKED) return MMC_ERROR_CARD_IS_LOCKED; */
++ if (r1->status & R1_LOCK_UNLOCK_FAILED) return MMC_ERROR_LOCK_UNLOCK_FAILED;
++ if (r1->status & R1_COM_CRC_ERROR) return MMC_ERROR_COM_CRC;
++ if (r1->status & R1_ILLEGAL_COMMAND) return MMC_ERROR_ILLEGAL_COMMAND;
++ if (r1->status & R1_CARD_ECC_FAILED) return MMC_ERROR_CARD_ECC_FAILED;
++ if (r1->status & R1_CC_ERROR) return MMC_ERROR_CC;
++ if (r1->status & R1_ERROR) return MMC_ERROR_GENERAL;
++ if (r1->status & R1_UNDERRUN) return MMC_ERROR_UNDERRUN;
++ if (r1->status & R1_OVERRUN) return MMC_ERROR_OVERRUN;
++ if (r1->status & R1_CID_CSD_OVERWRITE) return MMC_ERROR_CID_CSD_OVERWRITE;
++ }
++
++ if (buf[0] != request->cmd)
++ return MMC_ERROR_HEADER_MISMATCH;
++
++ /* This should be last - it's the least dangerous error */
++
++ return 0;
++}
++
++int mmc_unpack_scr(struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state, u32 *scr)
++{
++ u8 *buf = request->response;
++ if (request->result)
++ return request->result;
++
++ *scr = PARSE_U32(buf, 5); /* Save SCR returned by the SD Card */
++ return mmc_unpack_r1(request, r1, state);
++
++}
++
++int mmc_unpack_r6(struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state, int *rca)
++{
++ u8 *buf = request->response;
++
++ if (request->result)
++ return request->result;
++
++ *rca = PARSE_U16(buf,1); /* Save RCA returned by the SD Card */
++
++ *(buf+1) = 0;
++ *(buf+2) = 0;
++
++ return mmc_unpack_r1(request, r1, state);
++}
++
++int mmc_unpack_cid(struct mmc_request *request, struct mmc_cid *cid)
++{
++ int i;
++ u8 *buf = request->response;
++
++ if (request->result)
++ return request->result;
++
++ cid->mid = buf[1];
++ cid->oid = PARSE_U16(buf,2);
++ for (i = 0 ; i < 5 ; i++)
++ cid->pnm[i] = buf[4+i];
++ cid->pnm[6] = 0;
++ cid->prv = buf[10];
++ cid->psn = PARSE_U32(buf,10);
++ cid->mdt = buf[15];
++
++ printf("Man %02x OEM 0x%04x \"%s\" %d.%d 0x%08x "
++ "Date %02u/%04u\n",
++ cid->mid,
++ cid->oid,
++ cid->pnm,
++ cid->prv >> 4,
++ cid->prv & 0xf,
++ cid->psn,
++ cid->mdt & 0xf,
++ (cid->mdt >> 4) + 2000);
++
++ if (buf[0] != 0x3f)
++ return MMC_ERROR_HEADER_MISMATCH;
++ return 0;
++}
++
++int mmc_unpack_r3(struct mmc_request *request, struct mmc_response_r3 *r3)
++{
++ u8 *buf = request->response;
++
++ if (request->result)
++ return request->result;
++
++ r3->ocr = PARSE_U32(buf,1);
++ debug("mmc_unpack_r3: ocr=%08x\n", r3->ocr);
++
++ if (buf[0] != 0x3f) return MMC_ERROR_HEADER_MISMATCH;
++ return 0;
++}
++
++#define KBPS 1
++#define MBPS 1000
++
++static u32 ts_exp[] = { 100*KBPS, 1*MBPS, 10*MBPS, 100*MBPS, 0, 0, 0, 0 };
++static u32 ts_mul[] = { 0, 1000, 1200, 1300, 1500, 2000, 2500, 3000,
++ 3500, 4000, 4500, 5000, 5500, 6000, 7000, 8000 };
++
++u32 mmc_tran_speed(u8 ts)
++{
++ u32 rate = ts_exp[(ts & 0x7)] * ts_mul[(ts & 0x78) >> 3];
++
++ if (rate <= 0) {
++ debug("%s: error - unrecognized speed 0x%02x\n", __func__, ts);
++ return 1;
++ }
++
++ return rate;
++}
++
++void mmc_send_cmd(struct mmc_request *request, int cmd, u32 arg,
++ u16 nob, u16 block_len, enum mmc_rsp_t rtype, u8 *buffer)
++{
++ request->cmd = cmd;
++ request->arg = arg;
++ request->rtype = rtype;
++ request->nob = nob;
++ request->block_len = block_len;
++ request->buffer = buffer;
++ request->cnt = nob * block_len;
++
++ jz_mmc_exec_cmd(request);
++}
+diff --git a/drivers/mmc/jz_mmc.h b/drivers/mmc/jz_mmc.h
+new file mode 100644
+index 0000000..936c514
+--- /dev/null
++++ b/drivers/mmc/jz_mmc.h
+@@ -0,0 +1,176 @@
++/*
++ * linux/drivers/mmc/jz_mmc.h
++ *
++ * Author: Vladimir Shebordaev, Igor Oblakov
++ * Copyright: MontaVista Software Inc.
++ *
++ * $Id: jz_mmc.h,v 1.3 2007-06-15 08:04:20 jlwei Exp $
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#ifndef __MMC_JZMMC_H__
++#define __MMC_JZMMC_H__
++
++#define ID_TO_RCA(x) ((x)+1)
++#define MMC_OCR_ARG 0x00ff8000 /* Argument of OCR */
++
++/* Standard MMC/SD clock speeds */
++#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */
++#define MMC_CLOCK_FAST 20000000 /* 20 MHz for maximum for normal operation */
++#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */
++
++/* Use negative numbers to disambiguate */
++#define MMC_CIM_RESET -1
++#define SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
++
++#define R1_OUT_OF_RANGE (1 << 31) /* er, c */
++#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */
++#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */
++#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */
++#define R1_ERASE_PARAM (1 << 27) /* ex, c */
++#define R1_WP_VIOLATION (1 << 26) /* erx, c */
++#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */
++#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */
++#define R1_COM_CRC_ERROR (1 << 23) /* er, b */
++#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */
++#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */
++#define R1_CC_ERROR (1 << 20) /* erx, c */
++#define R1_ERROR (1 << 19) /* erx, c */
++#define R1_UNDERRUN (1 << 18) /* ex, c */
++#define R1_OVERRUN (1 << 17) /* ex, c */
++#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */
++#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */
++#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
++#define R1_ERASE_RESET (1 << 13) /* sr, c */
++#define R1_STATUS(x) (x & 0xFFFFE000)
++
++#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
++
++#define MMC_PROGRAM_CID 26 /* adtc R1 */
++#define MMC_PROGRAM_CSD 27 /* adtc R1 */
++
++#define MMC_GO_IRQ_STATE 40 /* bcr R5 */
++#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1b */
++#define MMC_LOCK_UNLOCK 42 /* adtc R1b */
++#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
++#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
++#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */
++
++
++enum mmc_result_t {
++ MMC_NO_RESPONSE = -1,
++ MMC_NO_ERROR = 0,
++ MMC_ERROR_OUT_OF_RANGE,
++ MMC_ERROR_ADDRESS,
++ MMC_ERROR_BLOCK_LEN,
++ MMC_ERROR_ERASE_SEQ,
++ MMC_ERROR_ERASE_PARAM,
++ MMC_ERROR_WP_VIOLATION,
++ MMC_ERROR_CARD_IS_LOCKED,
++ MMC_ERROR_LOCK_UNLOCK_FAILED,
++ MMC_ERROR_COM_CRC,
++ MMC_ERROR_ILLEGAL_COMMAND,
++ MMC_ERROR_CARD_ECC_FAILED,
++ MMC_ERROR_CC,
++ MMC_ERROR_GENERAL,
++ MMC_ERROR_UNDERRUN,
++ MMC_ERROR_OVERRUN,
++ MMC_ERROR_CID_CSD_OVERWRITE,
++ MMC_ERROR_STATE_MISMATCH,
++ MMC_ERROR_HEADER_MISMATCH,
++ MMC_ERROR_TIMEOUT,
++ MMC_ERROR_CRC,
++ MMC_ERROR_DRIVER_FAILURE,
++};
++
++enum card_state {
++ CARD_STATE_EMPTY = -1,
++ CARD_STATE_IDLE = 0,
++ CARD_STATE_READY = 1,
++ CARD_STATE_IDENT = 2,
++ CARD_STATE_STBY = 3,
++ CARD_STATE_TRAN = 4,
++ CARD_STATE_DATA = 5,
++ CARD_STATE_RCV = 6,
++ CARD_STATE_PRG = 7,
++ CARD_STATE_DIS = 8,
++};
++
++enum mmc_rsp_t {
++ RESPONSE_NONE = 0,
++ RESPONSE_R1 = 1,
++ RESPONSE_R1B = 2,
++ RESPONSE_R2_CID = 3,
++ RESPONSE_R2_CSD = 4,
++ RESPONSE_R3 = 5,
++ RESPONSE_R4 = 6,
++ RESPONSE_R5 = 7,
++ RESPONSE_R6 = 8,
++};
++
++struct mmc_response_r1 {
++ u8 cmd;
++ u32 status;
++};
++
++struct mmc_response_r3 {
++ u32 ocr;
++};
++
++/* the information structure of MMC/SD Card */
++struct mmc_info {
++ int id; /* Card index */
++ int sd; /* MMC or SD card */
++ int rca; /* RCA */
++ u32 scr; /* SCR 63:32*/
++ int flags; /* Ejected, inserted */
++ enum card_state state; /* empty, ident, ready, whatever */
++
++ /* Card specific information */
++ struct mmc_cid cid;
++ struct mmc_csd csd;
++ u32 block_num;
++ u32 block_len;
++ u32 erase_unit;
++};
++
++struct mmc_info mmcinfo;
++
++struct mmc_request {
++ int index; /* Slot index - used for CS lines */
++ int cmd; /* Command to send */
++ u32 arg; /* Argument to send */
++ enum mmc_rsp_t rtype; /* Response type expected */
++
++ /* Data transfer (these may be modified at the low level) */
++ u16 nob; /* Number of blocks to transfer*/
++ u16 block_len; /* Block length */
++ u8 *buffer; /* Data buffer */
++ u32 cnt; /* Data length, for PIO */
++
++ /* Results */
++ u8 response[18]; /* Buffer to store response - CRC is optional */
++ enum mmc_result_t result;
++};
++
++char * mmc_result_to_string(int);
++int mmc_unpack_csd(struct mmc_request *request, struct mmc_csd *csd);
++int mmc_unpack_r1(struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state);
++int mmc_unpack_r6(struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state, int *rca);
++int mmc_unpack_scr(struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state, u32 *scr);
++int mmc_unpack_cid(struct mmc_request *request, struct mmc_cid *cid);
++int mmc_unpack_r3(struct mmc_request *request, struct mmc_response_r3 *r3);
++
++void mmc_send_cmd(struct mmc_request *request, int cmd, u32 arg,
++ u16 nob, u16 block_len, enum mmc_rsp_t rtype, u8 *buffer);
++u32 mmc_tran_speed(u8 ts);
++void jz_mmc_set_clock(int sd, u32 rate);
++
++static inline void mmc_simple_cmd(struct mmc_request *request, int cmd, u32 arg, enum mmc_rsp_t rtype)
++{
++ mmc_send_cmd( request, cmd, arg, 0, 0, rtype, 0);
++}
++
++#endif /* __MMC_JZMMC_H__ */
+diff --git a/include/configs/qi_lb60.h b/include/configs/qi_lb60.h
+index 7bff444..7b33be0 100644
+--- a/include/configs/qi_lb60.h
++++ b/include/configs/qi_lb60.h
+@@ -31,6 +31,15 @@
+ /*
+ * Miscellaneous configurable options
+ */
++#define CONFIG_JZ4740_MMC
++#define CONFIG_MMC 1
++#define CONFIG_FAT 1
++#define CONFIG_DOS_PARTITION 1
++#define CONFIG_CMD_MMC
++#define CONFIG_CMD_FAT
++#define CONFIG_CMD_EXT2
++
++
+ #define CONFIG_SYS_SDRAM_BASE 0x80000000 /* Cached addr */
+ #define CONFIG_SYS_INIT_SP_OFFSET 0x400000
+ #define CONFIG_SYS_LOAD_ADDR 0x80600000
+diff --git a/include/mmc.h b/include/mmc.h
+index a13e2bd..3c4761c 100644
+--- a/include/mmc.h
++++ b/include/mmc.h
+@@ -283,4 +283,44 @@ struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+ int mmc_legacy_init(int verbose);
+ #endif
+
++struct mmc_csd
++{
++ u8 csd_structure:2,
++ spec_vers:4,
++ rsvd1:2;
++ u8 taac;
++ u8 nsac;
++ u8 tran_speed;
++ u16 ccc:12,
++ read_bl_len:4;
++ u32 c_size:22;
++ u64 read_bl_partial:1,
++ write_blk_misalign:1,
++ read_blk_misalign:1,
++ dsr_imp:1,
++ rsvd2:2,
++ vdd_r_curr_min:3,
++ vdd_r_curr_max:3,
++ vdd_w_curr_min:3,
++ vdd_w_curr_max:3,
++ c_size_mult:3,
++ sector_size:5,
++ erase_grp_size:5,
++ wp_grp_size:5,
++ wp_grp_enable:1,
++ default_ecc:2,
++ r2w_factor:3,
++ write_bl_len:4,
++ write_bl_partial:1,
++ rsvd3:5;
++ u8 file_format_grp:1,
++ copy:1,
++ perm_write_protect:1,
++ tmp_write_protect:1,
++ file_format:2,
++ ecc:2;
++ u8 crc:7;
++ u8 one:1;
++};
++
+ #endif /* _MMC_H_ */
+--
+1.7.9.5
+
diff --git a/package/boot/uboot-xburst/patches/0004-add-more-boot-options-F1-F2-F3-F4-M-S.patch b/package/boot/uboot-xburst/patches/0004-add-more-boot-options-F1-F2-F3-F4-M-S.patch
new file mode 100644
index 0000000..73e0324
--- /dev/null
+++ b/package/boot/uboot-xburst/patches/0004-add-more-boot-options-F1-F2-F3-F4-M-S.patch
@@ -0,0 +1,200 @@
+From c52b6168979d03fc31205444c3278c537787472a Mon Sep 17 00:00:00 2001
+From: Xiangfu <xiangfu@openmobilefree.net>
+Date: Wed, 10 Oct 2012 18:39:55 +0800
+Subject: [PATCH 4/6] add more boot options(F1/F2/F3/F4/M/S)
+
+---
+ arch/mips/include/asm/global_data.h | 3 +++
+ arch/mips/lib/bootm.c | 17 ++++++++++++++++-
+ board/qi/qi_lb60/qi_lb60.c | 26 +++++++++++++++++++++++---
+ common/main.c | 21 +++++++++++++++++++--
+ include/configs/qi_lb60.h | 32 ++++++++++++++++++++++++++++++++
+ 5 files changed, 93 insertions(+), 6 deletions(-)
+
+diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h
+index 6e2cdc7..cd03d7e 100644
+--- a/arch/mips/include/asm/global_data.h
++++ b/arch/mips/include/asm/global_data.h
+@@ -59,6 +59,9 @@ typedef struct global_data {
+ unsigned long env_valid; /* Checksum of Environment valid? */
+ void **jt; /* jump table */
+ char env_buf[32]; /* buffer for getenv() before reloc. */
++#if defined(CONFIG_NANONOTE)
++ unsigned long boot_option;
++#endif
+ } gd_t;
+
+ #include <asm-generic/global_data_flags.h>
+diff --git a/arch/mips/lib/bootm.c b/arch/mips/lib/bootm.c
+index 608c1a7..e00416b 100644
+--- a/arch/mips/lib/bootm.c
++++ b/arch/mips/lib/bootm.c
+@@ -47,10 +47,25 @@ int do_bootm_linux(int flag, int argc, char * const argv[],
+ bootm_headers_t *images)
+ {
+ void (*theKernel) (int, char **, char **, int *);
+- char *commandline = getenv("bootargs");
++ char *commandline;
+ char env_buf[12];
+ char *cp;
+
++#if defined(CONFIG_NANONOTE)
++ if (gd->boot_option & BOOT_FROM_MEMCARD)
++ commandline = getenv ("bootargsfromsd");
++ else if (gd->boot_option & BOOT_WITH_F1)
++ commandline = getenv ("bootargsf1");
++ else if (gd->boot_option & BOOT_WITH_F2)
++ commandline = getenv ("bootargsf2");
++ else if (gd->boot_option & BOOT_WITH_F3)
++ commandline = getenv ("bootargsf3");
++ else if (gd->boot_option & BOOT_WITH_F4)
++ commandline = getenv ("bootargsf4");
++ else
++#endif
++ commandline = getenv ("bootargs");
++
+ if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
+ return 1;
+
+diff --git a/board/qi/qi_lb60/qi_lb60.c b/board/qi/qi_lb60/qi_lb60.c
+index a2ba648..d622219 100644
+--- a/board/qi/qi_lb60/qi_lb60.c
++++ b/board/qi/qi_lb60/qi_lb60.c
+@@ -15,7 +15,7 @@ DECLARE_GLOBAL_DATA_PTR;
+
+ static void gpio_init(void)
+ {
+- unsigned int i;
++ unsigned int i, j;
+
+ /* Initialize NAND Flash Pins */
+ __gpio_as_nand();
+@@ -42,14 +42,34 @@ static void gpio_init(void)
+
+ if (__gpio_get_pin(GPIO_KEYIN_BASE + 2) == 0){
+ printf("[S] pressed, enable UART0\n");
++ gd->boot_option |= BOOT_WITH_ENABLE_UART;
+ __gpio_as_uart0();
+ } else {
+ __gpio_as_input(GPIO_KEYIN_8);
+ __gpio_enable_pull(GPIO_KEYIN_8);
+ }
+
+- /* enable the TP4, TP5 as UART0 */
+- __gpio_jtag_to_uart0();
++ if (__gpio_get_pin(GPIO_KEYIN_BASE + 3) == 0) {
++ printf("[M] pressed, boot from memory card\n");
++ gd->boot_option |= BOOT_FROM_MEMCARD;
++ __gpio_jtag_to_uart0();
++ }
++
++ for (j = 0; j < 4; j++) {
++ for (i = 0; i < 4; i++)
++ __gpio_set_pin(GPIO_KEYOUT_BASE + i);
++
++ __gpio_clear_pin(GPIO_KEYOUT_BASE + j);
++
++ if (__gpio_get_pin(GPIO_KEYIN_BASE) == 0) {
++ printf("[F%d] pressed", (j + 1));
++ gd->boot_option |= (1 << (j + 2));
++ /* BOOT_WITH_F1 (1 << 2) */
++ /* BOOT_WITH_F2 (1 << 3) */
++ /* BOOT_WITH_F3 (1 << 4) */
++ /* BOOT_WITH_F4 (1 << 5) */
++ }
++ }
+
+ __gpio_as_output(GPIO_AUDIO_POP);
+ __gpio_set_pin(GPIO_AUDIO_POP);
+diff --git a/common/main.c b/common/main.c
+index 9507cec..dbfb7ca 100644
+--- a/common/main.c
++++ b/common/main.c
+@@ -355,7 +355,11 @@ void main_loop (void)
+ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
+ s = getenv ("bootdelay");
+ bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
+-
++#if defined(CONFIG_NANONOTE)
++ DECLARE_GLOBAL_DATA_PTR;
++ if (gd->boot_option & BOOT_WITH_ENABLE_UART)
++ bootdelay = 3;
++# endif
+ debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
+
+ #if defined(CONFIG_MENU_SHOW)
+@@ -379,7 +383,20 @@ void main_loop (void)
+ }
+ else
+ #endif /* CONFIG_BOOTCOUNT_LIMIT */
+- s = getenv ("bootcmd");
++#if defined(CONFIG_NANONOTE)
++ if (gd->boot_option & BOOT_FROM_MEMCARD)
++ s = getenv ("bootcmdfromsd");
++ else if (gd->boot_option & BOOT_WITH_F1)
++ s = getenv ("bootcmdf1");
++ else if (gd->boot_option & BOOT_WITH_F2)
++ s = getenv ("bootcmdf2");
++ else if (gd->boot_option & BOOT_WITH_F3)
++ s = getenv ("bootcmdf3");
++ else if (gd->boot_option & BOOT_WITH_F4)
++ s = getenv ("bootcmdf4");
++ else
++#endif
++ s = getenv ("bootcmd");
+
+ debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
+
+diff --git a/include/configs/qi_lb60.h b/include/configs/qi_lb60.h
+index 7b33be0..52b370c 100644
+--- a/include/configs/qi_lb60.h
++++ b/include/configs/qi_lb60.h
+@@ -31,6 +31,7 @@
+ /*
+ * Miscellaneous configurable options
+ */
++#define CONFIG_NANONOTE
+ #define CONFIG_JZ4740_MMC
+ #define CONFIG_MMC 1
+ #define CONFIG_FAT 1
+@@ -39,6 +40,37 @@
+ #define CONFIG_CMD_FAT
+ #define CONFIG_CMD_EXT2
+
++#define CONFIG_CMD_UBIFS
++#define CONFIG_CMD_UBI
++#define CONFIG_MTD_PARTITIONS
++#define CONFIG_MTD_DEVICE
++#define CONFIG_CMD_MTDPARTS
++#define CONFIG_CMD_UBI
++#define CONFIG_CMD_UBIFS
++#define CONFIG_LZO
++#define CONFIG_RBTREE
++
++#define MTDIDS_DEFAULT "nand0=jz4740-nand"
++#define MTDPARTS_DEFAULT "mtdparts=jz4740-nand:4M@0(uboot)ro,4M@4M(kernel)ro,512M@8M(rootfs)ro,-(data)ro"
++
++#define BOOT_FROM_MEMCARD 1
++#define BOOT_WITH_ENABLE_UART (1 << 1) /* Vaule for global_data.h gd->boot_option */
++#define BOOT_WITH_F1 (1 << 2)
++#define BOOT_WITH_F2 (1 << 3)
++#define BOOT_WITH_F3 (1 << 4)
++#define BOOT_WITH_F4 (1 << 5)
++
++#define CONFIG_EXTRA_ENV_SETTINGS \
++ "bootcmdfromsd=mmc init; ext2load mmc 0 0x80600000 /boot/uImage; bootm;\0" \
++ "bootargsfromsd=mem=32M console=tty0 console=ttyS0,57600n8 rootfstype=ext2 root=/dev/mmcblk0p1 rw rootwait\0" \
++ "bootcmdf1=mmc init; ext2load mmc 0:1 0x80600000 /boot/uImage; bootm;\0" \
++ "bootargsf1=mem=32M console=tty0 console=ttyS0,57600n8 rootfstype=ext2 root=/dev/mmcblk0p1 rw rootwait\0" \
++ "bootcmdf2=mmc init; ext2load mmc 0:2 0x80600000 /boot/uImage; bootm;\0" \
++ "bootargsf2=mem=32M console=tty0 console=ttyS0,57600n8 rootfstype=ext2 root=/dev/mmcblk0p2 rw rootwait\0" \
++ "bootcmdf3=mmc init; ext2load mmc 0:3 0x80600000 /boot/uImage; bootm;\0" \
++ "bootargsf3=mem=32M console=tty0 console=ttyS0,57600n8 rootfstype=ext2 root=/dev/mmcblk0p3 rw rootwait\0" \
++ "bootcmdf4=mtdparts default;ubi part rootfs;ubifsmount rootfs;ubifsload 0x80600000 /boot/uImage; bootm;\0" \
++ "bootargsf4=mem=32M console=tty0 console=ttyS0,57600n8 ubi.mtd=2 rootfstype=ubifs root=ubi0:rootfs rw rootwait"
+
+ #define CONFIG_SYS_SDRAM_BASE 0x80000000 /* Cached addr */
+ #define CONFIG_SYS_INIT_SP_OFFSET 0x400000
+--
+1.7.9.5
+
diff --git a/package/boot/uboot-xburst/patches/0005-add-nanonote-lcd-support.patch b/package/boot/uboot-xburst/patches/0005-add-nanonote-lcd-support.patch
new file mode 100644
index 0000000..2c550f7
--- /dev/null
+++ b/package/boot/uboot-xburst/patches/0005-add-nanonote-lcd-support.patch
@@ -0,0 +1,847 @@
+From ca8c5216cfd3ad3fda9867ed2d157ae5a209834b Mon Sep 17 00:00:00 2001
+From: Xiangfu <xiangfu@openmobilefree.net>
+Date: Wed, 10 Oct 2012 22:05:27 +0800
+Subject: [PATCH 5/6] add nanonote lcd support
+
+---
+ arch/mips/include/asm/global_data.h | 1 +
+ arch/mips/include/asm/jz4740.h | 90 ++++++++
+ arch/mips/lib/board.c | 6 +
+ common/lcd.c | 9 +-
+ drivers/video/Makefile | 1 +
+ drivers/video/nanonote_gpm940b0.c | 400 +++++++++++++++++++++++++++++++++++
+ drivers/video/nanonote_gpm940b0.h | 135 ++++++++++++
+ include/configs/qi_lb60.h | 7 +
+ include/lcd.h | 52 ++++-
+ 9 files changed, 697 insertions(+), 4 deletions(-)
+ create mode 100644 drivers/video/nanonote_gpm940b0.c
+ create mode 100644 drivers/video/nanonote_gpm940b0.h
+
+diff --git a/arch/mips/include/asm/global_data.h b/arch/mips/include/asm/global_data.h
+index cd03d7e..7cec2de 100644
+--- a/arch/mips/include/asm/global_data.h
++++ b/arch/mips/include/asm/global_data.h
+@@ -44,6 +44,7 @@ typedef struct global_data {
+ unsigned long per_clk; /* Peripheral bus clock */
+ unsigned long mem_clk; /* Memory bus clock */
+ unsigned long dev_clk; /* Device clock */
++ unsigned long fb_base; /* base address of framebuffer */
+ /* "static data" needed by most of timer.c */
+ unsigned long tbl;
+ unsigned long lastinc;
+diff --git a/arch/mips/include/asm/jz4740.h b/arch/mips/include/asm/jz4740.h
+index 68287fb..13724a2 100644
+--- a/arch/mips/include/asm/jz4740.h
++++ b/arch/mips/include/asm/jz4740.h
+@@ -1312,5 +1312,95 @@ do { \
+ while (REG_MSC_STAT & MSC_STAT_IS_RESETTING); \
+ } while (0)
+
++/*************************************************************************
++ * LCD (LCD Controller)
++ *************************************************************************/
++#define REG32(addr) *((volatile u32 *)(addr))
++
++#define CPM_BASE 0xB0000000
++#define CPM_CPCCR (CPM_BASE+0x00)
++#define REG_CPM_CPCCR REG32(CPM_CPCCR)
++
++#define LCD_BASE 0xB3050000
++#define LCD_CFG (LCD_BASE + 0x00) /* LCD Configure Register */
++#define LCD_VSYNC (LCD_BASE + 0x04) /* Vertical Synchronize Register */
++#define LCD_HSYNC (LCD_BASE + 0x08) /* Horizontal Synchronize Register */
++#define LCD_VAT (LCD_BASE + 0x0c) /* Virtual Area Setting Register */
++#define LCD_DAH (LCD_BASE + 0x10) /* Display Area Horizontal Start/End Point */
++#define LCD_DAV (LCD_BASE + 0x14) /* Display Area Vertical Start/End Point */
++#define LCD_PS (LCD_BASE + 0x18) /* PS Signal Setting */
++#define LCD_CLS (LCD_BASE + 0x1c) /* CLS Signal Setting */
++#define LCD_SPL (LCD_BASE + 0x20) /* SPL Signal Setting */
++#define LCD_REV (LCD_BASE + 0x24) /* REV Signal Setting */
++#define LCD_CTRL (LCD_BASE + 0x30) /* LCD Control Register */
++#define LCD_STATE (LCD_BASE + 0x34) /* LCD Status Register */
++#define LCD_IID (LCD_BASE + 0x38) /* Interrupt ID Register */
++#define LCD_DA0 (LCD_BASE + 0x40) /* Descriptor Address Register 0 */
++#define LCD_SA0 (LCD_BASE + 0x44) /* Source Address Register 0 */
++#define LCD_FID0 (LCD_BASE + 0x48) /* Frame ID Register 0 */
++#define LCD_CMD0 (LCD_BASE + 0x4c) /* DMA Command Register 0 */
++#define LCD_DA1 (LCD_BASE + 0x50) /* Descriptor Address Register 1 */
++#define LCD_SA1 (LCD_BASE + 0x54) /* Source Address Register 1 */
++#define LCD_FID1 (LCD_BASE + 0x58) /* Frame ID Register 1 */
++#define LCD_CMD1 (LCD_BASE + 0x5c) /* DMA Command Register 1 */
++
++#define REG_LCD_CFG REG32(LCD_CFG)
++#define REG_LCD_VSYNC REG32(LCD_VSYNC)
++#define REG_LCD_HSYNC REG32(LCD_HSYNC)
++#define REG_LCD_VAT REG32(LCD_VAT)
++#define REG_LCD_DAH REG32(LCD_DAH)
++#define REG_LCD_DAV REG32(LCD_DAV)
++#define REG_LCD_PS REG32(LCD_PS)
++#define REG_LCD_CLS REG32(LCD_CLS)
++#define REG_LCD_SPL REG32(LCD_SPL)
++#define REG_LCD_REV REG32(LCD_REV)
++#define REG_LCD_CTRL REG32(LCD_CTRL)
++#define REG_LCD_STATE REG32(LCD_STATE)
++#define REG_LCD_IID REG32(LCD_IID)
++#define REG_LCD_DA0 REG32(LCD_DA0)
++#define REG_LCD_SA0 REG32(LCD_SA0)
++#define REG_LCD_FID0 REG32(LCD_FID0)
++#define REG_LCD_CMD0 REG32(LCD_CMD0)
++#define REG_LCD_DA1 REG32(LCD_DA1)
++#define REG_LCD_SA1 REG32(LCD_SA1)
++#define REG_LCD_FID1 REG32(LCD_FID1)
++#define REG_LCD_CMD1 REG32(LCD_CMD1)
++
++#define LCD_CTRL_BPP_BIT 0 /* Bits Per Pixel */
++#define LCD_CTRL_BPP_MASK (0x07 << LCD_CTRL_BPP_BIT)
++ #define LCD_CTRL_BPP_1 (0 << LCD_CTRL_BPP_BIT) /* 1 bpp */
++ #define LCD_CTRL_BPP_2 (1 << LCD_CTRL_BPP_BIT) /* 2 bpp */
++ #define LCD_CTRL_BPP_4 (2 << LCD_CTRL_BPP_BIT) /* 4 bpp */
++ #define LCD_CTRL_BPP_8 (3 << LCD_CTRL_BPP_BIT) /* 8 bpp */
++ #define LCD_CTRL_BPP_16 (4 << LCD_CTRL_BPP_BIT) /* 15/16 bpp */
++ #define LCD_CTRL_BPP_18_24 (5 << LCD_CTRL_BPP_BIT) /* 18/24/32 bpp */
++
++#define LCD_CTRL_BST_BIT 28 /* Burst Length Selection */
++#define LCD_CTRL_BST_MASK (0x03 << LCD_CTRL_BST_BIT)
++ #define LCD_CTRL_BST_4 (0 << LCD_CTRL_BST_BIT) /* 4-word */
++ #define LCD_CTRL_BST_8 (1 << LCD_CTRL_BST_BIT) /* 8-word */
++ #define LCD_CTRL_BST_16 (2 << LCD_CTRL_BST_BIT) /* 16-word */
++#define LCD_CTRL_RGB565 (0 << 27) /* RGB565 mode */
++#define LCD_CTRL_RGB555 (1 << 27) /* RGB555 mode */
++#define LCD_CTRL_OFUP (1 << 26) /* Output FIFO underrun protection enable */
++#define LCD_CTRL_FRC_BIT 24 /* STN FRC Algorithm Selection */
++#define LCD_CTRL_FRC_MASK (0x03 << LCD_CTRL_FRC_BIT)
++ #define LCD_CTRL_FRC_16 (0 << LCD_CTRL_FRC_BIT) /* 16 grayscale */
++ #define LCD_CTRL_FRC_4 (1 << LCD_CTRL_FRC_BIT) /* 4 grayscale */
++ #define LCD_CTRL_FRC_2 (2 << LCD_CTRL_FRC_BIT) /* 2 grayscale */
++
++#define CPM_LPCDR (CPM_BASE+0x64)
++#define CPM_CLKGR (CPM_BASE+0x20)
++#define REG_CPM_LPCDR REG32(CPM_LPCDR)
++#define REG_CPM_CLKGR REG32(CPM_CLKGR)
++
++#define __cpm_start_tcu() (REG_CPM_CLKGR &= ~CPM_CLKGR_TCU)
++#define __cpm_stop_lcd() (REG_CPM_CLKGR |= CPM_CLKGR_LCD)
++#define __cpm_set_pixdiv(v) \
++ (REG_CPM_LPCDR = (REG_CPM_LPCDR & ~CPM_LPCDR_PIXDIV_MASK) | ((v) << (CPM_LPCDR_PIXDIV_BIT)))
++#define __cpm_set_ldiv(v) \
++ (REG_CPM_CPCCR = (REG_CPM_CPCCR & ~CPM_CPCCR_LDIV_MASK) | ((v) << (CPM_CPCCR_LDIV_BIT)))
++#define __cpm_start_lcd() (REG_CPM_CLKGR &= ~CPM_CLKGR_LCD)
++
+ #endif /* !__ASSEMBLY__ */
+ #endif /* __JZ4740_H__ */
+diff --git a/arch/mips/lib/board.c b/arch/mips/lib/board.c
+index b14b33e..c2e64d9 100644
+--- a/arch/mips/lib/board.c
++++ b/arch/mips/lib/board.c
+@@ -172,6 +172,12 @@ void board_init_f(ulong bootflag)
+ addr &= ~(4096 - 1);
+ debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
+
++#ifdef CONFIG_LCD
++ /* reserve memory for LCD display (always full pages) */
++ addr = lcd_setmem (addr);
++ gd->fb_base = addr;
++#endif /* CONFIG_LCD */
++
+ /* Reserve memory for U-Boot code, data & bss
+ * round down to next 16 kB limit
+ */
+diff --git a/common/lcd.c b/common/lcd.c
+index b6be800..af1281a 100644
+--- a/common/lcd.c
++++ b/common/lcd.c
+@@ -263,6 +263,13 @@ static void lcd_drawchars(ushort x, ushort y, uchar *str, int count)
+ lcd_color_fg : lcd_color_bg;
+ bits <<= 1;
+ }
++#elif LCD_BPP == LCD_COLOR32
++ uint *m = (uint *)d;
++ for (c=0; c<32; ++c) {
++ *m++ = (bits & 0x80) ?
++ lcd_color_fg : lcd_color_bg;
++ bits <<= 1;
++ }
+ #endif
+ }
+ #if LCD_BPP == LCD_MONOCHROME
+@@ -509,7 +516,7 @@ static inline ushort *configuration_get_cmap(void)
+ return (ushort *)&(cp->lcd_cmap[255 * sizeof(ushort)]);
+ #elif defined(CONFIG_ATMEL_LCD)
+ return (ushort *)(panel_info.mmio + ATMEL_LCDC_LUT(0));
+-#elif !defined(CONFIG_ATMEL_HLCD) && !defined(CONFIG_EXYNOS_FB)
++#elif !defined(CONFIG_ATMEL_HLCD) && !defined(CONFIG_EXYNOS_FB) && !defined(CONFIG_VIDEO_GPM940B0)
+ return panel_info.cmap;
+ #else
+ #if defined(CONFIG_LCD_LOGO)
+diff --git a/drivers/video/Makefile b/drivers/video/Makefile
+index ebb6da8..03625bc 100644
+--- a/drivers/video/Makefile
++++ b/drivers/video/Makefile
+@@ -50,6 +50,7 @@ COBJS-$(CONFIG_VIDEO_SED13806) += sed13806.o
+ COBJS-$(CONFIG_VIDEO_SM501) += sm501.o
+ COBJS-$(CONFIG_VIDEO_SMI_LYNXEM) += smiLynxEM.o videomodes.o
+ COBJS-$(CONFIG_VIDEO_VCXK) += bus_vcxk.o
++COBJS-$(CONFIG_VIDEO_GPM940B0) += nanonote_gpm940b0.o
+
+ COBJS := $(sort $(COBJS-y))
+ SRCS := $(COBJS:.o=.c)
+diff --git a/drivers/video/nanonote_gpm940b0.c b/drivers/video/nanonote_gpm940b0.c
+new file mode 100644
+index 0000000..11efb72
+--- /dev/null
++++ b/drivers/video/nanonote_gpm940b0.c
+@@ -0,0 +1,400 @@
++/*
++ * JzRISC lcd controller
++ *
++ * Xiangfu Liu <xiangfu@sharism.cc>
++ *
++ * 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 program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include <config.h>
++#include <common.h>
++#include <lcd.h>
++
++#include <asm/io.h> /* virt_to_phys() */
++#include <asm/jz4740.h>
++
++#include "nanonote_gpm940b0.h"
++
++#define align2(n) (n)=((((n)+1)>>1)<<1)
++#define align4(n) (n)=((((n)+3)>>2)<<2)
++#define align8(n) (n)=((((n)+7)>>3)<<3)
++
++struct jzfb_info {
++ unsigned int cfg; /* panel mode and pin usage etc. */
++ unsigned int w;
++ unsigned int h;
++ unsigned int bpp; /* bit per pixel */
++ unsigned int fclk; /* frame clk */
++ unsigned int hsw; /* hsync width, in pclk */
++ unsigned int vsw; /* vsync width, in line count */
++ unsigned int elw; /* end of line, in pclk */
++ unsigned int blw; /* begin of line, in pclk */
++ unsigned int efw; /* end of frame, in line count */
++ unsigned int bfw; /* begin of frame, in line count */
++};
++
++static struct jzfb_info jzfb = {
++ MODE_8BIT_SERIAL_TFT | PCLK_N | HSYNC_N | VSYNC_N,
++ 320, 240, 32, 70, 1, 1, 273, 140, 1, 20
++};
++
++vidinfo_t panel_info = {
++ 320, 240, LCD_BPP,
++};
++
++void *lcd_base;
++void *lcd_console_address;
++int lcd_line_length;
++int lcd_color_fg;
++int lcd_color_bg;
++short console_col;
++short console_row;
++
++static int jz_lcd_init_mem(void *lcdbase, vidinfo_t *vid)
++{
++ u_long palette_mem_size;
++ struct jz_fb_info *fbi = &vid->jz_fb;
++ int fb_size = vid->vl_row * (vid->vl_col * NBITS (vid->vl_bpix)) / 8;
++
++ fbi->screen = (u_long)lcdbase;
++ fbi->palette_size = 256;
++ palette_mem_size = fbi->palette_size * sizeof(u16);
++
++ debug("jz_lcd.c palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
++ /* locate palette and descs at end of page following fb */
++ fbi->palette = (u_long)lcdbase + fb_size + PAGE_SIZE - palette_mem_size;
++
++ return 0;
++}
++
++static void jz_lcd_desc_init(vidinfo_t *vid)
++{
++ struct jz_fb_info * fbi;
++ fbi = &vid->jz_fb;
++ fbi->dmadesc_fblow = (struct jz_fb_dma_descriptor *)((unsigned int)fbi->palette - 3*16);
++ fbi->dmadesc_fbhigh = (struct jz_fb_dma_descriptor *)((unsigned int)fbi->palette - 2*16);
++ fbi->dmadesc_palette = (struct jz_fb_dma_descriptor *)((unsigned int)fbi->palette - 1*16);
++
++ #define BYTES_PER_PANEL (vid->vl_col * vid->vl_row * NBITS(vid->vl_bpix) / 8)
++
++ /* populate descriptors */
++ fbi->dmadesc_fblow->fdadr = virt_to_phys(fbi->dmadesc_fblow);
++ fbi->dmadesc_fblow->fsadr = virt_to_phys((void *)(fbi->screen + BYTES_PER_PANEL));
++ fbi->dmadesc_fblow->fidr = 0;
++ fbi->dmadesc_fblow->ldcmd = BYTES_PER_PANEL / 4 ;
++
++ fbi->fdadr1 = virt_to_phys(fbi->dmadesc_fblow); /* only used in dual-panel mode */
++
++ fbi->dmadesc_fbhigh->fsadr = virt_to_phys((void *)fbi->screen);
++ fbi->dmadesc_fbhigh->fidr = 0;
++ fbi->dmadesc_fbhigh->ldcmd = BYTES_PER_PANEL / 4; /* length in word */
++
++ fbi->dmadesc_palette->fsadr = virt_to_phys((void *)fbi->palette);
++ fbi->dmadesc_palette->fidr = 0;
++ fbi->dmadesc_palette->ldcmd = (fbi->palette_size * 2)/4 | (1<<28);
++
++ if(NBITS(vid->vl_bpix) < 12) {
++ /* assume any mode with <12 bpp is palette driven */
++ fbi->dmadesc_palette->fdadr = virt_to_phys(fbi->dmadesc_fbhigh);
++ fbi->dmadesc_fbhigh->fdadr = virt_to_phys(fbi->dmadesc_palette);
++ /* flips back and forth between pal and fbhigh */
++ fbi->fdadr0 = virt_to_phys(fbi->dmadesc_palette);
++ } else {
++ /* palette shouldn't be loaded in true-color mode */
++ fbi->dmadesc_fbhigh->fdadr = virt_to_phys((void *)fbi->dmadesc_fbhigh);
++ fbi->fdadr0 = virt_to_phys(fbi->dmadesc_fbhigh); /* no pal just fbhigh */
++ }
++}
++
++static int jz_lcd_hw_init(vidinfo_t *vid)
++{
++ struct jz_fb_info *fbi = &vid->jz_fb;
++ unsigned int val = 0;
++ unsigned int pclk;
++ unsigned int stnH;
++ int pll_div;
++
++ /* Setting Control register */
++ switch (jzfb.bpp) {
++ case 1:
++ val |= LCD_CTRL_BPP_1;
++ break;
++ case 2:
++ val |= LCD_CTRL_BPP_2;
++ break;
++ case 4:
++ val |= LCD_CTRL_BPP_4;
++ break;
++ case 8:
++ val |= LCD_CTRL_BPP_8;
++ break;
++ case 15:
++ val |= LCD_CTRL_RGB555;
++ case 16:
++ val |= LCD_CTRL_BPP_16;
++ break;
++ case 17 ... 32:
++ val |= LCD_CTRL_BPP_18_24; /* target is 4bytes/pixel */
++ break;
++
++ default:
++ printf("jz_lcd.c The BPP %d is not supported\n", jzfb.bpp);
++ val |= LCD_CTRL_BPP_16;
++ break;
++ }
++
++ switch (jzfb.cfg & MODE_MASK) {
++ case MODE_STN_MONO_DUAL:
++ case MODE_STN_COLOR_DUAL:
++ case MODE_STN_MONO_SINGLE:
++ case MODE_STN_COLOR_SINGLE:
++ switch (jzfb.bpp) {
++ case 1:
++ /* val |= LCD_CTRL_PEDN; */
++ case 2:
++ val |= LCD_CTRL_FRC_2;
++ break;
++ case 4:
++ val |= LCD_CTRL_FRC_4;
++ break;
++ case 8:
++ default:
++ val |= LCD_CTRL_FRC_16;
++ break;
++ }
++ break;
++ }
++
++ val |= LCD_CTRL_BST_16; /* Burst Length is 16WORD=64Byte */
++ val |= LCD_CTRL_OFUP; /* OutFIFO underrun protect */
++
++ switch (jzfb.cfg & MODE_MASK) {
++ case MODE_STN_MONO_DUAL:
++ case MODE_STN_COLOR_DUAL:
++ case MODE_STN_MONO_SINGLE:
++ case MODE_STN_COLOR_SINGLE:
++ switch (jzfb.cfg & STN_DAT_PINMASK) {
++ case STN_DAT_PIN1:
++ /* Do not adjust the hori-param value. */
++ break;
++ case STN_DAT_PIN2:
++ align2(jzfb.hsw);
++ align2(jzfb.elw);
++ align2(jzfb.blw);
++ break;
++ case STN_DAT_PIN4:
++ align4(jzfb.hsw);
++ align4(jzfb.elw);
++ align4(jzfb.blw);
++ break;
++ case STN_DAT_PIN8:
++ align8(jzfb.hsw);
++ align8(jzfb.elw);
++ align8(jzfb.blw);
++ break;
++ }
++ break;
++ }
++
++ REG_LCD_CTRL = val;
++
++ switch (jzfb.cfg & MODE_MASK) {
++ case MODE_STN_MONO_DUAL:
++ case MODE_STN_COLOR_DUAL:
++ case MODE_STN_MONO_SINGLE:
++ case MODE_STN_COLOR_SINGLE:
++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL))
++ stnH = jzfb.h >> 1;
++ else
++ stnH = jzfb.h;
++
++ REG_LCD_VSYNC = (0 << 16) | jzfb.vsw;
++ REG_LCD_HSYNC = ((jzfb.blw+jzfb.w) << 16) | (jzfb.blw+jzfb.w+jzfb.hsw);
++
++ /* Screen setting */
++ REG_LCD_VAT = ((jzfb.blw + jzfb.w + jzfb.hsw + jzfb.elw) << 16) | (stnH + jzfb.vsw + jzfb.bfw + jzfb.efw);
++ REG_LCD_DAH = (jzfb.blw << 16) | (jzfb.blw + jzfb.w);
++ REG_LCD_DAV = (0 << 16) | (stnH);
++
++ /* AC BIAs signal */
++ REG_LCD_PS = (0 << 16) | (stnH+jzfb.vsw+jzfb.efw+jzfb.bfw);
++
++ break;
++
++ case MODE_TFT_GEN:
++ case MODE_TFT_SHARP:
++ case MODE_TFT_CASIO:
++ case MODE_TFT_SAMSUNG:
++ case MODE_8BIT_SERIAL_TFT:
++ case MODE_TFT_18BIT:
++ REG_LCD_VSYNC = (0 << 16) | jzfb.vsw;
++ REG_LCD_HSYNC = (0 << 16) | jzfb.hsw;
++ REG_LCD_DAV =((jzfb.vsw+jzfb.bfw) << 16) | (jzfb.vsw +jzfb.bfw+jzfb.h);
++ REG_LCD_DAH = ((jzfb.hsw + jzfb.blw) << 16) | (jzfb.hsw + jzfb.blw + jzfb.w );
++ REG_LCD_VAT = (((jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw)) << 16) \
++ | (jzfb.vsw + jzfb.bfw + jzfb.h + jzfb.efw);
++ break;
++ }
++
++ switch (jzfb.cfg & MODE_MASK) {
++ case MODE_TFT_SAMSUNG:
++ {
++ unsigned int total, tp_s, tp_e, ckv_s, ckv_e;
++ unsigned int rev_s, rev_e, inv_s, inv_e;
++
++ pclk = val * (jzfb.w + jzfb.hsw + jzfb.elw + jzfb.blw) *
++ (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */
++
++ total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw;
++ tp_s = jzfb.blw + jzfb.w + 1;
++ tp_e = tp_s + 1;
++ ckv_s = tp_s - pclk/(1000000000/4100);
++ ckv_e = tp_s + total;
++ rev_s = tp_s - 11; /* -11.5 clk */
++ rev_e = rev_s + total;
++ inv_s = tp_s;
++ inv_e = inv_s + total;
++ REG_LCD_CLS = (tp_s << 16) | tp_e;
++ REG_LCD_PS = (ckv_s << 16) | ckv_e;
++ REG_LCD_SPL = (rev_s << 16) | rev_e;
++ REG_LCD_REV = (inv_s << 16) | inv_e;
++ jzfb.cfg |= STFT_REVHI | STFT_SPLHI;
++ break;
++ }
++ case MODE_TFT_SHARP:
++ {
++ unsigned int total, cls_s, cls_e, ps_s, ps_e;
++ unsigned int spl_s, spl_e, rev_s, rev_e;
++ total = jzfb.blw + jzfb.w + jzfb.elw + jzfb.hsw;
++ spl_s = 1;
++ spl_e = spl_s + 1;
++ cls_s = 0;
++ cls_e = total - 60; /* > 4us (pclk = 80ns) */
++ ps_s = cls_s;
++ ps_e = cls_e;
++ rev_s = total - 40; /* > 3us (pclk = 80ns) */
++ rev_e = rev_s + total;
++ jzfb.cfg |= STFT_PSHI;
++ REG_LCD_SPL = (spl_s << 16) | spl_e;
++ REG_LCD_CLS = (cls_s << 16) | cls_e;
++ REG_LCD_PS = (ps_s << 16) | ps_e;
++ REG_LCD_REV = (rev_s << 16) | rev_e;
++ break;
++ }
++ case MODE_TFT_CASIO:
++ break;
++ }
++
++ /* Configure the LCD panel */
++ REG_LCD_CFG = jzfb.cfg;
++
++ /* Timing setting */
++ __cpm_stop_lcd();
++
++ val = jzfb.fclk; /* frame clk */
++ if ( (jzfb.cfg & MODE_MASK) != MODE_8BIT_SERIAL_TFT) {
++ pclk = val * (jzfb.w + jzfb.hsw + jzfb.elw + jzfb.blw) *
++ (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */
++ } else {
++ /* serial mode: Hsync period = 3*Width_Pixel */
++ pclk = val * (jzfb.w*3 + jzfb.hsw + jzfb.elw + jzfb.blw) *
++ (jzfb.h + jzfb.vsw + jzfb.efw + jzfb.bfw); /* Pixclk */
++ }
++
++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL))
++ pclk = (pclk * 3);
++
++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_SINGLE) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_SINGLE) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
++ pclk = pclk >> ((jzfb.cfg & STN_DAT_PINMASK) >> 4);
++
++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
++ pclk >>= 1;
++
++ pll_div = (REG_CPM_CPCCR & CPM_CPCCR_PCS); /* clock source,0:pllout/2 1: pllout */
++ pll_div = pll_div ? 1 : 2;
++ val = (__cpm_get_pllout() / pll_div) / pclk;
++ val--;
++ if (val > 0x1ff) {
++ printf("CPM_LPCDR too large, set it to 0x1ff\n");
++ val = 0x1ff;
++ }
++ __cpm_set_pixdiv(val);
++
++ val = pclk * 3 ; /* LCDClock > 2.5*Pixclock */
++ if (val > 150000000) {
++ printf("Warning: LCDClock=%d\n, LCDClock must less or equal to 150MHz.\n", val);
++ printf("Change LCDClock to 150MHz\n");
++ val = 150000000;
++ }
++ val = (__cpm_get_pllout() / pll_div) / val;
++ val--;
++ if (val > 0x1f) {
++ printf("CPM_CPCCR.LDIV too large, set it to 0x1f\n");
++ val = 0x1f;
++ }
++ __cpm_set_ldiv( val );
++ REG_CPM_CPCCR |= CPM_CPCCR_CE ; /* update divide */
++
++ __cpm_start_lcd();
++ udelay(1000);
++
++ REG_LCD_DA0 = fbi->fdadr0; /* frame descripter*/
++
++ if (((jzfb.cfg & MODE_MASK) == MODE_STN_COLOR_DUAL) ||
++ ((jzfb.cfg & MODE_MASK) == MODE_STN_MONO_DUAL))
++ REG_LCD_DA1 = fbi->fdadr1; /* frame descripter*/
++
++ return 0;
++}
++
++void lcd_ctrl_init (void *lcdbase)
++{
++ __lcd_display_pin_init();
++ __lcd_display_on() ;
++
++ jz_lcd_init_mem(lcdbase, &panel_info);
++ jz_lcd_desc_init(&panel_info);
++ jz_lcd_hw_init(&panel_info);
++
++}
++
++/*
++ * Before enabled lcd controller, lcd registers should be configured correctly.
++ */
++void lcd_enable (void)
++{
++ REG_LCD_CTRL &= ~(1<<4); /* LCDCTRL.DIS */
++ REG_LCD_CTRL |= 1<<3; /* LCDCTRL.ENA*/
++}
++
++void lcd_disable (void)
++{
++ REG_LCD_CTRL |= (1<<4);
++}
++
++void lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue)
++{
++}
++
++void lcd_initcolregs (void)
++{
++}
+diff --git a/drivers/video/nanonote_gpm940b0.h b/drivers/video/nanonote_gpm940b0.h
+new file mode 100644
+index 0000000..efe491e
+--- /dev/null
++++ b/drivers/video/nanonote_gpm940b0.h
+@@ -0,0 +1,135 @@
++/*
++ * JzRISC lcd controller
++ *
++ * Xiangfu Liu <xiangfu@sharism.cc>
++ *
++ * 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 program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#ifndef __QI_LB60_GPM940B0_H__
++#define __QI_LB60_GPM940B0_H__
++
++struct lcd_desc{
++ unsigned int next_desc; /* LCDDAx */
++ unsigned int databuf; /* LCDSAx */
++ unsigned int frame_id; /* LCDFIDx */
++ unsigned int cmd; /* LCDCMDx */
++};
++
++#define MODE_MASK 0x0f
++#define MODE_TFT_GEN 0x00
++#define MODE_TFT_SHARP 0x01
++#define MODE_TFT_CASIO 0x02
++#define MODE_TFT_SAMSUNG 0x03
++#define MODE_CCIR656_NONINT 0x04
++#define MODE_CCIR656_INT 0x05
++#define MODE_STN_COLOR_SINGLE 0x08
++#define MODE_STN_MONO_SINGLE 0x09
++#define MODE_STN_COLOR_DUAL 0x0a
++#define MODE_STN_MONO_DUAL 0x0b
++#define MODE_8BIT_SERIAL_TFT 0x0c
++
++#define MODE_TFT_18BIT (1<<7)
++
++#define STN_DAT_PIN1 (0x00 << 4)
++#define STN_DAT_PIN2 (0x01 << 4)
++#define STN_DAT_PIN4 (0x02 << 4)
++#define STN_DAT_PIN8 (0x03 << 4)
++#define STN_DAT_PINMASK STN_DAT_PIN8
++
++#define STFT_PSHI (1 << 15)
++#define STFT_CLSHI (1 << 14)
++#define STFT_SPLHI (1 << 13)
++#define STFT_REVHI (1 << 12)
++
++#define SYNC_MASTER (0 << 16)
++#define SYNC_SLAVE (1 << 16)
++
++#define DE_P (0 << 9)
++#define DE_N (1 << 9)
++
++#define PCLK_P (0 << 10)
++#define PCLK_N (1 << 10)
++
++#define HSYNC_P (0 << 11)
++#define HSYNC_N (1 << 11)
++
++#define VSYNC_P (0 << 8)
++#define VSYNC_N (1 << 8)
++
++#define DATA_NORMAL (0 << 17)
++#define DATA_INVERSE (1 << 17)
++
++
++/* Jz LCDFB supported I/O controls. */
++#define FBIOSETBACKLIGHT 0x4688
++#define FBIODISPON 0x4689
++#define FBIODISPOFF 0x468a
++#define FBIORESET 0x468b
++#define FBIOPRINT_REG 0x468c
++
++/*
++ * LCD panel specific definition
++ */
++#define MODE (0xc9) /* 8bit serial RGB */
++
++#define __spi_write_reg1(reg, val) \
++do { \
++ unsigned char no; \
++ unsigned short value; \
++ unsigned char a=reg; \
++ unsigned char b=val; \
++ __gpio_set_pin(SPEN); \
++ __gpio_set_pin(SPCK); \
++ __gpio_clear_pin(SPDA); \
++ __gpio_clear_pin(SPEN); \
++ value=((a<<8)|(b&0xFF)); \
++ for(no=0;no<16;no++) \
++ { \
++ __gpio_clear_pin(SPCK); \
++ if((value&0x8000)==0x8000) \
++ __gpio_set_pin(SPDA); \
++ else \
++ __gpio_clear_pin(SPDA); \
++ __gpio_set_pin(SPCK); \
++ value=(value<<1); \
++ } \
++ __gpio_set_pin(SPEN); \
++} while (0)
++
++#define __lcd_display_pin_init() \
++do { \
++ __cpm_start_tcu(); \
++ __gpio_as_output(SPEN); /* use SPDA */ \
++ __gpio_as_output(SPCK); /* use SPCK */ \
++ __gpio_as_output(SPDA); /* use SPDA */ \
++} while (0)
++
++#define __lcd_display_on() \
++do { \
++ __spi_write_reg1(0x05, 0x1e); \
++ __spi_write_reg1(0x05, 0x5e); \
++ __spi_write_reg1(0x07, 0x8d); \
++ __spi_write_reg1(0x13, 0x01); \
++ __spi_write_reg1(0x05, 0x5f); \
++} while (0)
++
++#define __lcd_display_off() \
++do { \
++ __spi_write_reg1(0x05, 0x5e); \
++} while (0)
++
++#endif /* __QI_LB60_GPM940B0_H__ */
+diff --git a/include/configs/qi_lb60.h b/include/configs/qi_lb60.h
+index 52b370c..d3e78ad 100644
+--- a/include/configs/qi_lb60.h
++++ b/include/configs/qi_lb60.h
+@@ -32,6 +32,13 @@
+ * Miscellaneous configurable options
+ */
+ #define CONFIG_NANONOTE
++
++#define CONFIG_LCD
++#define CONFIG_SYS_WHITE_ON_BLACK
++#define LCD_BPP LCD_COLOR32
++#define CONFIG_VIDEO_GPM940B0
++
++
+ #define CONFIG_JZ4740_MMC
+ #define CONFIG_MMC 1
+ #define CONFIG_FAT 1
+diff --git a/include/lcd.h b/include/lcd.h
+index 42070d7..6de5482 100644
+--- a/include/lcd.h
++++ b/include/lcd.h
+@@ -263,8 +263,44 @@ typedef struct vidinfo {
+
+ void init_panel_info(vidinfo_t *vid);
+
+-#else
++#elif defined(CONFIG_JZSOC)
++/*
++ * LCD controller stucture for JZSOC: JZ4740
++ */
++struct jz_fb_dma_descriptor {
++ u_long fdadr; /* Frame descriptor address register */
++ u_long fsadr; /* Frame source address register */
++ u_long fidr; /* Frame ID register */
++ u_long ldcmd; /* Command register */
++};
++
++/*
++ * Jz LCD info
++ */
++struct jz_fb_info {
++
++ u_long fdadr0; /* physical address of frame/palette descriptor */
++ u_long fdadr1; /* physical address of frame descriptor */
++
++ /* DMA descriptors */
++ struct jz_fb_dma_descriptor * dmadesc_fblow;
++ struct jz_fb_dma_descriptor * dmadesc_fbhigh;
++ struct jz_fb_dma_descriptor * dmadesc_palette;
++ u_long screen; /* address of frame buffer */
++ u_long palette; /* address of palette memory */
++ u_int palette_size;
++};
++typedef struct vidinfo {
++ ushort vl_col; /* Number of columns (i.e. 640) */
++ ushort vl_row; /* Number of rows (i.e. 480) */
++ u_char vl_bpix; /* Bits per pixel, 0 = 1, 1 = 2, 2 = 4, 3 = 8 */
++
++ struct jz_fb_info jz_fb;
++} vidinfo_t;
++
++extern vidinfo_t panel_info;
+
++#else
+ typedef struct vidinfo {
+ ushort vl_col; /* Number of columns (i.e. 160) */
+ ushort vl_row; /* Number of rows (i.e. 100) */
+@@ -318,6 +354,7 @@ void lcd_show_board_info(void);
+ #define LCD_COLOR4 2
+ #define LCD_COLOR8 3
+ #define LCD_COLOR16 4
++#define LCD_COLOR32 5
+
+ /*----------------------------------------------------------------------*/
+ #if defined(CONFIG_LCD_INFO_BELOW_LOGO)
+@@ -369,7 +406,7 @@ void lcd_show_board_info(void);
+ # define CONSOLE_COLOR_GREY 14
+ # define CONSOLE_COLOR_WHITE 15 /* Must remain last / highest */
+
+-#else
++#elif LCD_BPP == LCD_COLOR16
+
+ /*
+ * 16bpp color definitions
+@@ -377,6 +414,15 @@ void lcd_show_board_info(void);
+ # define CONSOLE_COLOR_BLACK 0x0000
+ # define CONSOLE_COLOR_WHITE 0xffff /* Must remain last / highest */
+
++#elif LCD_BPP == LCD_COLOR32
++/*
++ * 18,24,32 bpp color definitions
++ */
++# define CONSOLE_COLOR_BLACK 0x00000000
++# define CONSOLE_COLOR_WHITE 0xffffffff /* Must remain last / highest */
++
++#else
++
+ #endif /* color definitions */
+
+ /************************************************************************/
+@@ -406,7 +452,7 @@ void lcd_show_board_info(void);
+ #if LCD_BPP == LCD_MONOCHROME
+ # define COLOR_MASK(c) ((c) | (c) << 1 | (c) << 2 | (c) << 3 | \
+ (c) << 4 | (c) << 5 | (c) << 6 | (c) << 7)
+-#elif (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16)
++#elif (LCD_BPP == LCD_COLOR8) || (LCD_BPP == LCD_COLOR16) || (LCD_BPP == LCD_COLOR32)
+ # define COLOR_MASK(c) (c)
+ #else
+ # error Unsupported LCD BPP.
+--
+1.7.9.5
+
diff --git a/package/boot/uboot-xburst/patches/0006-enable-silent-console.patch b/package/boot/uboot-xburst/patches/0006-enable-silent-console.patch
new file mode 100644
index 0000000..ebd6a6a
--- /dev/null
+++ b/package/boot/uboot-xburst/patches/0006-enable-silent-console.patch
@@ -0,0 +1,60 @@
+From 5eb4d4c598f2806bd1b3d1140e917bfead7851ad Mon Sep 17 00:00:00 2001
+From: Xiangfu <xiangfu@openmobilefree.net>
+Date: Wed, 10 Oct 2012 23:51:26 +0800
+Subject: [PATCH 6/6] enable silent console
+
+---
+ common/console.c | 16 ++++++++++++++++
+ include/configs/qi_lb60.h | 2 ++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/common/console.c b/common/console.c
+index 1177f7d..e8a2078 100644
+--- a/common/console.c
++++ b/common/console.c
+@@ -685,6 +685,14 @@ done:
+
+ gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
+
++#ifdef CONFIG_SILENT_CONSOLE
++ /* Check one more time the contents of the silent environment
++ * variable, because if the environment is loaded from NAND it was
++ * not available when console_init_f() was called */
++ if (getenv("silent") != NULL)
++ gd->flags |= GD_FLG_SILENT;
++#endif
++
+ stdio_print_current_devices();
+
+ #ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE
+@@ -760,6 +768,14 @@ int console_init_r(void)
+
+ gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
+
++#ifdef CONFIG_SILENT_CONSOLE
++ /* Check one more time the contents of the silent environment
++ * variable, because if the environment is loaded from NAND it was
++ * not available when console_init_f() was called */
++ if (getenv("silent") != NULL)
++ gd->flags |= GD_FLG_SILENT;
++#endif
++
+ stdio_print_current_devices();
+
+ /* Setting environment variables */
+diff --git a/include/configs/qi_lb60.h b/include/configs/qi_lb60.h
+index d3e78ad..a3534ff 100644
+--- a/include/configs/qi_lb60.h
++++ b/include/configs/qi_lb60.h
+@@ -102,6 +102,8 @@
+ #define CONFIG_SYS_NO_FLASH
+ #define CONFIG_SYS_FLASH_BASE 0 /* init flash_base as 0 */
+
++#define CONFIG_SILENT_CONSOLE 1 /* Enable silent console */
++
+ /*
+ * Command line configuration
+ */
+--
+1.7.9.5
+