| /* | 
 |  * (C) Copyright 2016 ZXIC Inc. | 
 |  */ | 
 |     | 
 |  | 
 | #include <common.h> | 
 | #include <asm/arch/denali.h> | 
 | #include <asm/io.h> | 
 | #include <bbt.h> | 
 |  | 
 | #include "flash.h" | 
 |  | 
 |  | 
 | static struct nand_flash_device_para *nand_info = NULL; | 
 | /* nand flash parameter config */ | 
 | static const struct nand_flash_device_para nand_flash_para[] = { | 
 | 	/* MT29F4G08ABBDAH4 for 7520V5 evb 512MB X 8 */ | 
 | 	{0x2C, 0xAC, 0x90, 0, 4, 2048,11, 64, 17, 4096, 0x20000}, | 
 |     /* MT29F2G08ABBEA for 7520 evb 256MB X 8 */ | 
 |     {0x2C, 0xAA, 0x90, 0, 4, 2048,11, 64, 17, 2048, 0x20000}, | 
 |      | 
 |     /* K9F2G08U0B for 7520 FPGA */ | 
 |     {0xEC, 0xDA, 0x10, 0, 4, 2048,11, 64, 17,2048, 0x20000}, | 
 |  | 
 | 	/* JSFBA3YHABB for 7510 evb 256MB X 8  ---> DDR 128M*/ | 
 | 	{0xAD, 0xAA, 0x90, 0, 8, 2048, 11, 128, 17,2048, 0x20000}, | 
 |  | 
 | 	/* FM6BD2G1GA for 7510 evb 256MB X 8 ---> DDR 128M */ | 
 | 	{0xC8, 0xAA, 0x90, 0, 4, 2048, 11, 64, 17, 2048, 0x20000}, | 
 |  | 
 | 	/* H9TA4GG4GDMCPR for 7510 evb 512MB X 8 ---> DDR 512M */ | 
 | 	{0xAD, 0xAC, 0x90, 0, 8, 2048, 11, 128, 17, 4096, 0x20000}, | 
 |  | 
 | 	/* JSFCBX3Y7ABB for 7510 evb 512MB X 8 ---> DDR 256M */ | 
 | 	{0x01, 0xAC, 0x90, 0, 8, 2048, 11, 128, 17, 4096, 0x20000}, | 
 |  | 
 | 	/* FM6BD4G2GA for 7510 evb 512MB X 8 ---> DDR 256M*/ | 
 | 	{0xC8, 0xAC, 0x90, 0, 4, 2048, 11, 64, 17, 4096, 0x20000}, | 
 |  | 
 | 	/* NM1281KSLAXAJ for 7510 evb 256MB X 8  ---> DDR 128M*/ | 
 | 	{0x98, 0xAA, 0x90, 0, 8, 2048, 11, 128, 17, 2048, 0x20000}, | 
 |  | 
 | 	/* W71NW20GF3FW  for 7520 evb 256MB X 8 */	 | 
 | 	{0xEF, 0xAA, 0x90, 0, 4, 2048,11, 64, 17, 2048, 0x20000},	 | 
 | 	{0} | 
 | }; | 
 |  | 
 |  | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 |  static uint32_t wait_for_ready(uint32_t status_type) | 
 | { | 
 |     uint32_t status = 0; | 
 |     while (!(readl(INTR_STATUS(0)) & status_type)); | 
 |     status = readl(INTR_STATUS(0));   | 
 |     writew(0xffff, INTR_STATUS(0)); | 
 | 	return status; | 
 | } | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | static void index_addr(uint32_t address, uint32_t data) | 
 | { | 
 | 	writel(address, NAND_DATA); | 
 | 	writel(data, NAND_DATA_10); | 
 | } | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | static void index_addr_read_data(uint32_t address, uint32_t *pdata) | 
 | { | 
 | 	writel(address, NAND_DATA); | 
 | 	*pdata = readl(NAND_DATA_10); | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | void nand_read_oob(uint8_t *buf, uint32_t offset, uint32_t len) | 
 | { | 
 |     uint32_t cmd, i, status_type;  | 
 |     uint32_t *buf32 = NULL;  | 
 |     uint32_t addr = offset >> (nand_info->page_size_shift);     //addr = bank0 | page | 
 |      | 
 |     /* 0. disable ECC */ | 
 |     writel(ECC_DISABLE__FLAG, ECC_ENABLE); | 
 |  | 
 | 	writel(0xffff, INTR_STATUS(0)); | 
 |  | 
 |     writel(TRANSFER_SPARE_REG__FLAG, TRANSFER_SPARE_REG); | 
 |  | 
 |     /* setup page read request for SPARE_ACCESS read */ | 
 | 	cmd = MODE_10 | addr; | 
 | 	index_addr((uint32_t)cmd, SPARE_ACCESS); | 
 |  | 
 | 	cmd = MODE_10 | addr; | 
 | 	index_addr((uint32_t)cmd, 0x2001); | 
 |  | 
 |     /* page 33 of the NAND controller spec indicates we should not | 
 | 	    use the pipeline commands in Spare area only mode. So we | 
 | 		don't. | 
 | 	 */ | 
 |     cmd = MODE_01 | addr; | 
 |     writel(cmd, NAND_DATA); | 
 |  | 
 |     status_type = wait_for_ready(INTR_STATUS__LOAD_COMP | INTR_STATUS__TIME_OUT); | 
 | 	if (status_type & INTR_STATUS__TIME_OUT) | 
 | 		printf("[OT]\n");   /* OOB TIMEOUT */ | 
 |  | 
 | 	/* 0. read oob data */ | 
 |     buf32 = (uint32_t *)buf; | 
 | 	for (i = 0; i < len / 4; i++) | 
 | 		*buf32++ = readl(NAND_DATA_10); | 
 |  | 
 | 	/* We set the device back to MAIN_ACCESS here as I observed | 
 | 	 * instability with the controller if you do a block erase | 
 | 	 * and the last transaction was a SPARE_ACCESS. Block erase | 
 | 	 * is reliable (according to the MTD test infrastructure) | 
 | 	 * if you are in MAIN_ACCESS. | 
 | 	 */ | 
 | 	cmd = MODE_10 | addr; | 
 | 	index_addr((uint32_t)cmd, MAIN_ACCESS); | 
 |      | 
 | } | 
 |  | 
 |  | 
 | void clear_intr(void) | 
 | { | 
 | 	while(readl(INTR_STATUS(0))) | 
 | 	{ | 
 | 		writew(0xffff, INTR_STATUS(0)); | 
 | 		 | 
 | 	} | 
 | 	writew(0xffff, INTR_STATUS(0)); | 
 | 	 | 
 | 	nsdelay(400000);//0.02s | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | int32_t read_page(uint32_t buf, uint32_t offset) | 
 | { | 
 |     uint32_t status = 0; | 
 | 	int32_t ecc_status = 0; | 
 |     uint32_t page = offset >> (nand_info->page_size_shift); | 
 |     uint32_t mode = MODE_10;         /* MODE_10 | BANK(denali->flash_bank) */ | 
 |  | 
 |     if((buf & 0x3) != 0)          /* DMAµØÖ·ÒªÇó4×Ö½Ú¶ÔÆë */ | 
 |     { | 
 |         printf("[AE]\n"); /* [read_page]:ADDR ERROR */ | 
 |         return -1;       | 
 |     } | 
 |  | 
 | 	ecc_status =readl(ECC_CORRECTION); | 
 |  | 
 | 	if(page < 64) | 
 | 	{ | 
 | 		writel(0x8, ECC_CORRECTION); | 
 | 	} | 
 |      | 
 |     /* clear status */ | 
 |     writew(0xffff, INTR_STATUS(0)); | 
 |      | 
 | 	writel(TRANSFER_MAIN_REG__FLAG, TRANSFER_SPARE_REG); | 
 |      | 
 |     /* enable DMA */ | 
 |     writel(DMA_ENABLE__FLAG, DMA_ENABLE);  | 
 |  | 
 | 	/* setup transfer type and # of pages -FOR DMA */ | 
 |     index_addr(mode | page, 0x2001);    //mode10(0x08000000) | bank0(0) | page,  | 
 |                                          //  0x2000 | DENALI_READ(0) | 1 | 
 |  | 
 | 	/* set memory high address bits 23:8 -FOR DMA */ | 
 |     index_addr(mode | ((uint16_t)(buf >> 16) << 8), 0x2200); | 
 |  | 
 | 	/* set memory low address bits 23:8 -FOR DMA */ | 
 |     index_addr(mode | ((uint16_t)buf << 8), 0x2300); | 
 |  | 
 | 	/* interrupt when complete, burst len = 64 bytes -FOR DMA*/ | 
 |     index_addr( mode | 0x14000, 0x2400);     //zhouqi not interrupt 0X40 | 
 |  | 
 |     /* wait_for_ready */ | 
 |     status = wait_for_ready (INTR_STATUS__DMA_CMD_COMP | INTR_STATUS__ECC_ERR); | 
 |     if (status & INTR_STATUS__ECC_ERR ) | 
 |     { | 
 |         printf("[EE]\n");     /* [read_page]: ECC ERROR */ | 
 | 		/* 0. clear status */ | 
 |     	clear_intr(); | 
 |         return -1;         | 
 |     } | 
 |          | 
 |  | 
 |     if (status & INTR_STATUS__TIME_OUT) | 
 |     { | 
 |         printf("TO\n");  /* [read_page]: TIME OUT */ | 
 | 		/* clear status */ | 
 |     	clear_intr(); | 
 |         return -1;       | 
 |     } | 
 |  | 
 | 	writel(ecc_status, ECC_CORRECTION); | 
 |  | 
 |     /* disable DMA */ | 
 |     writel(DMA_DISABLE__FLAG, DMA_ENABLE); | 
 |      | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * from: must page align | 
 |    len:  must page align | 
 |  */ | 
 |  /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | int32_t nand_read(uint32_t from, uint32_t len, uint32_t to) | 
 | { | 
 |     uint32_t offset = from; | 
 |     uint32_t left_to_read = len; | 
 |     uint32_t p_to = to; | 
 |     int32_t ret = 0; | 
 |     int32_t page_size = (nand_info->page_size); | 
 |  | 
 |     if((offset & (page_size - 1)) || (len & (page_size - 1))) | 
 |     { | 
 |         printf("param err.\n"); | 
 |         return -1;        | 
 |     } | 
 |  | 
 |     while(left_to_read > 0) | 
 |     {        | 
 |         ret = read_page(p_to, offset); | 
 | 		if(ret != 0) | 
 | 		{ | 
 | 			return -1; | 
 | 		} | 
 |          | 
 |         left_to_read -= page_size; | 
 |         offset += page_size; | 
 |         p_to += page_size;           | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | int32_t read_data(uint32_t from, uint32_t len, uint32_t to) | 
 | { | 
 |     uint32_t offset = from; | 
 |     uint32_t left_to_read = len; | 
 |     uint32_t p_to = to; | 
 |     int32_t ret = 0; | 
 |     int32_t block_size = (nand_info->block_size); | 
 |              | 
 |     while( left_to_read > 0 ) | 
 |     { | 
 |         uint32_t block_offset = offset & (block_size - 1); | 
 | 		uint32_t read_length; | 
 |  | 
 |         if( nand_block_isbad(offset) ) | 
 |         { | 
 |             offset += block_size; | 
 |             continue; | 
 |         } | 
 |              | 
 |         if (left_to_read < (block_size - block_offset)) | 
 | 			read_length = left_to_read; | 
 | 		else | 
 | 			read_length = block_size - block_offset; | 
 |        | 
 |         ret = nand_read(offset, read_length, p_to); | 
 | 		if(ret != 0) | 
 | 		{ | 
 | 			return -1; | 
 | 		} | 
 |  | 
 |         left_to_read -= read_length; | 
 |         offset += read_length; | 
 |         p_to += read_length;          | 
 |     } | 
 |      | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 |  int32_t nand_read_id (void) | 
 | { | 
 |     uint32_t id[5]; | 
 |     uint32_t i = 0; | 
 | 	uint32_t addr = (uint32_t)MODE_11;      /*  | BANK(0) */ | 
 |     struct nand_flash_device_para *nand = &nand_flash_para[0]; | 
 |  | 
 |     index_addr(addr | 0, 0x90); | 
 | 	index_addr(addr | 1, 0); | 
 | 	for (i = 0; i < 5; i++)  | 
 |     { | 
 | 		index_addr_read_data(addr | 2,	&id[i]); | 
 | 	} | 
 |  | 
 |     i = 0; | 
 |     while( nand->manuf_id != 0 ) | 
 |     {    | 
 |         if( ((uint8_t)id[0] == nand->manuf_id) &&  | 
 |                 ((uint8_t)id[1] == nand->device_id) && | 
 |                     ((uint8_t)id[2] == nand->res_id)) | 
 |         { | 
 |             nand_info = nand; | 
 |  | 
 | 			writel(nand_info->bus_num, DEVICE_WIDTH); | 
 | 			writel(nand_info->page_size, DEVICE_MAIN_AREA_SIZE); | 
 | 			writel(nand_info->page_size, LOGICAL_PAGE_DATA_SIZE); | 
 | 			writel(nand_info->oob_size, DEVICE_SPARE_AREA_SIZE); | 
 | 			writel(nand_info->ecc_strength, ECC_CORRECTION);    | 
 |             return 0; | 
 |         }; | 
 |         nand++; | 
 |         i++; | 
 |     } | 
 |      | 
 |     return 1;    | 
 | } | 
 |  | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | int32_t nand_init (void) | 
 | { | 
 |     int32_t ret = 0; | 
 |     writel(ECC_DISABLE__FLAG, ECC_ENABLE);      /*  ecc_enable */ | 
 |     writel(2, SPARE_AREA_SKIP_BYTES); | 
 |  | 
 |     ret = nand_read_id(); | 
 |     if( ret != 0 ) | 
 |         return -1; | 
 |  | 
 | 	flash.flash_type = NAND_BOOT; | 
 | 	flash.manuf_id = nand_info->manuf_id; | 
 | 	flash.device_id = nand_info->device_id; | 
 | 	flash.page_size = nand_info->page_size; | 
 | 	flash.page_size_shift = nand_info->page_size_shift; | 
 | 	flash.oob_size = nand_info->oob_size; | 
 | 	flash.block_size = nand_info->block_size; | 
 | 	flash.block_size_shift = nand_info->block_size_shift; | 
 | 	flash.block_num = nand_info->block_num; | 
 | 	flash.read = read_data; | 
 | 	flash.read_oob = nand_read_oob; | 
 |  | 
 |     return 0; | 
 |      | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  ****************************************************************************** | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  *	 Output: | 
 |  * Returns: | 
 |  * Others: | 
 |  ******************************************************************************* | 
 |  */ | 
 | int board_flash_init(void) | 
 | { | 
 | 	int ret = 0; | 
 | 	char boot_mode = 0; | 
 |  | 
 | 	boot_mode = get_boot_mode(); | 
 | 	if(boot_mode != NAND_BOOT) | 
 | 	{ | 
 | 		printf("not nand flash.\n"); | 
 | 		return -1; | 
 | 	} | 
 | 	 | 
 | 	writel(CFG_START_MODE_NAND, CFG_BOOT_MODE_START_MODE_FOR_UBOOT);  | 
 | 	ret = nand_init();  | 
 | 	if(ret != 0) | 
 | 	{   | 
 | 		printf("nand init err.\n"); | 
 | 		return -1; | 
 | 	}	 | 
 | 	printf("nand init ok.\n"); | 
 | 	 | 
 | 	nand_creat_bbt(); | 
 | 		 | 
 | 	return 0; | 
 | } | 
 |  | 
 |  |