blob: d28cfbc704cc32a781af96a19a8379ac5796a0c8 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2016, ZIXC Corporation.
*
* File Name:
* File Mark:
* Description:
* Others:
* Version: 1.0
* Author: geanfeng
* Date: 2013-3-4
* History 1:
* Date:
* Version:
* Author:
* Modification:
* History 2:
********************************************************************************/
/****************************************************************************
* Include files
****************************************************************************/
#include "downloader_nand.h"
#include <asm/errno.h>
#include "partition_table.h"
#include <linux/mtd/nor_spifc.h>
/****************************************************************************
* Local Macros
****************************************************************************/
#define DATA_WITH_OOB (1 << 0) /* whether write with oob data*/
/****************************************************************************
* Local Types
****************************************************************************/
/****************************************************************************
* Global Variables
****************************************************************************/
extern partition_table_t * g_partition_table;
extern partition_table_t * g_partition_table_dl;
extern partition_entry_t * get_partitions(const char *partname, partition_table_t *table);
/****************************************************************************
* Global Function Prototypes
****************************************************************************/
/****************************************************************************
* Function Definitions
****************************************************************************/
/*******************************************************************************
* Function:get_part_offset_skipbase
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*phyBase
*
* Others:
********************************************************************************/
static int get_part_offset_skipbase(partition_entry_t * part, uint offset, uint * skipBase)
{
nand_info_t * pNandInfo = NULL;
uint offsetSkipBase = 0;
uint blockNum = 0;
int ret = 0;
pNandInfo = &nand_info[nand_curr_device];
offsetSkipBase = part->part_offset;
assert((offset & (pNandInfo->erasesize - 1)) == 0);
assert((offsetSkipBase & (pNandInfo->erasesize - 1)) == 0);
if(offset != 0)
{
blockNum = offset /pNandInfo->erasesize;
while(blockNum > 0)
{
ret = nand_block_isbad (pNandInfo, offsetSkipBase);
offsetSkipBase += pNandInfo->erasesize;
if (ret)
{
continue;
}
else
{
blockNum--;
}
}
}
*skipBase = offsetSkipBase;
return 0;
}
/*******************************************************************************
* Function:nand_read_skip_bad_compt
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* NULL:error else: success
*
* Others:
********************************************************************************/
int nand_read_skip_bad_compat(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_read = *length;
u_char *p_buffer = buffer;
if (flags & DATA_WITH_OOB) {
int pages;
pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
printf ("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
} else
{
blocksize = nand->erasesize;
}
if ((offset & (nand->writesize - 1)) != 0) {
printf ("Attempt to write non page aligned data\n");
*length = 0;
return -EINVAL;
}
while (left_to_read > 0) {
size_t block_offset = offset & (nand->erasesize - 1);
size_t read_size, truncated_read_size;
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08llx\n",
(u64)(offset & ~(nand->erasesize - 1)));
offset += nand->erasesize - block_offset;
continue;
}
if (left_to_read < (blocksize - block_offset))
read_size = left_to_read;
else
read_size = blocksize - block_offset;
if (flags & DATA_WITH_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
memset(&ops, 0x0, sizeof(ops));
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
pages = read_size / pagesize_oob;
for (page = 0; page < pages; page++) {
ops.datbuf = p_buffer;
ops.oobbuf = ops.datbuf + pagesize;
rval = nand->read_oob(nand, offset, &ops);
if (rval)
break;
offset += pagesize;
p_buffer += pagesize_oob;
}
}
else
{
truncated_read_size = read_size;
rval = nand_read(nand, offset, &truncated_read_size,
p_buffer);
offset += read_size;
p_buffer += read_size;
}
if (rval != 0) {
printf ("NAND read from offset %llx failed %d\n",
(u64)offset, rval);
*length -= left_to_read;
return rval;
}
left_to_read -= read_size;
}
return 0;
}
/*******************************************************************************
* Function:nand_read_skip_bad_compt
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* NULL:error else: success
*
* Others:
********************************************************************************/
int nand_write_skip_bad_compat(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
u_char *p_buffer = buffer;
if (flags & DATA_WITH_OOB) {
int pages;
pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
printf ("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
} else
{
blocksize = nand->erasesize;
}
/*
* nand_write() handles unaligned, partial page writes.
*
* We allow length to be unaligned, for convenience in
* using the $filesize variable.
*
* However, starting at an unaligned offset makes the
* semantics of bad block skipping ambiguous (really,
* you should only start a block skipping access at a
* partition boundary). So don't try to handle that.
*/
if ((offset & (nand->writesize - 1)) != 0) {
printf ("Attempt to write non page aligned data\n");
*length = 0;
return -EINVAL;
}
while (left_to_write > 0) {
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size, truncated_write_size;
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08llx\n",
(u64)(offset & ~(nand->erasesize - 1)));
offset += nand->erasesize - block_offset;
continue;
}
if (left_to_write < (blocksize - block_offset))
write_size = left_to_write;
else
write_size = blocksize - block_offset;
if (flags & DATA_WITH_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
for (page = 0; page < pages; page++) {
ops.datbuf = p_buffer;
ops.oobbuf = ops.datbuf + pagesize;
//ops.oobbuf = NULL;
if(*(ops.datbuf + pagesize) != 0xFF || *(ops.datbuf + pagesize+1) != 0xFF) {
printf ("Fs image format error\n");
return -EINVAL;
}
rval = nand->write_oob(nand, offset, &ops);
if (rval)
break;
offset += pagesize;
p_buffer += pagesize_oob;
}
}
else
{
truncated_write_size = write_size;
rval = nand_write(nand, offset, &truncated_write_size,
p_buffer);
offset += write_size;
p_buffer += write_size;
}
if (rval != 0) {
printf ("NAND write to offset %llx failed %d\n",
(u64)offset, rval);
*length -= left_to_write;
return rval;
}
left_to_write -= write_size;
}
return 0;
}
/*******************************************************************************
* Function:downloader_get_part
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* NULL:error else: success
*
* Others:
********************************************************************************/
partition_entry_t * downloader_get_part(const char *partname)
{
partition_entry_t *part;
part = find_partition_para((uchar *)partname);
if(part == NULL)
return NULL;
return part;
}
/*******************************************************************************
* Function:downloader_get_part_dl
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* NULL:error else: success
*
* Others:
********************************************************************************/
partition_entry_t * downloader_get_part_dl(const char *partname)
{
partition_entry_t *part;
part = get_partitions((const char *)partname,g_partition_table_dl);
if(part == NULL)
{
return NULL;
}
printf("name=%s,part-name=%s,typt=%s\n",partname,part->part_name,part->part_type);
return part;
}
/*******************************************************************************
* Function:downloader_get_part_actual_size
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* NULL:error else: success
*
* Others:
********************************************************************************/
u32 downloader_get_part_actual_size(partition_entry_t *part)
{
nand_info_t * pNandInfo = NULL;
pNandInfo = &nand_info[nand_curr_device];
u32 bad_blk_cnt = 0;
u32 offset = part->part_offset;
while(offset < part->part_offset + part->part_size )
{
if (nand_block_isbad (pNandInfo, offset) ){
bad_blk_cnt = bad_blk_cnt +1 ;
offset += pNandInfo->erasesize;
continue;
}
offset += pNandInfo->erasesize;
}
printf("downloader_get_part_actual_size:[%s] bad_blk_cnt = %d\n",part->part_name, bad_blk_cnt);
u32 part_actual_size = part->part_size -(bad_blk_cnt * pNandInfo->erasesize);
return part_actual_size;
}
/*******************************************************************************
* Function:downloader_nand_read
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_read(partition_entry_t * part, uint offset, uint size, unchar * buffer)
{
nand_info_t * pNandInfo = NULL;
uint nandPhyBase = 0;
int ret = 0;
char ack[64] = {0};
if(part == NULL || \
offset>part->part_size || (size>downloader_get_part_actual_size(part)) || (offset+size) > part->part_size)
{
printf(" [downloader_nand_read:][%s]bin_size > actual part_size \n", part->part_name);
sprintf(ack, " READ FAIL UNLEGAL SIZE ");
downloader_serial_write(ack, strlen(ack)+1);
return -1;
}
pNandInfo = &nand_info[nand_curr_device];
get_part_offset_skipbase(part,offset,&nandPhyBase);
if( strcmp((const char *)part->part_name , "zloader") == 0 )
{
/* ÕâÀﲻʹÓÃECC¶Áȡʱ£¬Òª¶ÁÈ¡OOB£¬¶øOOBµÃÊý¾Ý»á·ÅÈëdata->buf;
ËùÒÔ´æ·ÅÊý¾ÝµÄbuf±ØÐëΪ (1 page + oob),²»ÄÜʹÓÃÉÏÃæ´«ÏÂÀ´µÄ
buffer,ÒòΪbufferµÄ´óСΪ²»°üº¬ oob µÄ´óС
*/
u_char *buf = kzalloc(pNandInfo->writesize + pNandInfo->oobsize, GFP_KERNEL);
if( buf == NULL )
{
printf("downloader_nand_read kzalloc error\n");
return -1;
}
int times = size/pNandInfo->writesize;
int i = 0;
for(; i<times; i++)
{
ret += nand_read_page_with_ecc(pNandInfo,
((loff_t)i*pNandInfo->writesize),
&size,
(u_char*)buf );
memcpy((u_char*)buffer, buf ,pNandInfo->writesize);
buffer += pNandInfo->writesize;
}
kfree(buf);
}
else
ret = nand_read_skip_bad(pNandInfo,\
nandPhyBase+offset%(pNandInfo->erasesize),&size,(u_char*)buffer);
if(ret)
{
printf("downloader_nand_read error\n");
return -1;
}
return 0;
}
/*******************************************************************************
* Function:downloader_nand_write
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_write(partition_entry_t * part, uint offset, uint size, unchar * buffer)
{
nand_info_t * pNandInfo = NULL;
uint nandPhyBase = 0;
int ret = 0;
uchar * buf = buffer;
char ack[64] = {0};
if(part == NULL || \
offset>part->part_size || (size > downloader_get_part_actual_size(part)) || (offset+size) > part->part_size)
{
printf("[downloader_nand_write:][%s] bin_size > actual part_size \n", part->part_name);
sprintf(ack, "WRITE FAIL UNLEGAL SIZE ");
downloader_serial_write(ack, strlen(ack)+1);
return -1;
}
/*if(zftl_get_ZFTLrecord(part->part_offset))
{
printf("Function not allowed write zftl\n");
return -1;
}*/
pNandInfo = &nand_info[nand_curr_device];
get_part_offset_skipbase(part,offset,&nandPhyBase);
printf("entry nand_write\n");
/* רÃÅдZ-LOADʱʹÓ㬲»Ê¹ÓÃECC*/
if( strcmp((const char *)part->part_name , "zloader") == 0 )
{
if( size != 12*1024 )
{
printf("downloader_nand_write z-load size != 12k...\n");
return -1;
}
int times = 12*1024/pNandInfo->writesize;
int i = 0;
for(; i<times; i++)
{
ret += nand_write_page_with_ecc(pNandInfo,
((loff_t)i*(pNandInfo->writesize)),
buf );
buf += pNandInfo->writesize;
}
}
else
ret = nand_write_skip_bad(pNandInfo,\
nandPhyBase+offset%(pNandInfo->erasesize),&size,(u_char*)buffer, 0);
printf("write skipbad finish, addr = %d,size = %d\n",nandPhyBase+offset%(pNandInfo->erasesize),size);
if(ret)
{
printf("downloader_nand_write error\n");
return -1;
}
return 0;
}
/*******************************************************************************
* Function:downloader_nand_fs_read
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_fs_read(partition_entry_t * part, uint offset, uint size, unchar * buffer)
{
nand_info_t * pNandInfo = NULL;
uint nandPhyBase = 0;
int ret = 0;
char ack[64] = {0};
if(part == NULL || \
offset>part->part_size || (size>downloader_get_part_actual_size(part)) || (offset+size) > part->part_size)
{
printf("[downloader_nand_fs_read:][%s] bin_size > actual part_size \n", part->part_name);
sprintf(ack, " FS_READ FAIL UNLEGAL SIZE ");
downloader_serial_write(ack, strlen(ack)+1);
return -1;
}
pNandInfo = &nand_info[nand_curr_device];
assert(pNandInfo!=NULL);
get_part_offset_skipbase(part,offset,&nandPhyBase);
ret = nand_read_skip_bad_compat(pNandInfo,nandPhyBase+offset%(pNandInfo->erasesize),&size,(u_char*)buffer,DATA_WITH_OOB);
if(ret)
{
printf("downloader_nand_fs_read error\n");
return -1;
}
return 0;
}
/*******************************************************************************
* Function:downloader_nand_fs_write
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_fs_write(partition_entry_t * part, uint offset, uint size, unchar * buffer)
{
nand_info_t * pNandInfo = NULL;
uint nandPhyBase = 0;
int ret = 0;
char ack[64] = {0};
if(part == NULL || \
offset>part->part_size || (size>downloader_get_part_actual_size(part)) || (offset+size) > part->part_size)
{
printf("[downloader_nand_fs_write:][%s] bin_size > actual part_size \n", part->part_name);
sprintf(ack, " FS_WRITE FAIL UNLEGAL SIZE ");
downloader_serial_write(ack, strlen(ack)+1);
return -1;
}
pNandInfo = &nand_info[nand_curr_device];
assert(pNandInfo!=NULL);
get_part_offset_skipbase(part,offset,&nandPhyBase);
ret = nand_write_skip_bad_compat(pNandInfo,nandPhyBase+offset%(pNandInfo->erasesize),&size,(u_char*)buffer, DATA_WITH_OOB);
if(ret)
{
printf("downloader_nand_fs_write error\n");
return -1;
}
return 0;
}
/*******************************************************************************
* Function:downloader_nand_erase
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_erase(partition_entry_t * part, uint partEraseSize)
{
nand_info_t * pNandInfo = NULL;
int ret = 0;
struct erase_info instr;
uint size = 0;
if(part == NULL )
{
return -1;
}
pNandInfo = &nand_info[nand_curr_device];
instr.mtd = pNandInfo;
instr.addr = part->part_offset;
assert( (instr.addr & (pNandInfo->erasesize - 1)) == 0);
instr.callback = 0;
//ret=nand_erase(pNandInfo, part->part_offset, part->part_size);
while(size < partEraseSize && (instr.addr < (part->part_offset+part->part_size)))
{
if(nand_block_isbad (pNandInfo, instr.addr))
{
instr.addr += pNandInfo->erasesize;
continue ;
}
instr.len = pNandInfo->erasesize;
instr.state = 0;
ret = pNandInfo->erase(pNandInfo, &instr);
if(ret && instr.state == MTD_ERASE_FAILED)
{
pNandInfo->block_markbad(pNandInfo,instr.addr);
}
else if (ret == 0)
{
size += pNandInfo->erasesize;
}
else
{
printf( "downloader nand: erase error\n");
return 1;
}
instr.addr += pNandInfo->erasesize;
}
return ret;
}
/*******************************************************************************
* Function:downloader_nand_eraseall
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_eraseall(void)
{
nand_info_t * pNandInfo = NULL;
struct erase_info instr;
int i = 0;
int ret = 0;
for(i=0; i<CONFIG_SYS_MAX_NAND_DEVICE; i++)
{
pNandInfo = &nand_info[i];
instr.mtd = pNandInfo;
instr.addr = 0x00;
assert( (instr.addr & (pNandInfo->erasesize - 1)) == 0);
instr.callback = 0;
while(instr.addr < pNandInfo->size)
{
if(nand_block_isbad (pNandInfo, instr.addr))
{
instr.addr += pNandInfo->erasesize;
continue ;
}
instr.len = pNandInfo->erasesize;
instr.state = 0;
ret = pNandInfo->erase(pNandInfo, &instr);
if(ret && instr.state == MTD_ERASE_FAILED)
{
pNandInfo->block_markbad(pNandInfo,instr.addr);
}
instr.addr += pNandInfo->erasesize;
}
}
return ret;
}
/*******************************************************************************
* Function:downloader_nand_erase_auto
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nand_erase_auto(void)
{
int ret = 0;
partition_entry_t *entry = &g_partition_table->table[0];
uint32_t entry_nums = g_partition_table->entrys;
while( entry_nums-- )
{
if ( strcmp((const char *)entry->part_name, "nvrofs") == 0 \
||strcmp((const char *)entry->part_name, "ddr") == 0 \
||strcmp((const char *)entry->part_name, "raw") == 0)
{
entry++;
continue;
}
ret = downloader_nand_erase(entry,entry->part_size);
entry++;
}
return ret;
}
extern struct fsl_qspi spi_nor_flash;
/*******************************************************************************
* Function:downloader_nor_read
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nor_read(partition_entry_t * part, uint offset, uint size, unchar * buffer)
{
int ret = 0;
char ack[64] = {0};
size_t read_size = size;
struct fsl_qspi *nor = NULL;
if(part == NULL
||offset>part->part_size
|| (offset+size) > part->part_size)
{
printf(" [downloader_nor_read:][%s]bin_size > actual part_size \n", part->part_name);
sprintf(ack, " READ FAIL UNLEGAL SIZE ");
downloader_serial_write(ack, strlen(ack)+1);
return -1;
}
nor = &spi_nor_flash;
ret = nand_read(&(nor->nor[0].mtd), part->part_offset + offset, &read_size, buffer);
if(ret)
{
printf("downloader_nor_read error\n");
return -1;
}
return 0;
}
/*******************************************************************************
* Function:downloader_nor_write
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nor_write(partition_entry_t * part, uint offset, uint size, unchar * buffer)
{
int ret = 0;
size_t write_size=0;
char ack[64] = {0};
struct fsl_qspi *nor = NULL;
write_size = size;
if(part == NULL
|| offset>part->part_size
|| (offset+size) > part->part_size)
{
printf("[downloader_nor_write:][%s] bin_size > actual part_size \n", part->part_name);
sprintf(ack, "WRITE FAIL UNLEGAL SIZE ");
downloader_serial_write(ack, strlen(ack)+1);
return -1;
}
nor = &spi_nor_flash;
ret = nand_write(&(nor->nor[0].mtd), part->part_offset + offset, &write_size, buffer);
if(ret)
{
printf("downloader_nor_write error\n");
return ret;
}
return 0;
}
/*******************************************************************************
* Function:downloader_nand_erase
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nor_erase(partition_entry_t * part, uint partEraseSize)
{
int ret = 0;
struct fsl_qspi *nor = NULL;
if(part == NULL)
{
return -1;
}
nor = &spi_nor_flash;
ret = nand_erase(&(nor->nor[0].mtd), part->part_offset, partEraseSize);
if(ret)
{
printf("downloader_nor_erase error\n");
return ret;
}
printf("downloader_nor_erase ok\n");
return 0;
}
/*******************************************************************************
* Function:downloader_nor_eraseall
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nor_eraseall(void)
{
int ret = 0;
struct fsl_qspi *nor = NULL;
nor = &spi_nor_flash;
ret = nand_erase(&(nor->nor[0].mtd), 0x0, nor->nor[0].mtd.size);
if(ret)
{
printf("downloader_nor_eraseall error\n");
return ret;
}
printf("downloader_nor_eraseall ok\n");
return 0;
}
/*******************************************************************************
* Function:downloader_nor_erase_auto
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
* 0: success else:error
*
* Others:
********************************************************************************/
int downloader_nor_erase_auto(void)
{
int ret = 0;
partition_entry_t *entry = &g_partition_table->table[0];
uint32_t entry_nums = g_partition_table->entrys;
while(entry_nums--)
{
if ( strcmp((const char *)entry->part_name, "nvrofs") == 0 \
||strcmp((const char *)entry->part_name, "ddr") == 0 \
||strcmp((const char *)entry->part_name, "raw") == 0)
{
entry++;
continue;
}
ret = downloader_nor_erase(entry,entry->part_size);
if(ret)
{
printf("downloader_nor_erase_auto error\n");
return ret;
}
entry++;
}
printf("downloader_nor_erase_auto ok\n");
return 0;
}
int get_nor_null_slice_flag(unsigned int *flag)
{
int ret = 0;
size_t read_size = 0x100;
unchar buffer[256];
struct fsl_qspi *nor = NULL;
memset(buffer, 0xFF, 0x100);
nor = &spi_nor_flash;
ret = nand_read(&(nor->nor[0].mtd), 0x0, &read_size, buffer);
if(ret)
{
printf("downloader_nor_read error\n");
return -1;
}
if(strncmp((const char *)(buffer+4), "ZX7521V1", 8) == 0)
{
*flag = 1;
printf("nor flash not null\n");
}
return 0;
}