mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			430 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2007 Ingenic Semiconductor Inc.
 | |
|  * Author: Peter <jlwei@ingenic.cn>
 | |
|  *
 | |
|  * 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 <common.h>
 | |
| #include <nand.h>
 | |
| 
 | |
| #include <asm/io.h>
 | |
| #include <asm/jz4740.h>
 | |
| 
 | |
| #define KEY_U_OUT       (32 * 2 + 16)
 | |
| #define KEY_U_IN        (32 * 3 + 19)
 | |
| 
 | |
| /*
 | |
|  * NAND flash definitions
 | |
|  */
 | |
| 
 | |
| #define NAND_DATAPORT	0xb8000000
 | |
| #define NAND_ADDRPORT	0xb8010000
 | |
| #define NAND_COMMPORT	0xb8008000
 | |
| 
 | |
| #define ECC_BLOCK	512
 | |
| #define ECC_POS		6
 | |
| #define PAR_SIZE	9
 | |
| 
 | |
| #define __nand_enable()		(REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1)
 | |
| #define __nand_disable()	(REG_EMC_NFCSR &= ~(EMC_NFCSR_NFCE1))
 | |
| #define __nand_ecc_rs_encoding() \
 | |
| 	(REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_ENCODING)
 | |
| #define __nand_ecc_rs_decoding() \
 | |
| 	(REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_DECODING)
 | |
| #define __nand_ecc_disable()	(REG_EMC_NFECR &= ~EMC_NFECR_ECCE)
 | |
| #define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF))
 | |
| #define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF))
 | |
| 
 | |
| static inline void __nand_dev_ready(void)
 | |
| {
 | |
| 	unsigned int timeout = 10000;
 | |
| 	while ((REG_GPIO_PXPIN(2) & 0x40000000) && timeout--);
 | |
| 	while (!(REG_GPIO_PXPIN(2) & 0x40000000));
 | |
| }
 | |
| 
 | |
| #define __nand_cmd(n)		(REG8(NAND_COMMPORT) = (n))
 | |
| #define __nand_addr(n)		(REG8(NAND_ADDRPORT) = (n))
 | |
| #define __nand_data8()		REG8(NAND_DATAPORT)
 | |
| #define __nand_data16()		REG16(NAND_DATAPORT)
 | |
| 
 | |
| #if (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B8R3)
 | |
| 	#define NAND_BUS_WIDTH 8
 | |
| 	#define NAND_ROW_CYCLE 3
 | |
| #elif (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B8R2)
 | |
| 	#define NAND_BUS_WIDTH 8
 | |
| 	#define NAND_ROW_CYCLE 2
 | |
| #elif (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B16R3)
 | |
| 	#define NAND_BUS_WIDTH 16
 | |
| 	#define NAND_ROW_CYCLE 3
 | |
| #elif (JZ4740_NANDBOOT_CFG == JZ4740_NANDBOOT_B16R2)
 | |
| 	#define NAND_BUS_WIDTH 16
 | |
| 	#define NAND_ROW_CYCLE 2
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * NAND flash parameters
 | |
|  */
 | |
| static int page_size = 2048;
 | |
| static int oob_size = 64;
 | |
| static int ecc_count = 4;
 | |
| static int page_per_block = 64;
 | |
| static int bad_block_pos = 0;
 | |
| static int block_size = 131072;
 | |
| 
 | |
| static unsigned char oob_buf[128] = {0};
 | |
| 
 | |
| /*
 | |
|  * External routines
 | |
|  */
 | |
| extern void flush_cache_all(void);
 | |
| extern int serial_init(void);
 | |
| extern void serial_puts(const char *s);
 | |
| extern void sdram_init(void);
 | |
| extern void pll_init(void);
 | |
| extern void usb_boot();
 | |
| 
 | |
| /*
 | |
|  * NAND flash routines
 | |
|  */
 | |
| #if NAND_BUS_WIDTH == 16
 | |
| static inline void nand_read_buf16(void *buf, int count)
 | |
| {
 | |
| 	int i;
 | |
| 	u16 *p = (u16 *)buf;
 | |
| 
 | |
| 	for (i = 0; i < count; i += 2)
 | |
| 		*p++ = __nand_data16();
 | |
| }
 | |
| #define nand_read_buf nand_read_buf16
 | |
