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

1617 lines
36 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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