Files
MAX_CARLINK_A270S/A58-AMTLDR/Src/SpinandBooter.c
2025-05-17 14:22:08 +08:00

1394 lines
30 KiB
C

#include <stdio.h>
#include <string.h>
#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; j<i; j++) {
str[j] = buf[i-j-1];
}
str[i]='\0';
}
static void SNF_PrintString(char *variable, char const *func)
{
SendUartString((char *)func);
SendUartString(": ");
SendUartString(variable);
}
static void SNF_Print(char *variable, char const *func, int value)
{
char buf[20];
SendUartString((char *)func);
SendUartString(": ");
SendUartString(variable);
if (value < 0) {
SendUartString("-");
value = -value;
}
UInt2str(value, buf);
SendUartString(buf);
SendUartString("\n");
}
static void SNF_Print2(char *variable1, char *variable2, char const *func, int val1, int val2)
{
char buf[20];
SendUartString((char *)func);
SendUartString(": ");
//var1
SendUartString(variable1);
if (val1 < 0) {
SendUartString("-");
val1 = -val1;
}
UInt2str(val1, buf);
SendUartString(buf);
SendUartString(", ");
//var2
SendUartString(variable2);
if (val2 < 0) {
SendUartString("-");
val2 = -val2;
}
UInt2str(val2, buf);
SendUartString(buf);
SendUartString("\n");
}
static void delay(u32 time)
{
volatile u32 count = time;
while (count--);
}
static void SetCSGpioEnable(int enable)
{
gpio_direction_output(SPI_CS_GPIO, !enable);
}
static void SpiWaitIdle(void)
{
while (rSPI_SR & SPI_BUSY);
udelay(1);
}
#if 0
static void SpiEmptyRxFIFO(void)
{
int data = 0;
(void)data;
while(rSPI_SR & SPI_RXFIFO_NOTEMPTY)
data = rSPI_DR;
}
#endif
static void SetSpiDataMode(u32 bitMode)
{
u32 val = 0;
SpiWaitIdle();;
rSPI_SSIENR = 0;
val = rSPI_CTLR0;
val &=~(0x1f<<16);
val |=((bitMode-1)<<16);
rSPI_CTLR0 = val;
rSPI_SSIENR = 1;
}
static void SpiSelectPad(void)
{
u32 val;
/* pad config */
val = rSYS_PAD_CTRL02;
val &= ~0xfff;
val |= (0x1 << 10)|(0x1 << 8)|(0x1 << 6)|(0x1 << 4)| (0x1 << 2)/* | (1<<0)*/;
rSYS_PAD_CTRL02 = val;
/* clock config */
val = rSYS_SSP_CLK_CFG;
val |= (0x1<<31) | (0x1<<30);
rSYS_SSP_CLK_CFG = val;
//cs inactive first
SetCSGpioEnable(0);
}
static void SpiSoftReset(void)
{
u32 val;
val = rSYS_SOFT_RST;
val &= ~(0x1<<8);
rSYS_SOFT_RST = val;
udelay(10);
val |= (0x1<<8);
rSYS_SOFT_RST = val;
}
static void SpiInitialize()
{
SpiSelectPad();
SpiSoftReset();
rSPI_SSIENR = 0;
rSPI_CTLR0 = 0;
rSPI_CTLR0 |= (0<<21)|(0x1f<<16)|(0x0<<12)|(0x0<<8)|(0x0<<4);
// rSPI_CTLR1 = 63;
rSPI_BAUDR = 4;//2;//16;//2;
rSPI_SER = 1;
rSPI_IMR = 0;
rSPI_SSIENR = 1;
SetCSGpioEnable(0);
}
static void SpinandSoftreset(void)
{
SetSpiDataMode(8);
SetCSGpioEnable(1);
rSPI_DR = SPI_NAND_RESET;
SpiWaitIdle();
SetCSGpioEnable(0);
SetSpiDataMode(32);
}
static void SpiNandReadId(void)
{
u8 val[4];
int i;
SetSpiDataMode(8);
SetCSGpioEnable(1);
rSPI_DR = SPI_NAND_READ_ID;
rSPI_DR = 0;
rSPI_DR = 0;
rSPI_DR = 0;
for (i=0; i<4; i++)
{
while (!(rSPI_SR & SPI_RXFIFO_NOTEMPTY));
val[i] = rSPI_DR;
}
SpiWaitIdle();
SetCSGpioEnable(0);
SetSpiDataMode(32);
/* 3.3V:0xE571; 1.1V:0xE521 */
//PrintVariableValueHex("ManufacturerID", val[2]);
//PrintVariableValueHex("DeviceID", val[3]);
g_snf.MID = val[2];
g_snf.DID0 = val[3];
}
static void SpiNandWriteReg(u8 opcode, u8 val)
{
SetSpiDataMode(8);
SetCSGpioEnable(1);
rSPI_DR = SPI_NAND_SET_FEATURE;
rSPI_DR = opcode;
rSPI_DR = val;
SpiWaitIdle();
SetCSGpioEnable(0);
SetSpiDataMode(32);
}
static u8 SpiNandReadReg(u8 opcode)
{
u8 val;
int i;
SetSpiDataMode(8);
SetCSGpioEnable(1);
rSPI_DR = SPI_NAND_GET_FEATURE;
rSPI_DR = opcode;
rSPI_DR = 0;
for (i = 0; i < 3; i++)
{
while (!(rSPI_SR & SPI_RXFIFO_NOTEMPTY));
val = rSPI_DR;
}
SpiWaitIdle();
SetCSGpioEnable(0);
SetSpiDataMode(32);
return val;
}
static void SpiNandWriteEnable(void)
{
SetSpiDataMode(8);
SetCSGpioEnable(1);
rSPI_DR = SPI_NAND_WRITE_ENABLE;
SpiWaitIdle();
SetCSGpioEnable(0);
SetSpiDataMode(32);
}
static void SpiNandWriteDisable(void)
{
SetSpiDataMode(8);
SetCSGpioEnable(1);
rSPI_DR = SPI_NAND_WRITE_DISABLE;
SpiWaitIdle();
SetCSGpioEnable(0);
SetSpiDataMode(32);
}
static int SpiNandWaitTillReady(void)
{
int timeout = 100;
u8 status;
while (timeout--)
{
status = SpiNandReadReg(SPI_NAND_STATUS_REG);
//printk("status=0x%x.\n", status);
if (!(status & SPI_NAND_STATUS_REG_BUSY))
return status;
delay(1000);
}
return -SNF_TIMEOUT;
}
static int SpiNandEnableECC(void)
{
u8 status;
status = SpiNandReadReg(SPI_NAND_FEATURE_REG);
status |= SPI_NAND_ECC_EN;
SpiNandWriteReg(SPI_NAND_FEATURE_REG, status);
return SNF_OK;
}
static int SpiNandDisableQuad(void)
{
u8 status;
status = SpiNandReadReg(SPI_NAND_FEATURE_REG);
status &= ~SPI_NAND_QUAD_EN;
SpiNandWriteReg(SPI_NAND_FEATURE_REG, status);
return SNF_OK;
}
void SpiNandLoadPage(u32 page)
{
if (g_snf.ps) {
int block = page / g_snf.PagePerBlk;
if (block % 2)
page |= (1 << g_snf.ps->rpb);//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<whole_page_num; i++) {
result = SpiNandBurnPage(start_page, buf);
if (result != SNF_OK) {
SNF_Print2("Burn page failed, page:", "result:", __func__, start_page, result);
return result;
}
buf += byte_per_page;
left_size -= byte_per_page;
start_page++;
}
}
if (left_size > 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; i<SNF_BBT_BLK_COUNT; i++) {
curr_block = chip->BbtBlkOffset + 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; i<SNF_BBT_BLK_COUNT; i++) {
curr_block = chip->BbtBlkOffset + 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; i<chip->TotalBlk; 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; i<sizeof(g_SnfChipCfgArray)/sizeof(spi_nand_cfg); i++) {
if ((chip->MID == 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