| 
 | |
| #elif NAND_BUS_WIDTH == 8
 | |
| static inline void nand_read_buf8(void *buf, int count)
 | |
| {
 | |
| 	int i;
 | |
| 	u8 *p = (u8 *)buf;
 | |
| 
 | |
| 	for (i = 0; i < count; i++)
 | |
| 		*p++ = __nand_data8();
 | |
| }
 | |
| #define nand_read_buf nand_read_buf8
 | |
| 
 | |
| #endif
 | |
| 
 | |
| /* Correct 1~9-bit errors in 512-bytes data */
 | |
| static void rs_correct(unsigned char *dat, int idx, int mask)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	idx--;
 | |
| 
 | |
| 	i = idx + (idx >> 3);
 | |
| 	if (i >= 512)
 | |
| 		return;
 | |
| 
 | |
| 	mask <<= (idx & 0x7);
 | |
| 
 | |
| 	dat[i] ^= mask & 0xff;
 | |
| 	if (i < 511)
 | |
| 		dat[i+1] ^= (mask >> 8) & 0xff;
 | |
| }
 | |
| 
 | |
| static int nand_read_oob(int page_addr, uchar *buf, int size)
 | |
| {
 | |
| 	int col_addr;
 | |
| 	if (page_size != 512)
 | |
| 		col_addr = page_size;
 | |
| 	else {
 | |
| 		col_addr = 0;
 | |
| 		__nand_dev_ready();
 | |
| 	}
 | |
| 
 | |
| 	if (page_size != 512)
 | |
| 		/* Send READ0 command */
 | |
| 		__nand_cmd(NAND_CMD_READ0);
 | |
| 	else
 | |
| 		/* Send READOOB command */
 | |
| 		__nand_cmd(NAND_CMD_READOOB);
 | |
| 
 | |
| 	/* Send column address */
 | |
| 	__nand_addr(col_addr & 0xff);
 | |
| 	if (page_size != 512)
 | |
| 		__nand_addr((col_addr >> 8) & 0xff);
 | |
| 
 | |
| 	/* Send page address */
 | |
| 	__nand_addr(page_addr & 0xff);
 | |
| 	__nand_addr((page_addr >> 8) & 0xff);
 | |
| 	#ifdef NAND_ROW_CYCLE == 3
 | |
| 		__nand_addr((page_addr >> 16) & 0xff);
 | |
| 	#endif
 | |
| 
 | |
| 	/* Send READSTART command for 2048 or 4096 ps NAND */
 | |
| 	if (page_size != 512)
 | |
| 		__nand_cmd(NAND_CMD_READSTART);
 | |
| 
 | |
| 	/* Wait for device ready */
 | |
| 	__nand_dev_ready();
 | |
| 
 | |
| 	/* Read oob data */
 | |
| 	nand_read_buf(buf, size);
 | |
| 	if (page_size == 512)
 | |
| 		__nand_dev_ready();
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int nand_read_page(int page_addr, uchar *dst, uchar *oobbuf)
 | |
| {
 | |
| 	uchar *databuf = dst, *tmpbuf;
 | |
| 	int i, j;
 | |
| 
 | |
| 	/*
 | |
| 	 * Read oob data
 | |
| 	 */
 | |
| 	nand_read_oob(page_addr, oobbuf, oob_size);
 | |
| 
 | |
| 	/*
 | |
| 	 * Read page data
 | |
| 	 */
 | |
| 
 | |
| 	/* Send READ0 command */
 | |
| 	__nand_cmd(NAND_CMD_READ0);
 | |
| 
 | |
| 	/* Send column address */
 | |
| 	__nand_addr(0);
 | |
| 	if (page_size != 512)
 | |
| 		__nand_addr(0);
 | |
| 
 | |
| 	/* Send page address */
 | |
| 	__nand_addr(page_addr & 0xff);
 | |
| 	__nand_addr((page_addr >> 8) & 0xff);
 | |
| 	#if NAND_ROW_CYCLE == 3
 | |
| 		__nand_addr((page_addr >> 16) & 0xff);
 | |
| 	#endif
 | |
| 
 | |
| 	/* Send READSTART command for 2048 or 4096 ps NAND */
 | |
| 	if (page_size != 512)
 | |
| 		__nand_cmd(NAND_CMD_READSTART);
 | |
| 
 | |
| 	/* Wait for device ready */
 | |
| 	__nand_dev_ready();
 | |
| 
 | |
| 	/* Read page data */
 | |
| 	tmpbuf = databuf;
 | |
| 
 | |
| 	for (i = 0; i < ecc_count; i++) {
 | |
| 		volatile unsigned char *paraddr = (volatile unsigned char *)EMC_NFPAR0;
 | |
| 		unsigned int stat;
 | |
| 
 | |
| 		/* Enable RS decoding */
 | |
| 		REG_EMC_NFINTS = 0x0;
 | |
| 		__nand_ecc_rs_decoding();
 | |
| 
 | |
| 		/* Read data */
 | |
| 		nand_read_buf((void *)tmpbuf, ECC_BLOCK);
 | |
| 
 | |
| 		/* Set PAR values */
 | |
| 		for (j = 0; j < PAR_SIZE; j++) {
 | |
| #if defined(CONFIG_SYS_NAND_ECC_POS)
 | |
| 			*paraddr++ = oobbuf[CONFIG_SYS_NAND_ECC_POS + i*PAR_SIZE + j];
 | |
| #else
 | |
| 			*paraddr++ = oobbuf[ECC_POS + i*PAR_SIZE + j];
 | |
| #endif
 | |
| 		}
 | |
| 
 | |
| 		/* Set PRDY */
 | |
| 		REG_EMC_NFECR |= EMC_NFECR_PRDY;
 | |
| 
 | |
| 		/* Wait for completion */
 | |
| 		__nand_ecc_decode_sync();
 | |
| 
 | |
| 		/* Disable decoding */
 | |
| 		__nand_ecc_disable();
 | |
| 
 | |
| 		/* Check result of decoding */
 | |
| 		stat = REG_EMC_NFINTS;
 | |
| 		if (stat & EMC_NFINTS_ERR) {
 | |
| 			/* Error occurred */
 | |
| 			/* serial_puts("\n Error occurred\n"); */
 | |
| 			if (stat & EMC_NFINTS_UNCOR) {
 | |
| 				/* Uncorrectable error occurred */
 | |
| 				/* serial_puts("\nUncorrectable error occurred\n"); */
 | |
| 			}
 | |
| 			else {
 | |
| 				unsigned int errcnt, index, mask;
 | |
| 
 | |
| 				errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT;
 | |
| 				switch (errcnt) {
 | |
| 				case 4:
 | |
| 					index = (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT;
 | |
| 					mask = (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT;
 | |
| 					rs_correct(tmpbuf, index, mask);
 | |
| 					/* FALL-THROUGH */
 | |
| 				case 3:
 | |
| 					index = (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT;
 | |
| 					mask = (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT;
 | |
| 					rs_correct(tmpbuf, index, mask);
 | |
| 					/* FALL-THROUGH */
 | |
| 				case 2:
 | |
| 					index = (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT;
 | |
| 					mask = (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT;
 | |
| 					rs_correct(tmpbuf, index, mask);
 | |
| 					/* FALL-THROUGH */
 | |
| 				case 1:
 | |
| 					index = (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT;
 | |
| 					mask = (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT;
 | |
| 					rs_correct(tmpbuf, index, mask);
 | |
| 					break;
 | |
| 				default:
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		tmpbuf += ECC_BLOCK;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #ifndef CONFIG_SYS_NAND_BADBLOCK_PAGE
 | |
| #define CONFIG_SYS_NAND_BADBLOCK_PAGE 0 /* NAND bad block was marked at this page in a block, starting from 0 */
 | |
| #endif
 | |
| 
 | |
| static void nand_load(int offs, int uboot_size, uchar *dst)
 | |
| {
 | |
| 	int page;
 | |
| 	int pagecopy_count;
 | |
| 
 | |
| 	__nand_enable();
 | |
| 
 | |
| 	page = offs / page_size;
 | |
| 	pagecopy_count = 0;
 | |
| 	while (pagecopy_count < (uboot_size / page_size)) {
 | |
| 		if (page % page_per_block == 0) {
 | |
| 			nand_read_oob(page + CONFIG_SYS_NAND_BADBLOCK_PAGE, oob_buf, oob_size);
 | |
| 			if (oob_buf[bad_block_pos] != 0xff) {
 | |
| 				page += page_per_block;
 | |
| 				/* Skip bad block */
 | |
| 				continue;
 | |
| 			}
 | |
| 		}
 | |
| 		/* Load this page to dst, do the ECC */
 | |
| 		nand_read_page(page, dst, oob_buf);
 | |
| 
 | |
| 		dst += page_size;
 | |
| 		page++;
 | |
| 		pagecopy_count++;
 | |
| 	}
 | |
| 
 | |
| 	__nand_disable();
 | |
| }
 | |
| 
 | |
| static void jz_nand_init(void) {
 | |
| 
 | |
|  	/* Optimize the timing of nand */
 | |
| 	REG_EMC_SMCR1 = 0x094c4400;
 | |
| }
 | |
| 
 | |
| static void gpio_init(void)
 | |
| {
 | |
| 	/*
 | |
| 	 * Initialize SDRAM pins
 | |
| 	 */
 | |
| #if defined(CONFIG_JZ4720)
 | |
| 	__gpio_as_sdram_16bit_4720();
 | |
| #elif defined(CONFIG_JZ4725)
 | |
| 	__gpio_as_sdram_16bit_4725();
 | |
| #else
 | |
| 	__gpio_as_sdram_32bit();
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	 * Initialize UART0 pins
 | |
| 	 */
 | |
| 	__gpio_as_uart0();
 | |
| }
 | |
| 
 | |
| static int is_usb_boot()
 | |
| {
 | |
|  	int keyU = 0;
 | |
| 
 | |
|  	__gpio_as_input(KEY_U_IN);
 | |
|  	__gpio_enable_pull(KEY_U_IN);
 | |
| 
 | |
|  	__gpio_as_output(KEY_U_OUT);
 | |
|  	__gpio_clear_pin(KEY_U_OUT);
 | |
| 
 | |
|  	keyU = __gpio_get_pin(KEY_U_IN);
 | |
| 
 | |
|  	if (keyU)
 | |
|  		serial_puts("[U] not pressed\n");
 | |
|  	else
 | |
|  		serial_puts("[U] pressed\n");
 | |
| 
 | |
| 	return !keyU;
 | |
| }
 | |
| 
 | |
| void nand_boot(void)
 | |
| {
 | |
| 	void (*uboot)(void);
 | |
| 
 | |
| 	/*
 | |
| 	 * Init hardware
 | |
| 	 */
 | |
| 	jz_nand_init();
 | |
| 	gpio_init();
 | |
| 	serial_init();
 | |
| 
 | |
| 	serial_puts("\n\nNAND Secondary Program Loader\n\n");
 | |
| 
 | |
| 	pll_init();
 | |
| 	sdram_init();
 | |
| 
 | |
| #if defined(CONFIG_NANONOTE)
 | |
| 	if(is_usb_boot()) {
 | |
| 		serial_puts("enter USB BOOT mode\n");
 | |
| 		usb_boot();
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	page_size = CONFIG_SYS_NAND_PAGE_SIZE;
 | |
| 	block_size = CONFIG_SYS_NAND_BLOCK_SIZE;
 | |
| 	page_per_block = CONFIG_SYS_NAND_BLOCK_SIZE / CONFIG_SYS_NAND_PAGE_SIZE;
 | |
| 	bad_block_pos = (page_size == 512) ? 5 : 0;
 | |
| 	oob_size = page_size / 32;
 | |
| 	ecc_count = page_size / ECC_BLOCK;
 | |
| 
 | |
| 	/*
 | |
| 	 * Load U-Boot image from NAND into RAM
 | |
| 	 */
 | |
| 	nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE,
 | |
| 		  (uchar *)CONFIG_SYS_NAND_U_BOOT_DST);
 | |
| 
 | |
| 	uboot = (void (*)(void))CONFIG_SYS_NAND_U_BOOT_START;
 | |
| 
 | |
| 	serial_puts("Starting U-Boot ...\n");
 | |
| 
 | |
| 	/*
 | |
| 	 * Flush caches
 | |
| 	 */
 | |
| 	flush_cache_all();
 | |
| 
 | |
| 	/*
 | |
| 	 * Jump to U-Boot image
 | |
| 	 */
 | |
| 	(*uboot)();
 | |
| }
 |