#include #include #include "typedef.h" #include "amt630h.h" #include "UartPrint.h" #include "timer.h" #include "spi.h" #include "cp15.h" #include "sysinfo.h" #include "crc32.h" #include "gpio.h" #if DEVICE_TYPE_SELECT == SPI_NAND_FLASH /* SPI commands */ #define SPI_RXFIFO_ENABLE (1<<8) #define SPI_TRANS_COMPLETE (1<<7) #define SPI_RXFIFO_OVER (1<<6) #define SPI_TXFIFO_FULL (1<<2) #define SPI_TXFIFO_REQ (1<<1) #define SPI_RXFIFO_FULL (1<<4) #define SPI_RXFIFO_NOTEMPTY (1<<3) #define SPI_TXFIFO_EMPTY (1<<2) #define SPI_TXFIFO_NOTFULL (1<<1) #define SPI_BUSY (1<<0) #define SPI0_CS0_GPIO 32 #define SPI0_IO0_GPIO 34 #define SPI_CS_GPIO SPI0_CS0_GPIO /* SPI NAND commands */ #define SPI_NAND_WRITE_ENABLE 0x06 #define SPI_NAND_WRITE_DISABLE 0x04 #define SPI_NAND_GET_FEATURE 0x0f #define SPI_NAND_SET_FEATURE 0x1f #define SPI_NAND_PAGE_READ 0x13 #define SPI_NAND_READ_CACHE 0x03 #define SPI_NAND_FAST_READ_CACHE 0x0b #define SPI_NAND_READ_CACHE_X2 0x3b #define SPI_NAND_READ_CACHE_X4 0x6b #define SPI_NAND_READ_CACHE_DUAL_IO 0xbb #define SPI_NAND_READ_CACHE_QUAD_IO 0xeb #define SPI_NAND_READ_ID 0x9f #define SPI_NAND_PROGRAM_LOAD 0x02 #define SPI_NAND_PROGRAM_LOAD4 0x32 #define SPI_NAND_PROGRAM_EXEC 0x10 #define SPI_NAND_PROGRAM_LOAD_RANDOM 0x84 #define SPI_NAND_PROGRAM_LOAD_RANDOM4 0xc4 #define SPI_NAND_BLOCK_ERASE 0xd8 #define SPI_NAND_RESET 0xff /* Registers common to all devices */ #define SPI_NAND_LOCK_REG 0xa0 #define SPI_NAND_PROT_UNLOCK_ALL 0x0 #define SPI_NAND_FEATURE_REG 0xb0 #define SPI_NAND_ECC_EN (1 << 4) #define SPI_NAND_QUAD_EN (1 << 0) #define SPI_NAND_STATUS_REG 0xc0 #define SPI_NAND_STATUS_REG_ECC_MASK 0x3 #define SPI_NAND_STATUS_REG_ECC_SHIFT 4 #define SPI_NAND_STATUS_REG_PROG_FAIL (1 << 3) #define SPI_NAND_STATUS_REG_ERASE_FAIL (1 << 2) #define SPI_NAND_STATUS_REG_WREN (1 << 1) #define SPI_NAND_STATUS_REG_BUSY (1 << 0) #define SPI_NAND_ECC_UNCORR 0x2 /* */ #define MAX_SNF_BUF_SIZE 32 #define MAX_SPARE_SIZE 64 #define MAX_PAGE_SIZE 2048 #define MAX_BLK_SIZE 2048 #define SNF_MAX_BLOCK_COUNT 2048 #define SNF_REPLACE_BLK_PERCENT 2 //default: 2% #define SNF_BBT_BLK_COUNT 4 #define SNF_BBT_HEADER_SIZE 32 #define SNF_BBT_HEADER_MASK "SNF BAD BLOCK TABLE" //<32 bytes. enum { SNF_OK, SNF_ERR, SNF_TIMEOUT, SNF_BAD_BLK, }; enum { SNF_BLK_BAD = 0, SNF_BLK_USED, SNF_BLK_GOOD = 0xFF }; typedef struct _snf_blk_node { u8 status; u16 replace; //replace bad block. } snf_blk_node; typedef struct _snf_bbt { u8 header_mask[SNF_BBT_HEADER_SIZE]; snf_blk_node node[SNF_MAX_BLOCK_COUNT]; u32 bad_blocks; u32 reserved[8]; u32 bbt_crc; } snf_bbt; typedef struct spi_nand_plane_select { /* plane select is used for block select */ u32 rpb; //read page(from nand array to cache) bit. u32 rcb; //read cache(from page cache to app buffer) bit. u32 plb; //program load(write data to cache) bit. u32 peb; //program excute(flush cache to nand array) bit. } snf_ps; typedef struct spi_nand_cfg_tag { char Name[MAX_SNF_BUF_SIZE]; u8 MID; u8 DID0; u8 DID1; u8 BBMType; u32 BytePerPage; u32 PagePerBlk; u32 TotalBlk; u32 SprSize; int PsArrayIndex; } spi_nand_cfg; typedef struct _snf_chip_info { u8 Name[MAX_SNF_BUF_SIZE]; u8 MID; u8 DID0; u8 DID1; u8 BBMType; u32 BytePerPage; u32 BytePerBlk; u32 TotalBlk; u32 SprSize; u32 FtlSprSize; u32 PsArrayIndex; // u32 AvailableBlks; u32 Capacity; u32 AvailableCapacity; u32 PagePerBlk; u32 BbtBlkOffset; u32 ReplaceBlks; snf_bbt bbt; snf_ps *ps; } snf_chip_info; static int SpiNandFindAReplaceBlock(snf_chip_info *chip, u32 *block); static int SpiNandUpdateBbt(void); static int SpiNandCheckReplaceBlockValid(u32 block); /* PlaneSelect bit array */ static snf_ps g_SnfPsArray[] = { [0] = {6, 12, 12, 6}, /* DS35Q2GA */ }; static spi_nand_cfg g_SnfChipCfgArray[] = { // name, MID, DID0, DID1, BBM, BytePerPage, PagePerBlk, TotalBlkCnt, SpareSize, PlaneSelect { "DS35Q1GA", 0xE5, 0x71, 0x00, 2, 2048, 64, 1024, 64, -1/* not use */}, { "DS35Q2GA", 0xE5, 0x72, 0x00, 2, 2048, 64, 2048, 64, 0}, { "GD5F2GM7", 0xc8, 0x92, 0x00, 2, 2048, 64, 2048, 64, -1}, }; static snf_chip_info g_snf = {0}; static unsigned int pagebuf[MAX_PAGE_SIZE/4]; static void UInt2str(u32 number, char *str) { int i=0, j; int temp; char buf[20]; do { temp = number % 10; buf[i] = temp + 48; i++; number = number / 10; } while (number != 0); buf[i]='\0'; for (j=0; jrpb);//set plane select bit. } SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_NAND_PAGE_READ; rSPI_DR = (page >> 16) & 0xFF; rSPI_DR = (page >> 8) & 0xFF; rSPI_DR = page & 0xFF; SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } void SpiNandStorePage(u32 page) { if (g_snf.ps) { int block = page / g_snf.PagePerBlk; if (block % 2) page |= (1 << g_snf.ps->peb); } SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_NAND_PROGRAM_EXEC; rSPI_DR = (page >> 16) & 0xFF; rSPI_DR = (page >> 8) & 0xFF; rSPI_DR = page & 0xFF; SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); } int SpiNandReadCache(u32 page, u32 offset, int length, u8 *read_buf) { int i; u8 val = 0; u32 addr = (offset & 0xFFF); (void)val; if (g_snf.ps) { int block = page / g_snf.PagePerBlk; if (block % 2) addr |= (1 << g_snf.ps->rcb); } SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_NAND_READ_CACHE; rSPI_DR = (addr >> 8) & 0xFF; rSPI_DR = addr & 0xFF; rSPI_DR = 0x0; //dummy byte. for (i = 0; i < 4; i++) { while (!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); val = rSPI_DR; } for (i = 0; i < length; i++) { rSPI_DR = 0; while (!(rSPI_SR & SPI_RXFIFO_NOTEMPTY)); read_buf[i] = rSPI_DR; } SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); return SNF_OK; } static int SpiNandReadPageSpare(u32 page, char *buf) { int status; int ecc_status; int page_size = g_snf.BytePerPage; int spare_size = g_snf.SprSize; SpiNandLoadPage(page); status = SpiNandWaitTillReady(); if (status < 0) return status; ecc_status = (status >> SPI_NAND_STATUS_REG_ECC_SHIFT) & SPI_NAND_STATUS_REG_ECC_MASK; if (ecc_status == SPI_NAND_ECC_UNCORR) { PrintVariableValueHex("SpiNandReadPageSpare ECC error, status:", status); SNF_Print("pagenum:", __func__, page); return -SNF_BAD_BLK; } return SpiNandReadCache(page, page_size, spare_size, (u8 *)buf); } static int SpiNandStoreCache(u32 page, u32 offset, int length, u8 *write_buf) { int i; u32 addr = (offset & 0xFFF); if (g_snf.ps) { int block = page / g_snf.PagePerBlk; if (block % 2) addr |= (1 << g_snf.ps->plb); //set read cache plane select bit. } SetSpiDataMode(8); SetCSGpioEnable(1); rSPI_DR = SPI_NAND_PROGRAM_LOAD; rSPI_DR = (addr >> 8) & 0xFF; rSPI_DR = addr & 0xFF; for (i = 0; i < length; i++) { while (!(rSPI_SR & SPI_TXFIFO_NOTFULL)); rSPI_DR = write_buf[i]; } SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); return SNF_OK; } static int SpiNandEraseBlock(int block) { u32 page_offset; int status; int timeout = 0; retry: SpiNandWriteEnable(); SetSpiDataMode(8); SetCSGpioEnable(1); page_offset = block * g_snf.PagePerBlk; rSPI_DR = SPI_NAND_BLOCK_ERASE; rSPI_DR = (page_offset >> 16) & 0xff; rSPI_DR = (page_offset >> 8) & 0xff; rSPI_DR = page_offset & 0xff; SpiWaitIdle(); SetCSGpioEnable(0); SetSpiDataMode(32); status = SpiNandWaitTillReady(); if (status < 0) { if (timeout++ < 3) goto retry; SNF_Print("block erase timeout, block:", __func__, block); return status; } if (status & SPI_NAND_STATUS_REG_ERASE_FAIL) { /*find a new bad block. */ SNF_Print("block erase failed, block:", __func__, block); return -SNF_BAD_BLK; } return SNF_OK; } /* read a few bytes in one page */ static int SpiNandReadBytesInPage(u32 page, u32 offset, u8 *data, u32 size) { snf_chip_info *chip = &g_snf; int status; int ecc_status; SpiNandLoadPage(page); status = SpiNandWaitTillReady(); if (status < 0) { SNF_Print2("timeout, block:", "page", __func__, page / chip->PagePerBlk, page % chip->PagePerBlk); return status; } ecc_status = (status >> SPI_NAND_STATUS_REG_ECC_SHIFT) & SPI_NAND_STATUS_REG_ECC_MASK; if (ecc_status == SPI_NAND_ECC_UNCORR) //ECC error meains bad block. { SNF_Print2("ECC error blk:", "page:", __func__, page / chip->PagePerBlk, page % chip->PagePerBlk); return -SNF_ERR;//-SNF_BAD_BLK; } return SpiNandReadCache(page, offset, size, data); } static int SpiNandReadOnePage(u32 page, u8 *data) { return SpiNandReadBytesInPage(page, 0, data, g_snf.BytePerPage); } /* write a few bytes in one page */ static int SpiNandWriteBytesInPage(u32 page, u32 offset, u8 *data, u32 size) { int timeout = 0; int ret; retry: SpiNandWriteEnable(); SpiNandStoreCache(page, offset, size, data); SpiNandStorePage(page); ret = SpiNandWaitTillReady(); if (ret < 0) { if (timeout++ < 3) goto retry; } else if (ret & SPI_NAND_STATUS_REG_PROG_FAIL) { return -SNF_BAD_BLK; } return ret; } static int SpiNandWriteOnePage(u32 page, u8 *data) { return SpiNandWriteBytesInPage(page, 0, data, g_snf.BytePerPage); } static int SpiNandReadPhysicalBlock(u32 block , u32 offset_in_block, u8 *data, u32 size) { snf_chip_info *chip = &g_snf; u8 *pdata = data; u32 currpage, BytePerPage; u32 offset_in_page; u32 read_size; int remain = size; int ret; BytePerPage = chip->BytePerPage; currpage = block * chip->PagePerBlk + offset_in_block / BytePerPage; offset_in_page = offset_in_block % BytePerPage; while (remain > 0) { read_size = ((BytePerPage - offset_in_page) >= remain) ? remain : (BytePerPage - offset_in_page); ret = SpiNandReadBytesInPage(currpage, offset_in_page, pdata, read_size); if (ret != SNF_OK) { SNF_Print2("read page falied, page:", "block:", __func__, currpage, block); break; } if (offset_in_page) offset_in_page = 0; currpage++; remain -= read_size; pdata += read_size; } return ret; } static int SpiNandReadInBlock(u32 block , u32 offset_in_block, u8 *data, u32 size) { snf_chip_info *chip = &g_snf; u32 blk = block; int ret; //bad block check and replace. if (chip->bbt.node[block].status == SNF_BLK_BAD) { blk = chip->bbt.node[block].replace; ret = SpiNandCheckReplaceBlockValid(blk); if ((ret == SNF_OK) && (chip->bbt.node[blk].status != SNF_BLK_BAD)) { //find replace block. if (chip->bbt.node[blk].status != SNF_BLK_USED) { SNF_Print2("block:", "replace blk:", __func__, block, blk); chip->bbt.node[blk].status = SNF_BLK_USED; } } else { SNF_Print2("bad block:", "invalid replace blk:", __func__, block, blk); return -SNF_BAD_BLK; } } return SpiNandReadPhysicalBlock(blk, offset_in_block, data, size); } static int SpiNandReadData(u32 addr, void *data, int size) { snf_chip_info *chip = &g_snf; u8 *pdata; u32 read_size, bytePerBlk; u32 currblk,offset_in_block; int remain; int ret = SNF_OK; pdata = (u8 *)data; remain = size; bytePerBlk = chip->BytePerBlk; currblk = addr / bytePerBlk; offset_in_block = addr % bytePerBlk; //area check valid. if ((addr + size) > chip->AvailableCapacity) { SNF_PrintString("burn out of boundry.\n", __func__); return -SNF_ERR; } while (remain > 0) { //read block. read_size = ((bytePerBlk - offset_in_block) >= remain) ? remain : (bytePerBlk - offset_in_block); ret = SpiNandReadInBlock(currblk, offset_in_block, pdata, read_size); if (ret != SNF_OK) { SNF_Print("SpiNandReadInBlock failed, block:", __func__, currblk); break; } if (offset_in_block) offset_in_block = 0; currblk++; pdata += read_size; remain -= read_size; if (remain < 0) { ret = -SNF_ERR; SNF_PrintString("Invalid remain.\n", __func__); break; } } return ret; } /* offset is at least align to SECOTR_SIZE */ static int SpiNandBurnPage(int page, u8 *buf) { int ret; ret = SpiNandWriteOnePage(page, buf); if (ret == SNF_OK) { u32 *tmp = (u32 *)buf; int wordPerPage = g_snf.BytePerPage / 4; int i; ret = SpiNandReadOnePage(page, (u8 *)pagebuf); if (ret == SNF_OK) { for (i = 0; i < wordPerPage; i++) { if (tmp[i] != pagebuf[i]) { SNF_Print("burn error, pos:", __func__, i); PrintVariableValueHex("write:", tmp[i]); PrintVariableValueHex("read:", pagebuf[i]); return -SNF_ERR; } } } else { SNF_Print("read page failed, page:", __func__, page); return -SNF_ERR; //read page should not create a bad block. } } else { SNF_Print("SpiNandWriteOnePage failed, page:", __func__, page); return ret; } return SNF_OK; } static int SpiNandBurnBlock(u32 block, u32 offset_in_block, const u8 *data, u32 size) { snf_chip_info *chip = &g_snf; u8 *buf = (u8 *)data; u32 left_size = size; u8 pageBuf[MAX_PAGE_SIZE]; u32 byte_per_page; u32 start_page; u32 whole_page_num; u32 result; u32 i; byte_per_page = chip->BytePerPage; start_page = block * chip->PagePerBlk + offset_in_block / byte_per_page; if (offset_in_block % byte_per_page) { SNF_PrintString("offset is not align with page.\n", __func__); return -SNF_ERR; } if (left_size > 0) { whole_page_num = left_size / byte_per_page; for (i=0; i 0) { memset(pageBuf + left_size, 0xff, MAX_PAGE_SIZE - left_size); memcpy(pageBuf, buf, left_size); result = SpiNandBurnPage(start_page, pageBuf); if (result != SNF_OK) { SNF_Print2("Burn page failed, page:", "result:", __func__, start_page, result); return result; } } return result; } static int SpiNandEraseWriteInBlock(u32 block, u32 offset_in_block, void *data, u32 size) { snf_chip_info *chip = &g_snf; int ret; u32 blk = block; u32 bbtUpdate = 0; //bad block check and replace. if (chip->bbt.node[block].status == SNF_BLK_BAD) { blk = chip->bbt.node[block].replace; ret = SpiNandCheckReplaceBlockValid(blk); if ((ret == SNF_OK) && (chip->bbt.node[blk].status != SNF_BLK_BAD)) { //find replace block. if (chip->bbt.node[blk].status != SNF_BLK_USED) { SNF_Print2("block:", "replace blk:", __func__, block, blk); chip->bbt.node[blk].status = SNF_BLK_USED; } } else { retry: //find a new replace block. ret = SpiNandFindAReplaceBlock(chip, &blk); if (ret != SNF_OK) { SNF_PrintString("SpiNandFindAReplaceBlock failed, no replace block.\n", __func__); goto exit; } SNF_Print2("block:","find a new replace blk:", __func__, block, blk); chip->bbt.node[block].replace = blk; bbtUpdate = 1; } } if (offset_in_block == 0) { ret = SpiNandEraseBlock(blk);//phy block if (ret != SNF_OK) { SNF_Print("SpiNandEraseBlock failed, mask bad block and try to find a replace block, blk:", __func__, blk); chip->bbt.node[blk].status = SNF_BLK_BAD; chip->bbt.bad_blocks++; bbtUpdate = 1; goto retry; } } ret = SpiNandBurnBlock(blk, offset_in_block, data, size);//phy block if (ret != SNF_OK) { if ((ret == -SNF_BAD_BLK) && (offset_in_block == 0)) { SNF_Print("SpiNandWriteData failed, try to find replace block, blk:", __func__, blk); chip->bbt.node[blk].status = SNF_BLK_BAD; chip->bbt.bad_blocks++; bbtUpdate = 1; goto retry; } else { SNF_Print("SpiNandWriteData failed, blk:", __func__, blk); } } exit: if (bbtUpdate) SpiNandUpdateBbt(); return ret; } static int SpiNandCheckReplaceBlockValid(u32 block) { snf_chip_info *chip = &g_snf; if ((block >= chip->AvailableBlks) && (block < chip->BbtBlkOffset)) { return SNF_OK; } return -SNF_ERR; } static int SpiNandCheckPhysicalBadBlock(u32 blknum) { int page = blknum * g_snf.PagePerBlk; int bbm = g_snf.BBMType; char spareBuf[MAX_SPARE_SIZE] = {0}; int ret; ret = SpiNandReadPageSpare(page, spareBuf); if (ret != SNF_OK) { SNF_Print("SpiNandReadPageSpare page0 failed, ret:", __func__, ret); goto exit; } if (spareBuf[0] == 0xFF) { if(bbm == 2) { ret = SpiNandReadPageSpare(page+1, spareBuf); if (ret != SNF_OK) { SNF_Print("SpiNandReadPageSpare page1 failed, ret:", __func__, ret); goto exit; } if (spareBuf[0] == 0xFF) { //SNF_Print("good block:", __func__, blknum); return SNF_OK; } else { SNF_Print("bad block:", __func__, blknum); } } } exit: SNF_Print("bad block:", __func__, blknum); return -SNF_BAD_BLK; } static int SpiNandFindAReplaceBlock(snf_chip_info *chip, u32 *block) { int i; u32 replace_blk_start = chip->AvailableBlks; u32 replace_blk_stop = chip->AvailableBlks + chip->ReplaceBlks; for (i=replace_blk_stop-1; i>=replace_blk_start; i--) { if (chip->bbt.node[i].status == SNF_BLK_GOOD) { *block = i; chip->bbt.node[i].status = SNF_BLK_USED; return SNF_OK; } } SNF_PrintString("there are no valid replace blocks.\n", __func__); return -SNF_ERR; } static int SpiNandWriteBbt(u8 *buf , u32 size) { snf_chip_info *chip = &g_snf; int curr_block; int i, ret; int bbt_bad_blocks = 0; int write_block_num = 0; for(i=0; iBbtBlkOffset + i; if(write_block_num >= 2) { break; } if(chip->bbt.node[curr_block].status == SNF_BLK_BAD) { bbt_bad_blocks++; continue; } ret = SpiNandEraseBlock(curr_block); if (ret != SNF_OK) { SNF_Print2("erase block failed, block: ", "ret:", __func__, curr_block, ret); if (ret == -SNF_BAD_BLK) { chip->bbt.node[curr_block].status = SNF_BLK_BAD; chip->bbt.bad_blocks++; bbt_bad_blocks++; continue; } break; } ret = SpiNandBurnBlock(curr_block, 0, buf, size); if (ret != SNF_OK) { SNF_Print2("burn block failed, block: ", "ret:", __func__, curr_block, ret); if (ret == -SNF_BAD_BLK) { chip->bbt.node[curr_block].status = SNF_BLK_BAD; chip->bbt.bad_blocks++; bbt_bad_blocks++; continue; } break; } write_block_num++; } if (bbt_bad_blocks) { SNF_Print("%s, bbt has bad blcok: ", __func__, bbt_bad_blocks); } return ret; } static int SpiNandReadBbt(u8* buf, u32 size) { snf_chip_info *chip = &g_snf; u32 crc_size; u32 checksum; int curr_block; int ret, i; crc_size = size - SNF_BBT_HEADER_SIZE - sizeof(chip->bbt.bbt_crc); for(i=0; iBbtBlkOffset + i; ret = SpiNandReadPhysicalBlock(curr_block, 0, buf, size); if (ret != SNF_OK) { SNF_Print("SpiNandReadPhysicalBlock failed, block:", __func__, curr_block); continue; } if (memcmp(chip->bbt.header_mask, SNF_BBT_HEADER_MASK, sizeof(SNF_BBT_HEADER_MASK))) { SNF_Print("Not fond BBT header, block:", __func__, curr_block); ret = -SNF_ERR; continue; } checksum = xcrc32(buf + SNF_BBT_HEADER_SIZE, crc_size, 0xFFFFFFFF); if (chip->bbt.bbt_crc != checksum) { SNF_Print("BBT information is abnormal, block:", __func__, curr_block); ret = -SNF_ERR; continue; } break; } return ret; } static int SpiNandUpdateBbt(void) { snf_chip_info *chip = &g_snf; int ret = SNF_OK; u32 bbt_size, crc_size; u8 *pbuf; pbuf = (u8 *)&chip->bbt; bbt_size = sizeof(snf_bbt); crc_size = bbt_size - SNF_BBT_HEADER_SIZE - sizeof(chip->bbt.bbt_crc); chip->bbt.bbt_crc = xcrc32(pbuf + SNF_BBT_HEADER_SIZE, crc_size, 0xFFFFFFFF); ret = SpiNandWriteBbt(pbuf, bbt_size); if (ret != SNF_OK) { SNF_Print("write bbt failed, ret:", __func__, ret); } return ret; } static int SpiNandBbtInit(void) { snf_chip_info *chip = &g_snf; int ret = 0; u32 bbt_size; u8 *pbuf; int i; pbuf = (u8 *)&chip->bbt; bbt_size = sizeof(snf_bbt); ret = SpiNandReadBbt(pbuf, bbt_size); if (ret != SNF_OK) { memset(&chip->bbt, 0, bbt_size); SNF_PrintString("Create a new BBT.\n", __func__); for (i=0; iTotalBlk; i++) { ret = SpiNandCheckPhysicalBadBlock(i); if (ret == SNF_OK) { chip->bbt.node[i].status = SNF_BLK_GOOD; } else { chip->bbt.node[i].status = SNF_BLK_BAD; chip->bbt.bad_blocks++; } chip->bbt.node[i].replace = 0; } memcpy(chip->bbt.header_mask, SNF_BBT_HEADER_MASK, sizeof(SNF_BBT_HEADER_MASK)); SpiNandUpdateBbt(); ret = SNF_OK; } return ret; } static int SpiNandSetAttribute(snf_chip_info *chip) { spi_nand_cfg *cfg = g_SnfChipCfgArray; int fond = 0; int i; if (!chip) { SNF_PrintString("SpiNandSetAttribute: Invalid chip.\n", __func__); return -SNF_ERR; } for (i=0; iMID == cfg[i].MID) && (chip->DID0 == cfg[i].DID0)) { fond = 1; break; } } if (fond) { memcpy(chip->Name, cfg[i].Name, MAX_SNF_BUF_SIZE); chip->BBMType = cfg[i].BBMType; chip->BytePerPage = cfg[i].BytePerPage; chip->PagePerBlk = cfg[i].PagePerBlk; chip->BytePerBlk = chip->BytePerPage * chip->PagePerBlk; chip->TotalBlk = cfg[i].TotalBlk; chip->SprSize = cfg[i].SprSize; chip->FtlSprSize = 24; //reserved. chip->Capacity = chip->BytePerBlk * chip->TotalBlk; chip->ReplaceBlks = chip->TotalBlk * SNF_REPLACE_BLK_PERCENT / 100; chip->AvailableBlks = chip->TotalBlk - chip->ReplaceBlks - SNF_BBT_BLK_COUNT; chip->AvailableCapacity = chip->AvailableBlks * chip->BytePerBlk; chip->BbtBlkOffset = chip->TotalBlk - SNF_BBT_BLK_COUNT; if ((cfg[i].PsArrayIndex < 0) || (cfg[i].PsArrayIndex >= sizeof(g_SnfPsArray)/sizeof(g_SnfPsArray[0]))) { chip->ps = NULL; } else { chip->ps = &g_SnfPsArray[cfg[i].PsArrayIndex]; } } else { SNF_PrintString("Not find spi nand flash.\n", __func__); return -SNF_ERR; } if (chip->BytePerPage > MAX_PAGE_SIZE) { SNF_Print("Invalid MAX_PAGE_SIZE:", __func__, MAX_PAGE_SIZE); return -SNF_ERR; } if (chip->SprSize > MAX_SPARE_SIZE) { SNF_Print("Invalid MAX_SPARE_SIZE:", __func__, MAX_SPARE_SIZE); return -SNF_ERR; } if (chip->TotalBlk > MAX_BLK_SIZE) { SNF_Print("Invalid MAX_BLK_SIZE:", __func__, MAX_BLK_SIZE); return -SNF_ERR; } return SNF_OK; } int SpiInit(void) { snf_chip_info *snf = &g_snf; int ret; memset(snf, 0, sizeof(snf_chip_info)); /* spi controler init */ SpiInitialize(); /* spi nand init */ SpinandSoftreset(); SpiNandWaitTillReady(); SpiNandReadId(); ret = SpiNandSetAttribute(snf); if (ret != SNF_OK) { SNF_PrintString("Not find spi nand flash.\n", __func__); return ret; } SpiNandWriteReg(SPI_NAND_LOCK_REG, SPI_NAND_PROT_UNLOCK_ALL); SpiNandEnableECC(); SpiNandDisableQuad(); //SpiNandWriteEnable(); ret = SpiNandBbtInit(); if (ret != SNF_OK) SNF_PrintString("SpiNandLoderBbt failed.\n", __func__); return ret; } static void SpiLoadStepldr(void) { UINT32 *buf = (UINT32*)STEPLDR_ENTRY; UINT32 offset; UINT32 size; int ret; int count = 3; retry: if (ReadSysInfo()) { SNF_PrintString("read sysinfo fail, try to load stepldr part A.\n", __func__); offset = STEPLDRA_OFFSET; size = STEPLDR_MAX_SIZE; } else { SysInfo *sysinfo = GetSysInfo(); offset = sysinfo->stepldr_offset; size = sysinfo->stepldr_size; } PrintVariableValueHex("stepldr offset", offset); ret = SpiNandReadData(offset, buf, size); if (ret != SNF_OK) { if (--count) { goto retry; } else { SNF_PrintString("SpiNandReadData failed.\n", __func__); } } #ifdef MMU_ENABLE CP15_clean_dcache_for_dma(STEPLDR_ENTRY, STEPLDR_ENTRY + size); #endif } void bootFromSPI(void) { void (*funPtr)(void); SpiLoadStepldr(); funPtr = (void (*)(void))STEPLDR_ENTRY; funPtr(); } /* offset is at least align to PAGE_SIZE */ static int SpiNandBurn(void *buf, u32 offset, u32 size) { snf_chip_info *chip = &g_snf; u32 currblk, burn_size; u32 bytePerBlk; u32 offset_in_block; int remain; int ret = SNF_OK; u8 *pbuf; pbuf = (u8 *)buf; remain = size; bytePerBlk = chip->BytePerBlk; currblk = offset / bytePerBlk; offset_in_block = offset % bytePerBlk; //area check valid. if ((offset + size) > chip->AvailableCapacity) { SNF_PrintString("burn out of boundry.\n", __func__); return -SNF_ERR; } //offset check align if (offset % chip->BytePerPage) { SNF_PrintString("offset is not align with page.\n", __func__); return -SNF_ERR; } while (remain > 0) { burn_size = ((bytePerBlk - offset_in_block) >= remain) ? remain : (bytePerBlk - offset_in_block); ret = SpiNandEraseWriteInBlock(currblk, offset_in_block, pbuf, burn_size);//logic block if (ret != SNF_OK) { SNF_Print("erase write block failed, block: ", __func__, currblk); break; } if (offset_in_block) offset_in_block = 0; currblk++; pbuf += burn_size; remain -= burn_size; if (remain < 0) { ret = -SNF_ERR; SNF_PrintString("Invalid remain.\n", __func__); break; } } SpiNandWriteDisable(); return ret; } int FlashBurn(void *buf, u32 offset, u32 size) { return SpiNandBurn(buf, offset, size); } int SpiReadSysInfo(SysInfo *info) { u32 checksum; u32 calc_checksum; u8 data[MAX_PAGE_SIZE]; int ret; ret = SpiNandReadData(SYSINFOA_OFFSET, data, sizeof(SysInfo)); if (ret != SNF_OK) { SNF_Print("SYSINFOA_OFFSET read failed, ret:", __func__, ret); } checksum = *(u32*)((u32)data + sizeof(SysInfo) - 4); calc_checksum = xcrc32((unsigned char*)data, sizeof(SysInfo) - 4, 0xffffffff); if (calc_checksum == checksum) { memcpy(info, data, sizeof(SysInfo)); return 0; } ret = SpiNandReadData(SYSINFOB_OFFSET, data, sizeof(SysInfo)); if (ret != SNF_OK) { SNF_Print("SYSINFOB_OFFSET read failed, ret:", __func__, ret); } checksum = *(u32*)((u32)data + sizeof(SysInfo) - 4); calc_checksum = xcrc32((unsigned char*)data, sizeof(SysInfo) - 4, 0xffffffff); if (calc_checksum == checksum) { memcpy(info, data, sizeof(SysInfo)); return 0; } return -1; } void SpiWriteSysInfo(SysInfo *info) { SpiNandBurn(info, SYSINFOB_OFFSET, sizeof(SysInfo)); SpiNandBurn(info, SYSINFOA_OFFSET, sizeof(SysInfo)); } #endif