1617 lines
36 KiB
C
1617 lines
36 KiB
C
#include <stdio.h>
|
||
#include <string.h>
|
||
#include "typedef.h"
|
||
#include "amt630h.h"
|
||
#include "uart.h"
|
||
#include "timer.h"
|
||
#include "gpio.h"
|
||
#include "cp15.h"
|
||
#include "sysinfo.h"
|
||
#include "crc32.h"
|
||
#include "update.h"
|
||
#include "board.h"
|
||
#include "os_adapt.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);
|
||
int SpiNandBurn(void *buf, u32 offset, u32 size, int show_progress);
|
||
|
||
/* 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 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);
|
||
|
||
printf("ManufacturerID:0x%x, DeviceID:0x%x.\n", val[2], 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;
|
||
}
|
||
|
||
#ifdef SPI0_QSPI_MODE
|
||
static int SpiNandEnableQuad(void)
|
||
{
|
||
u8 status;
|
||
|
||
status = SpiNandReadReg(SPI_NAND_FEATURE_REG);
|
||
status |= SPI_NAND_QUAD_EN;
|
||
SpiNandWriteReg(SPI_NAND_FEATURE_REG, status);
|
||
|
||
return SNF_OK;
|
||
}
|
||
#else
|
||
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;
|
||
}
|
||
#endif
|
||
|
||
static 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);
|
||
}
|
||
|
||
static void SpiNandStorePage(u32 page)
|
||
{
|
||
if (g_snf.ps) {
|
||
int block = page / g_snf.PagePerBlk;
|
||
if (block % 2)
|
||
page |= (1 << g_snf.ps->peb); //set plane select bit.
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
#ifdef SPI0_QSPI_MODE
|
||
//********format*********
|
||
//00 – Standard SPI Format
|
||
// 01 – Dual SPI Format
|
||
// 10 – Quad SPI Format
|
||
// 11 – Reserved
|
||
//********tmod**********
|
||
//00 –- Transmit & Receive
|
||
//01 –- Transmit Only
|
||
//10 –- Receive Only
|
||
//11 –- EEPROM Read
|
||
static void SpiSetFrameFormatMode(u8 format, u8 tmod)
|
||
{
|
||
u32 val = 0;
|
||
|
||
while ((rSPI_SR & SPI_BUSY));
|
||
rSPI_SSIENR = 0;
|
||
val = rSPI_CTLR0;
|
||
val &= ~((0x3 << 21) | (0x3 << 8));
|
||
val |= ((format << 21) | (tmod << 8));
|
||
rSPI_CTLR0 = val;
|
||
rSPI_SSIENR = 1;
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* func:
|
||
* read data from page cache.
|
||
* param:
|
||
* offset: start offset in page cache.
|
||
* len: the len of data.
|
||
* buf: write data.
|
||
*/
|
||
static int SpiNandReadCache(u32 page, u32 offset, int len, u8 *buf)
|
||
{
|
||
int left_size = len;
|
||
int block = page / g_snf.PagePerBlk;
|
||
u32 addr;
|
||
int i;
|
||
|
||
#ifdef SPI0_QSPI_MODE
|
||
//Quad mode.
|
||
if (len / 4){
|
||
addr = (offset & 0xFFFF); //3dummy bits(0) + 1bit plane select+ 12bits address.
|
||
if (g_snf.ps) {
|
||
if (block % 2)
|
||
addr |= (1 << g_snf.ps->rcb); //set read cache plane select bit.
|
||
}
|
||
addr = (addr << 8) | 0x0;
|
||
addr = ((addr >> 16) & 0xff) | (((addr >> 8) & 0xff) << 8) | ((addr & 0xff) << 16);
|
||
|
||
SpiSetFrameFormatMode(2, 2);
|
||
rSPI_SSIENR = 0;
|
||
rSPI_SPI_CTRLR0 = (0 << 11) | (2 << 8) | (6 << 2) | 0;
|
||
rSPI_CTLR1 = len/4 - 1;
|
||
rSPI_SSIENR = 1;
|
||
|
||
SetCSGpioEnable(1);
|
||
rSPI_DR = SPI_NAND_READ_CACHE_X4;
|
||
rSPI_DR = addr;
|
||
|
||
for (i = 0; i < len/4; i++)
|
||
{
|
||
while (!(rSPI_SR & SPI_RXFIFO_NOTEMPTY));
|
||
((u32 *)buf)[i] = rSPI_DR;
|
||
}
|
||
|
||
SpiWaitIdle();
|
||
SetCSGpioEnable(0);
|
||
rSPI_SSIENR = 0;
|
||
rSPI_SPI_CTRLR0 = 0;
|
||
rSPI_CTLR1 = 0;
|
||
rSPI_SSIENR = 1;
|
||
SpiSetFrameFormatMode(0, 0);
|
||
|
||
left_size = len % 4;
|
||
offset += len - left_size;
|
||
buf += len - left_size;
|
||
}
|
||
#endif
|
||
|
||
//Standerd mode.
|
||
if (left_size){
|
||
addr = (offset & 0xFFFF); //3bits dummy(0) + 1bit plane select + 12bits address.
|
||
if (g_snf.ps) {
|
||
if (block % 2)
|
||
addr |= (1 << g_snf.ps->rcb); //set read cache plane select bit.
|
||
}
|
||
|
||
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));
|
||
*buf = rSPI_DR; //Drop this value.
|
||
}
|
||
|
||
for (i = 0; i < left_size; i++)
|
||
{
|
||
rSPI_DR = 0;
|
||
while (!(rSPI_SR & SPI_RXFIFO_NOTEMPTY));
|
||
*buf++ = rSPI_DR;
|
||
}
|
||
|
||
SpiWaitIdle();
|
||
SetCSGpioEnable(0);
|
||
SetSpiDataMode(32);
|
||
}
|
||
|
||
return SNF_OK;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* read spare area of a page.
|
||
* param:
|
||
* page: page index(0~n).
|
||
* buf: read buffer.
|
||
*/
|
||
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)
|
||
{
|
||
//printf("%s, Bit errors greater than ECC capability(4 bits) and can not be corrected.\n", __func__);
|
||
printf("###ERR: %s (blk:%d, page:%d) ECC error, status:0x%x.\n", __func__, page / g_snf.PagePerBlk, page % g_snf.PagePerBlk, status);
|
||
return -SNF_BAD_BLK;
|
||
}
|
||
return SpiNandReadCache(page, page_size, spare_size, (u8 *)buf);
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* write data to page cache.
|
||
* param:
|
||
* offset: start offset in cache.
|
||
* len: the len of data.
|
||
* buf: write data.
|
||
*/
|
||
static int SpiNandStoreCache(u32 page, u32 offset, int len, u8 *buf)
|
||
{
|
||
int block = page / g_snf.PagePerBlk;
|
||
u32 addr;
|
||
int i;
|
||
|
||
addr = (offset & 0xFFFF); //3bits dummy + 1bit plane select + 12bits address.
|
||
if (g_snf.ps) {
|
||
if (block % 2)
|
||
addr |= (1 << g_snf.ps->plb); //set read cache plane select bit.
|
||
}
|
||
|
||
#ifdef SPI0_QSPI_MODE
|
||
//Quad mode.
|
||
if (len % 4 == 0){
|
||
addr = ((addr >> 8) & 0xff) | ((addr & 0xff) << 8);
|
||
SpiSetFrameFormatMode(2, 1);
|
||
rSPI_SSIENR = 0;
|
||
rSPI_SPI_CTRLR0 = (0 << 11) | (2 << 8) | (4 << 2) | 0;
|
||
rSPI_CTLR1 = len/4 - 1;
|
||
rSPI_SSIENR = 1;
|
||
SetCSGpioEnable(1);
|
||
rSPI_DR = SPI_NAND_PROGRAM_LOAD4;
|
||
rSPI_DR = addr & 0xFFFF;
|
||
|
||
for (i = 0; i < len/4; i++)
|
||
{
|
||
while (!(rSPI_SR & SPI_TXFIFO_NOTFULL));
|
||
rSPI_DR = ((u32 *)buf)[i];
|
||
}
|
||
|
||
SpiWaitIdle();
|
||
SetCSGpioEnable(0);
|
||
rSPI_SSIENR = 0;
|
||
rSPI_SPI_CTRLR0 = 0;
|
||
rSPI_CTLR1 = 0;
|
||
rSPI_SSIENR = 1;
|
||
SpiSetFrameFormatMode(0, 0);
|
||
return SNF_OK;
|
||
}
|
||
#endif
|
||
|
||
//Standerd mode.
|
||
SetSpiDataMode(8);
|
||
SetCSGpioEnable(1);
|
||
rSPI_DR = SPI_NAND_PROGRAM_LOAD;
|
||
rSPI_DR = (addr >> 8) & 0xFF;
|
||
rSPI_DR = addr & 0xFF;
|
||
for (i = 0; i < len; i++)
|
||
{
|
||
while (!(rSPI_SR & SPI_TXFIFO_NOTFULL));
|
||
rSPI_DR = buf[i];
|
||
}
|
||
SpiWaitIdle();
|
||
SetCSGpioEnable(0);
|
||
SetSpiDataMode(32);
|
||
|
||
return SNF_OK;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* erase one block.
|
||
* param:
|
||
* block: block index.
|
||
* replaced: if this block to be replaced by a good block from BBT when it is a bad block.
|
||
*/
|
||
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;
|
||
printf("%s block%d erase timeout.\n", __func__, block);
|
||
return status;
|
||
}
|
||
|
||
if (status & SPI_NAND_STATUS_REG_ERASE_FAIL) {
|
||
/*find a new bad block. */
|
||
printf("%s block: %d erase failed\n", __func__, block);
|
||
return -SNF_BAD_BLK;
|
||
}
|
||
|
||
return SNF_OK;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* read a few bytes in one page.
|
||
* param:
|
||
* page: the page index in falsh(0 ~ n).
|
||
* offset: read offset form one page.
|
||
* data: read data.
|
||
* size: read data size.
|
||
*/
|
||
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) {
|
||
printf("###ERR: %s timeout, block:%d page:%d.\n", __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.
|
||
{
|
||
printf("###ERR: %s, block:%d, page:%d ECC error, status:0x%x..\n", __func__, page / chip->PagePerBlk, page % chip->PagePerBlk, status);
|
||
return -SNF_ERR;//-SNF_BAD_BLK;
|
||
}
|
||
return SpiNandReadCache(page, offset, size, data);
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* read a whole page.
|
||
* param:
|
||
* page: page index.
|
||
* data: read buffer.
|
||
*/
|
||
static int SpiNandreadOnePage(u32 page, u8 *data)
|
||
{
|
||
return SpiNandReadBytesInPage(page, 0, data, g_snf.BytePerPage);
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* write a few bytes in one page.
|
||
* param:
|
||
* page: the page index in falsh(0 ~ n).
|
||
* offset: write offset form one page.
|
||
* data: write data.
|
||
* size: write data size.
|
||
*/
|
||
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) {
|
||
ret = -SNF_BAD_BLK;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* write a whole page.
|
||
* param:
|
||
* page: page index.
|
||
* data: write data.
|
||
*/
|
||
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) {
|
||
printf("%s read page:%d (blk: %d) failed\n", __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) {
|
||
printf("%s block:%d, replace blk:%d\n", __func__, block, blk);
|
||
chip->bbt.node[blk].status = SNF_BLK_USED;
|
||
}
|
||
} else {
|
||
printf("###ERR: %s, blk:%d is bad block, it's replace block:%d is Invalid.\n", __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) {
|
||
printf("###ERR:%s, 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) {
|
||
printf("###ERR:%s, SpiNandReadInBlock(%d) failed.\n", __func__, currblk);
|
||
break;
|
||
}
|
||
|
||
if (offset_in_block)
|
||
offset_in_block = 0;
|
||
|
||
currblk++;
|
||
pdata += read_size;
|
||
remain -= read_size;
|
||
if (remain < 0) {
|
||
ret = -SNF_ERR;
|
||
printf("###ERR:%s, Invalid remain.\n", __func__);
|
||
break;
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* burn one page data to spi flash.
|
||
* param:
|
||
* page: page index.
|
||
* buf: data buffer(at least PAGE_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;
|
||
u32 pagecheck[MAX_PAGE_SIZE/4];
|
||
int i;
|
||
|
||
ret = SpiNandreadOnePage(page, (u8 *)pagecheck);
|
||
if (ret == SNF_OK) {
|
||
for (i = 0; i < wordPerPage; i++) {
|
||
if (tmp[i] != pagecheck[i]) {
|
||
printf("%s(%d) checksum error, write:0x%x, read:0x%x, pos:%d\n", __func__, page, tmp[i], pagecheck[i], i);
|
||
return -SNF_ERR;
|
||
}
|
||
}
|
||
} else {
|
||
return -SNF_ERR; //read page should not create a bad block.
|
||
}
|
||
} else {
|
||
return ret;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* write random bytes data to flash.
|
||
* param:
|
||
* addr: write start address(must align with page).
|
||
* data: write data.
|
||
* size: write data size(random bytes).
|
||
*/
|
||
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) {
|
||
printf("###ERR:%s, 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) {
|
||
printf("%s Burn page(%d) failed, result:%d.\n", __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) {
|
||
printf("%s Burn the last page(%d) failed, result:%d.\n", __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) {
|
||
printf("%s block:%d, replace blk:%d\n", __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) {
|
||
printf("%s SpiNandFindAReplaceBlock failed, no replace block.\n", __func__);
|
||
goto exit;
|
||
}
|
||
printf("%s block:%d, find a new replace blk:%d\n", __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) {
|
||
printf("###ERR:%s, SpiNandEraseBlock(%d) failed, mask bad block and try to find a replace block.\n", __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)) {
|
||
printf("###ERR:%s, SpiNandBurnBlock %d failed, try to find replace block\n", __func__, blk);
|
||
chip->bbt.node[blk].status = SNF_BLK_BAD;
|
||
chip->bbt.bad_blocks++;
|
||
bbtUpdate = 1;
|
||
goto retry;
|
||
} else {
|
||
printf("###ERR:%s, SpiNandBurnBlock %d failed, exit\n", __func__, blk);
|
||
}
|
||
}
|
||
|
||
exit:
|
||
if (bbtUpdate)
|
||
SpiNandUpdateBbt();
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* check if the replace block is valid.
|
||
* param:
|
||
* block: block index.
|
||
*/
|
||
static int SpiNandCheckReplaceBlockValid(u32 block)
|
||
{
|
||
snf_chip_info *chip = &g_snf;
|
||
|
||
if ((block >= chip->AvailableBlks) && (block < chip->BbtBlkOffset)) {
|
||
return SNF_OK;
|
||
}
|
||
|
||
return -SNF_ERR;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* check if a block is a bad block.
|
||
* param:
|
||
* blknum: block index.
|
||
*/
|
||
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) {
|
||
printf("%s, SpiNandReadPageSpare page0 failed, ret:%d.\n", __func__, ret);
|
||
goto exit;
|
||
}
|
||
|
||
if (spareBuf[0] == 0xFF) {
|
||
if (bbm == 2) {
|
||
ret = SpiNandReadPageSpare(page+1, spareBuf);
|
||
if (ret != SNF_OK) {
|
||
printf("%s, SpiNandReadPageSpare page1 failed, ret:%d.\n", __func__, ret);
|
||
goto exit;
|
||
}
|
||
if (spareBuf[0] == 0xFF) {
|
||
//printf(" good block:%d.\n", blknum);
|
||
return SNF_OK;
|
||
} else {
|
||
printf("%s, bad block:%d.\n", __func__, blknum);
|
||
}
|
||
}
|
||
}
|
||
|
||
exit:
|
||
printf("%s bad block:%d.\n", __func__, blknum);
|
||
|
||
return -SNF_BAD_BLK;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* find a good block form backup area to replay bad block.
|
||
* param:
|
||
* block:
|
||
*/
|
||
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;
|
||
}
|
||
}
|
||
|
||
printf("%s failed, there are no valid replacement 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) {
|
||
printf("%s erase block(%d) failed, ret=%d\n", __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) {
|
||
printf("%s write block(%d) failed ret=%d\n", __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) {
|
||
printf("%s, bbt has %d bad blcoks.\n", __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) {
|
||
printf("%s SpiNandReadPhysicalBlock block:%d failed\n", __func__, curr_block);
|
||
continue;
|
||
}
|
||
|
||
if (memcmp(chip->bbt.header_mask, SNF_BBT_HEADER_MASK, sizeof(SNF_BBT_HEADER_MASK))) {
|
||
printf("%s Not fond BBT header(block: %d).\n", __func__, curr_block);
|
||
ret = -SNF_ERR;
|
||
continue;
|
||
}
|
||
|
||
checksum = xcrc32(buf + SNF_BBT_HEADER_SIZE, crc_size, 0xFFFFFFFF);
|
||
|
||
if (chip->bbt.bbt_crc != checksum) {
|
||
printf("%s BBT information is abnormal(block: %d).\n", __func__, curr_block);
|
||
ret = -SNF_ERR;
|
||
continue;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* update bad block table.
|
||
* param:
|
||
*/
|
||
static int SpiNandUpdateBbt(void)
|
||
{
|
||
snf_chip_info *chip = &g_snf;
|
||
int ret = SNF_OK;
|
||
u32 bbt_size, crc_size;
|
||
u8 *pbuf;
|
||
|
||
#if 0
|
||
for (i=0; i<chip->TotalBlk; i++) {
|
||
if (chip->bbt.node[i].status == SNF_BLK_BAD) {
|
||
printf("%s bad block:%d, resplace block:%d.\n", __func__, i, chip->bbt.node[i].replace);
|
||
}
|
||
}
|
||
#endif
|
||
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) {
|
||
printf("%s write bbt failed, ret:%d.\n", __func__, ret);
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* bad block table initialize.
|
||
* param:
|
||
*/
|
||
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);
|
||
printf("%s 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) {
|
||
printf("SpiNandSetAttribute: Invalid chip.\n");
|
||
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;
|
||
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];
|
||
}
|
||
printf("[SNF]:%s, Capacity:%dMB , AvailableCapacity:%dMB.\n",
|
||
chip->Name, chip->Capacity/1024/1024, chip->AvailableCapacity/1024/1024);
|
||
} else {
|
||
printf("Not find spi nand flash.\n");
|
||
return -SNF_ERR;
|
||
}
|
||
|
||
if (chip->BytePerPage > MAX_PAGE_SIZE) {
|
||
printf("###ERR: %s: Invalid MAX_PAGE_SIZE:%d.\n", __func__, MAX_PAGE_SIZE);
|
||
return -SNF_ERR;
|
||
}
|
||
if (chip->SprSize > MAX_SPARE_SIZE) {
|
||
printf("###ERR: %s: Invalid MAX_SPARE_SIZE:%d.\n", __func__, MAX_SPARE_SIZE);
|
||
return -SNF_ERR;
|
||
}
|
||
if (chip->TotalBlk > MAX_BLK_SIZE) {
|
||
printf("###ERR: %s: Invalid MAX_BLK_SIZE:%d.\n", __func__, MAX_BLK_SIZE);
|
||
return -SNF_ERR;
|
||
}
|
||
|
||
return SNF_OK;
|
||
}
|
||
|
||
int SpiInit(void)
|
||
{
|
||
snf_chip_info *chip = &g_snf;
|
||
int ret;
|
||
|
||
memset(chip, 0, sizeof(snf_chip_info));
|
||
|
||
/* spi controler init */
|
||
SpiInitialize();
|
||
|
||
/* spi nand init */
|
||
SpinandSoftreset();
|
||
SpiNandWaitTillReady();
|
||
SpiNandReadId();
|
||
ret = SpiNandSetAttribute(chip);
|
||
if (ret != SNF_OK) {
|
||
printf("Not find spi nand flash.\n");
|
||
return ret;
|
||
}
|
||
|
||
SpiNandWriteReg(SPI_NAND_LOCK_REG, SPI_NAND_PROT_UNLOCK_ALL);
|
||
SpiNandEnableECC();
|
||
|
||
#ifdef SPI0_QSPI_MODE
|
||
SpiNandEnableQuad();
|
||
#else
|
||
SpiNandDisableQuad();
|
||
#endif
|
||
SpiNandWriteEnable();
|
||
|
||
ret = SpiNandBbtInit();
|
||
if (ret != SNF_OK)
|
||
printf("%s SpiNandBbtInit failed.\n", __func__);
|
||
|
||
SpiNandWriteDisable();
|
||
|
||
return ret;
|
||
}
|
||
|
||
static int SpiNandLoadImage(void)
|
||
{
|
||
int ret;
|
||
u32 *buf = (u32*)IMAGE_ENTRY;
|
||
UpFileHeader *header;
|
||
UpFileInfo *appfile;
|
||
u32 appsize;
|
||
SysInfo *sysinfo = GetSysInfo();
|
||
u32 bytePerPage = g_snf.BytePerPage;
|
||
u32 image_offset = sysinfo->image_offset;
|
||
|
||
ret = SpiNandReadData(image_offset, buf, bytePerPage);
|
||
if (ret != SNF_OK) {
|
||
printf("%s Read Image file header failed.\n", __func__);
|
||
while (1);
|
||
}
|
||
|
||
header = (UpFileHeader*)buf;
|
||
appfile = &header->files[0];
|
||
if (appfile->offset & (bytePerPage - 1)) {
|
||
SendUartString("\nImage positon is not align to flash pagesize, can't load.\r\n");
|
||
while (1);
|
||
}
|
||
|
||
if (sysinfo->app_size)
|
||
appsize = sysinfo->app_size;
|
||
else
|
||
appsize = appfile->size;
|
||
|
||
ret = SpiNandReadData(appfile->offset + image_offset, buf, appsize);
|
||
if (ret != SNF_OK) {
|
||
printf("%s Read app file failed.\n", __func__);
|
||
while (1);
|
||
}
|
||
|
||
#ifdef MMU_ENABLE
|
||
CP15_clean_dcache_for_dma(IMAGE_ENTRY, IMAGE_ENTRY + appsize);
|
||
#endif
|
||
return SNF_OK;
|
||
}
|
||
|
||
void bootFromSPI(void)
|
||
{
|
||
void (*funPtr)(void);
|
||
|
||
SpiNandLoadImage();
|
||
|
||
funPtr = (void (*)(void))IMAGE_ENTRY;
|
||
funPtr();
|
||
}
|
||
|
||
/*
|
||
* func:
|
||
* burn data to spi flash.
|
||
* param:
|
||
* buf: data buffer.
|
||
* offset: address offset(must align with PAGE_SIZE).
|
||
* size: data size;
|
||
* show_progress: if show update progress in lcd.
|
||
*/
|
||
static int SpiNandBurn(void *buf, u32 offset, u32 size, int show_progress)
|
||
{
|
||
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) {
|
||
printf("###ERR:%s, burn out of boundry.\n", __func__);
|
||
return -SNF_ERR;
|
||
}
|
||
|
||
//offset check align
|
||
if (offset % chip->BytePerPage) {
|
||
printf("###ERR:%s, offset is not align with page\n", __func__);
|
||
return -SNF_ERR;
|
||
}
|
||
|
||
while (remain > 0) {
|
||
//burn block.
|
||
burn_size = ((bytePerBlk - offset_in_block) >= remain) ? remain : (bytePerBlk - offset_in_block);
|
||
ret = SpiNandEraseWriteInBlock(currblk, offset_in_block, pbuf, burn_size);
|
||
if (ret != SNF_OK) {
|
||
printf("###ERR:%s, erase write block(%d) failed.\n", __func__, currblk);
|
||
break;
|
||
}
|
||
|
||
if (offset_in_block)
|
||
offset_in_block = 0;
|
||
|
||
currblk++;
|
||
pbuf += burn_size;
|
||
remain -= burn_size;
|
||
if (remain < 0) {
|
||
ret = -SNF_ERR;
|
||
printf("###ERR:%s, Invalid remain\n", __func__);
|
||
break;
|
||
}
|
||
|
||
if (show_progress)
|
||
update_progress_set((size - remain) * 100 / size);
|
||
}
|
||
|
||
if (show_progress)
|
||
update_progress_set(100);
|
||
|
||
SpiNandWriteDisable();
|
||
|
||
return ret;
|
||
}
|
||
|
||
int FlashBurn(void *buf, u32 offset, u32 size, int show_progress)
|
||
{
|
||
return SpiNandBurn(buf, offset, size, show_progress);
|
||
}
|
||
|
||
int SpiReadSysInfo(SysInfo *info)
|
||
{
|
||
u32 *buf = (u32*)IMAGE_ENTRY;
|
||
u32 checksum;
|
||
u32 calc_checksum;
|
||
int ret;
|
||
|
||
ret = SpiNandReadData(SYSINFOA_OFFSET, buf, sizeof(SysInfo));
|
||
if (ret != SNF_OK) {
|
||
printf("###ERR:%s, SYSINFOA_OFFSET read failed, ret:%d.\n", __func__, ret);
|
||
}
|
||
|
||
checksum = *(u32*)(IMAGE_ENTRY + sizeof(SysInfo) - 4);
|
||
calc_checksum = xcrc32((u8*)IMAGE_ENTRY, sizeof(SysInfo) - 4, 0xffffffff);
|
||
if (calc_checksum == checksum) {
|
||
memcpy(info, (void*)IMAGE_ENTRY, sizeof(SysInfo));
|
||
printf("%s, SYSINFOA_OFFSET.\n", __func__);
|
||
return 0;
|
||
}
|
||
|
||
buf = (u32*)IMAGE_ENTRY;
|
||
|
||
ret = SpiNandReadData(SYSINFOB_OFFSET, buf, sizeof(SysInfo));
|
||
if (ret != SNF_OK) {
|
||
printf("###ERR:%s, SYSINFOB_OFFSET read failed, ret:%d.\n", __func__, ret);
|
||
}
|
||
|
||
checksum = *(u32*)(IMAGE_ENTRY + sizeof(SysInfo) - 4);
|
||
calc_checksum = xcrc32((u8*)IMAGE_ENTRY, sizeof(SysInfo) - 4, 0xffffffff);
|
||
if (calc_checksum == checksum) {
|
||
memcpy(info, (void*)IMAGE_ENTRY, sizeof(SysInfo));
|
||
printf("%s, SYSINFOB_OFFSET.\n", __func__);
|
||
return 0;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
|
||
void SpiWriteSysInfo(SysInfo *info)
|
||
{
|
||
SpiNandBurn(info, SYSINFOB_OFFSET, sizeof(SysInfo), 0);
|
||
SpiNandBurn(info, SYSINFOA_OFFSET, sizeof(SysInfo), 0);
|
||
}
|
||
|
||
u32 SpiGetUpfileOffset(int type)
|
||
{
|
||
int bytePerPage = g_snf.BytePerPage;
|
||
int bytePerBlk = g_snf.BytePerBlk;
|
||
SysInfo *sysinfo = GetSysInfo();
|
||
|
||
if (type == UPFILE_TYPE_WHOLE) {
|
||
if (sysinfo->image_offset == IMAGE_OFFSET)
|
||
sysinfo->image_offset = IMAGEB_OFFSET;
|
||
else
|
||
sysinfo->image_offset = IMAGE_OFFSET;
|
||
return sysinfo->image_offset;
|
||
} else if (type == UPFILE_TYPE_FIRSTLDR) {
|
||
return LOADER_OFFSET;
|
||
} else if (type == UPFILE_TYPE_STEPLDR) {
|
||
if (sysinfo->stepldr_offset == STEPLDRA_OFFSET)
|
||
sysinfo->stepldr_offset = STEPLDRB_OFFSET;
|
||
else
|
||
sysinfo->stepldr_offset = STEPLDRA_OFFSET;
|
||
return sysinfo->stepldr_offset;
|
||
} else {
|
||
u32 buf[MAX_PAGE_SIZE/4];
|
||
UpFileHeader *header;
|
||
UpFileInfo *appfile;
|
||
u32 offset;
|
||
int i;
|
||
|
||
SpiNandReadData(sysinfo->image_offset, buf, bytePerPage);
|
||
header = (UpFileHeader*)buf;
|
||
if (header->magic != MKTAG('U', 'P', 'D', 'F')) {
|
||
SendUartString("old update file isn't found in flash, can't support module update.\n");
|
||
return 0xffffffff;
|
||
}
|
||
for (i = 0; i < header->filenum; i++) {
|
||
appfile = &header->files[i];
|
||
if ((appfile->magic == UPFILE_APP_MAGIC && type == UPFILE_TYPE_APP) ||
|
||
(appfile->magic == MKTAG('R', 'O', 'M', 'A') && type == UPFILE_TYPE_RESOURCE) ||
|
||
(appfile->magic == MKTAG('B', 'A', 'N', 'I') && type == UPFILE_TYPE_ANIMATION)) {
|
||
offset = appfile->offset;
|
||
if (offset & (bytePerBlk - 1)) {
|
||
SendUartString("offset isn't align to block erase size, can't support module update.\n");
|
||
return 0xffffffff;
|
||
}
|
||
return offset + sysinfo->image_offset;
|
||
}
|
||
}
|
||
}
|
||
|
||
return 0xffffffff;
|
||
}
|
||
#endif
|