| /* | 
 |  * (C) Copyright 2016 ZXIC Inc. | 
 |  */ | 
 |     | 
 |  | 
 | #include <common.h> | 
 | #include <asm/io.h> | 
 | #include <bbt.h> | 
 | #include "config.h" | 
 | #include "flash.h" | 
 |  | 
 |  | 
 | static uint8_t block_bad_table[BBT_SIZE]={0}; | 
 |  | 
 |  | 
 | static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; | 
 | static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; | 
 |  | 
 | static struct nand_bbt_descr bbt_main_descr = { | 
 | 	.offs =	4, | 
 | 	.len = 4, | 
 | 	.veroffs = 20, | 
 | 	.maxblocks = 16, | 
 | 	.pattern = bbt_pattern, | 
 | }; | 
 |  | 
 | static struct nand_bbt_descr bbt_mirror_descr = { | 
 | 	.offs =	4, | 
 | 	.len = 4, | 
 | 	.veroffs = 20, | 
 | 	.maxblocks = 16, | 
 | 	.pattern = mirror_pattern, | 
 | }; | 
 |  | 
 | static int nand_check_pattern(uint8_t *buf,struct nand_bbt_descr *td) | 
 | { | 
 | 	int i; | 
 | 	uint8_t *p = buf; | 
 | 	p += td->offs; | 
 |  | 
 | 	/* Compare the pattern */ | 
 | 	for (i = 0; i < td->len; i++)  | 
 |     { | 
 | 		if (p[i] != td->pattern[i]) | 
 | 			return -1; | 
 | 	} | 
 | 	 | 
 | 	return 0; | 
 | } | 
 |  | 
 | void nand_creat_ram_bbt( void ) | 
 | { | 
 | 	uint32_t i = 0; | 
 | 	uint32_t off = 0; | 
 | 	uint32_t oob_size = flash.oob_size; | 
 | 	uint32_t block_size = flash.block_size; | 
 | 	uint8_t oob[256];   /* ÕâÀﶨÒå×î´óµÄOOB SIZE */ | 
 | //#ifdef CONFIG_ZX297520V3E_MDL_AB | 
 | #if defined(CONFIG_ZX297520V3E_MDL_AB) || defined(CONFIG_ZX297520V3E_VEHICLE_DC) | 
 | 	uint32_t block_nums = 72; | 
 | #else | 
 | 	uint32_t block_nums = 17; | 
 | #endif | 
 |  | 
 | 	for( i=0; i<block_nums; i++ ) | 
 | 	{ | 
 | 		flash.read_oob(&oob[0], off, oob_size); | 
 | 		if(oob[0] != 0xff)                      /* bad block */ | 
 | 			block_bad_table[i] = BAD_BLOCK; | 
 | 		else  | 
 | 			block_bad_table[i] = 0x0; | 
 |  | 
 | 		off += block_size; | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | int nand_search_bbt(struct nand_bbt_descr *td) | 
 | { | 
 |  | 
 | 	int  startblock, block; | 
 | 	int blocktopage = flash.block_size_shift - flash.page_size_shift; | 
 | 	 | 
 | 	uint8_t oob_buffer[256]={0xFF}; | 
 |     uint32_t offs = 0; | 
 |  | 
 | 	/* Search direction down -> top */ | 
 | 	 startblock = flash.block_num -1; | 
 | 	 | 
 | 	td->page= -1; | 
 | 	/* Scan the maximum number of blocks */ | 
 | 	for (block = 0; block < td->maxblocks; block++)  | 
 |     { | 
 |  | 
 | 		int actblock = startblock -block; | 
 | 		offs = actblock << flash.block_size_shift; | 
 |  | 
 | 		/* Read first page */ | 
 | 		flash.read_oob(oob_buffer, offs, flash.oob_size); | 
 | 		if (!nand_check_pattern(oob_buffer,td))  | 
 |         { | 
 | 			td->page = actblock << blocktopage; | 
 | 			td->version= oob_buffer[td->veroffs]; | 
 | 			 | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	return 0; | 
 | } | 
 |  | 
 |  | 
 | static int nand_read_bbt(struct nand_bbt_descr  *td,int num) | 
 | { | 
 | 	int bits=2; | 
 | 	 | 
 | 	int res, i, j, act = 0; | 
 | 	int totlen; | 
 | 	int from; | 
 | 	uint8_t msk = (uint8_t) ((1 << bits) - 1); | 
 | 	totlen = (num * bits) >> 3; | 
 |     from =(td->page)<<(flash.page_size_shift); | 
 | 	char* buf; | 
 | 	 | 
 |  | 
 | 	res = flash.read_page_raw(CFG_TEMP_ADDR, from); | 
 | 	if (res < 0)  | 
 |     { | 
 | 		return -1; | 
 | 	} | 
 | 	 | 
 | 	buf =(char*) CFG_TEMP_ADDR; | 
 | 	 | 
 |  | 
 | 	/* Analyse data */ | 
 | 	for (i = 0; i < totlen; i++)  | 
 |     { | 
 | 		uint8_t dat = buf[i]; | 
 | 		for (j = 0; j < 8; j += bits, act += 2)  | 
 |         { | 
 | 			uint8_t tmp = (dat >> j) & msk; | 
 | 			if (tmp == msk) | 
 | 			{ | 
 | 			    block_bad_table[(act >> 1)]= 0X0; | 
 | 				continue; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				block_bad_table[(act >> 1)]= BAD_BLOCK; | 
 | 			} | 
 | 	    }   | 
 |      } | 
 | 	 | 
 | 	  return 0; | 
 | } | 
 |  | 
 |  | 
 | static void nand_init_bbt_descr(struct nand_bbt_descr *td,  | 
 |         					    	 struct nand_bbt_descr *md) | 
 | { | 
 | 	switch(flash.manuf_id) | 
 | 	{ | 
 | 		case NAND_MFR_PARAGON: | 
 | 			if(flash.device_id != NAND_DEVID_FDANWEI_1G) | 
 | 			{ | 
 | 				td->offs = BBT_INFO_OOB_OFFSET_PARAGON; | 
 | 				md->offs = BBT_INFO_OOB_OFFSET_PARAGON; | 
 | 				td->veroffs = BBT_INFO_OOB_VER_OFFSET_PARAGON; | 
 | 				md->veroffs = BBT_INFO_OOB_VER_OFFSET_PARAGON; | 
 | 			} | 
 | 			break; | 
 | 		case NAND_MFR_HEYANGTEK: | 
 | 			td->offs = BBT_INFO_OOB_OFFSET_HEYANGTEK; | 
 | 			md->offs = BBT_INFO_OOB_OFFSET_HEYANGTEK; | 
 | 			td->veroffs = BBT_INFO_OOB_VER_OFFSET_HEYANGTEK; 	 | 
 | 			md->veroffs =BBT_INFO_OOB_VER_OFFSET_HEYANGTEK; | 
 | 			break; | 
 | 		default:  | 
 | 			break; | 
 | 	} | 
 | } | 
 |  | 
 | int nand_creat_bbt( void ) | 
 | {   | 
 |     struct nand_bbt_descr *td = &bbt_main_descr; | 
 | 	struct nand_bbt_descr *md = &bbt_mirror_descr; | 
 |  | 
 | 	nand_init_bbt_descr(td, md); | 
 | 	 | 
 |     // 2.0 search main bbt | 
 | 	nand_search_bbt(td); | 
 | 	// 2.1 search mirror bbt | 
 | 	nand_search_bbt(md); | 
 |  | 
 | 	if(td->page==-1&&md->page ==-1) | 
 | 	{ | 
 | 		/* if failed found bbt, we create a ram bbt */ | 
 | 		nand_creat_ram_bbt(); | 
 | 	    return 0; | 
 | 	} | 
 |  | 
 | 	// 3.0 read and analyze bbt | 
 | 	if(td->page==-1) | 
 | 	{ | 
 | 		nand_read_bbt(md,BBT_SIZE);	 | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		if(md->page == -1) | 
 | 		{ | 
 | 			nand_read_bbt(td,BBT_SIZE);	 | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			if(td->version >= md->version) | 
 | 			{ | 
 | 				nand_read_bbt(td,BBT_SIZE);	 | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				nand_read_bbt(md,BBT_SIZE);	 | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	printf("bbt ok.\n"); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /******************************************************************************* | 
 |  * Function:      | 
 |  * Description:  | 
 |  * Parameters: | 
 |  *	 Input: | 
 |  * | 
 |  *	 Output: | 
 |  * | 
 |  * Returns: | 
 |  * | 
 |  * | 
 |  * Others: | 
 |  ********************************************************************************/ | 
 | uint32_t nand_block_isbad(uint32_t offset) | 
 | { | 
 |     uint32_t block_offset = offset >> (flash.block_size_shift); | 
 |     return block_bad_table[block_offset]; | 
 | } | 
 |  | 
 |  | 
 |  |