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 739cd37..44d5dd6 100644
--- a/board/qi/qi_lb60/qi_lb60-spl.c
+++ b/board/qi/qi_lb60/qi_lb60-spl.c
@@ -12,6 +12,22 @@
 #include <asm/io.h>
 #include <asm/jz4740.h>
 
+#define KEY_U_OUT       (32 * 2 + 16)
+#define KEY_U_IN        (32 * 3 + 19)
+
+static int is_usb_boot()
+{
+	__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) == 0)
+		return 1;
+
+	return 0;
+}
+
 void nand_spl_boot(void)
 {
 	uint32_t reg;
@@ -26,6 +42,11 @@ void nand_spl_boot(void)
 	pll_init();
 	sdram_init();
 
+	if (is_usb_boot()) {
+		serial_puts("[U] pressed, goto USBBOOT mode\n");
+		usb_boot();
+	}
+
 	nand_init();
 
 	serial_puts("\nStarting 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
