1
0
mirror of https://xff.cz/git/u-boot/ synced 2025-09-01 16:52:14 +02:00
Files
u-boot-megous/drivers/spinor/sunxi_spinor.c
Ondrej Jirman ba8c26ce40 initial
2019-03-04 15:37:41 +01:00

1716 lines
36 KiB
C
Executable File
Raw Permalink 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.

/*
* (C) Copyright 2007-2013
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* wangwei <wangwei@allwinnertech.com>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/arch/spi.h>
#include <sunxi_mbr.h>
#include <private_boot0.h>
#include <asm/arch/spinor.h>
#include <sunxi_board.h>
#include <fdt_support.h>
#include <sys_config_old.h>
#include <private_uboot.h>
#include <private_boot0.h>
#include <private_toc.h>
DECLARE_GLOBAL_DATA_PTR;
static int spinor_flash_inited = 0;
uint total_write_bytes;
static char *spinor_store_buffer;
//static char spinor_mbr[SUNXI_MBR_SIZE];
static char *spinor_write_cache;
static int spinor_cache_block = -1;
static int spinor_4bytes_addr_mode = 0;
static int spi_freq = 0;
static int quad_flag;
#define ENABLE_4BYTES 1
#define DISABLE_4BYTES 0
#define SYSTEM_PAGE_SIZE (512)
#define SPINOR_PAGE_SIZE (256)
#define NPAGE_IN_1SYSPAGE (SYSTEM_PAGE_SIZE/SPINOR_PAGE_SIZE)
#define SPINOR_BLOCK_BYTES (64 * 1024)
#define SPINOR_BLOCK_SECTORS (SPINOR_BLOCK_BYTES/512)
#define SPINOR_FREAD_DUAL_OUT 0x3b // sclk <= 75MHz
#define SPI_NORMAL_FRQ (40000000)
#define SPINOR_FREAD_QUAD_OUT 0x6b
static void spinor_enter_4bytes_addr(int);
static void spinor_config_addr_mode(u8 *sdata, uint page_addr, uint *num, u8 cmd);
static int set_quad_mode(u8 cmd1, u8 cmd2);
static int spi_nor_fast_read_quad_output(uint start, uint sector_cnt, void *buf);
struct spinor_info {
uint id;
uint mode_cmd1;
uint mode_cmd2;
uint flags;
#define QUAD_ENABLE 1
#define QUAD_DISABLE 0
};
struct spinor_info spi_nor_ids[] = {
/* spinor_id, mode_cmd1, mode_cmd2, flags */
/* -- Macronix -- */
{0x1820c2, 0x40, 0x00, QUAD_ENABLE},
{0x1020c2, 0x40, 0x00},
{0x1220c2, 0x40, 0x00},
{0x1320c2, 0x40, 0x00},
{0x1420c2, 0x40, 0x00},
{0x1520c2, 0x40, 0x00},
{0x1620c2, 0x40, 0x00},
{0x169ec2, 0x40, 0x00},
{0x1720c2, 0x40, 0x00},
{0x3725c2, 0x40, 0x00},
{0x1826c2, 0x40, 0x00},
{0x1920c2, 0x40, 0x00},
{0x1926c2, 0x40, 0x00},
{0x1a20c2, 0x40, 0x00, QUAD_ENABLE},
{0x1b26c2, 0x40, 0x00, QUAD_ENABLE},
/* -- Winbond -- */
{0x1840ef, 0x00, 0x02, QUAD_ENABLE},
{0x1030ef, 0x00, 0x02},
{0x1130ef, 0x00, 0x02},
{0x1230ef, 0x00, 0x02},
{0x1330ef, 0x00, 0x02},
{0x1430ef, 0x00, 0x02},
{0x1530ef, 0x00, 0x02},
{0x1630ef, 0x00, 0x02},
{0x1640ef, 0x00, 0x02},
{0x1660ef, 0x00, 0x02, QUAD_ENABLE},
{0x1730ef, 0x00, 0x02},
{0x1740ef, 0x00, 0x02},
{0x1760ef, 0x00, 0x02, QUAD_ENABLE},
{0x1860ef, 0x00, 0x02, QUAD_ENABLE},
{0x1450ef, 0x00, 0x02},
{0x1440ef, 0x00, 0x02},
{0x1940ef, 0x00, 0x02},
/* -- Atmel -- */
{0x01661f, 0x80, 0x00},
{0x04661f, 0x80, 0x00},
{0x01441f, 0x80, 0x00},
{0x01471f, 0x80, 0x00},
{0x00481f, 0x80, 0x00},
{0x00041f, 0x80, 0x00},
{0x01451f, 0x80, 0x00},
{0x01461f, 0x80, 0x00},
{0x00471f, 0x80, 0x00},
{0x00251f, 0x80, 0x00},
/* -- ISSI -- */
{0x209d7f, 0x40, 0x00},
};
static uint sunxi_spinor_generate_checksum(void *buffer, uint length, uint src_sum)
{
#ifndef CONFIG_USE_NEON_SIMD
uint *buf;
uint count;
uint sum;
/* 生成校验和 */
count = length >> 2; // 以 字4bytes为单位计数
sum = 0;
buf = (__u32 *)buffer;
do
{
sum += *buf++; // 依次累加,求得校验和
sum += *buf++; // 依次累加,求得校验和
sum += *buf++; // 依次累加,求得校验和
sum += *buf++; // 依次累加,求得校验和
}while( ( count -= 4 ) > (4-1) );
while( count-- > 0 )
sum += *buf++;
#else
uint sum;
sum = add_sum_neon(buffer, length);
#endif
sum = sum - src_sum + STAMP_VALUE;
return sum;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int sunxi_spinor_verify_checksum(void *buffer, uint length, uint src_sum)
{
#ifndef CONFIG_USE_NEON_SIMD
uint *buf;
uint count;
uint sum;
/* 生成校验和 */
count = length >> 2; // 以 字4bytes为单位计数
sum = 0;
buf = (__u32 *)buffer;
do
{
sum += *buf++; // 依次累加,求得校验和
sum += *buf++; // 依次累加,求得校验和
sum += *buf++; // 依次累加,求得校验和
sum += *buf++; // 依次累加,求得校验和
}while( ( count -= 4 ) > (4-1) );
while( count-- > 0 )
sum += *buf++;
#else
uint sum;
sum = add_sum_neon(buffer, length);
#endif
sum = sum - src_sum + STAMP_VALUE;
debug("src sum=%x, check sum=%x\n", src_sum, sum);
if( sum == src_sum )
return 0; // 校验成功
else
return -1; // 校验失败
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_wren(void)
{
uint sdata = SPINOR_WREN;
int ret = -1;
int txnum ;
txnum = 1;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(1, (void *)&sdata, 0, NULL);
return ret;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_rdsr(u8 *reg)
{
u8 sdata = SPINOR_RDSR;
int ret = -1;
int txnum ;
txnum = 1;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(1, (void *)&sdata, 1, (void *)reg);
return ret;
}
static uint __spinor_wrsr(u8 reg)
{
u8 sdata[2] = {0};
s32 ret = -1;
u8 status = 0;
u32 i = 0;
uint txnum = 0;
uint rxnum = 0 ;
ret = __spinor_wren();
if (ret==-1)
goto __err_out_;
txnum = 2;
rxnum = 0;
sdata[0] = SPINOR_WRSR;
sdata[1] = reg;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(txnum, (void*)sdata, rxnum, (void*)0);
if (ret==-1)
goto __err_out_;
do {
ret = __spinor_rdsr( &status);
if (ret==-1)
goto __err_out_;
for(i=0; i<100; i++);
} while(status&0x01);
printf("_spinor_wrsr status is %d \n",status);
ret = 0;
__err_out_:
return ret;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_erase_block(uint block_index)
{
uint blk_addr = block_index * SPINOR_BLOCK_BYTES;
u8 sdata[4] = {0};
int ret = -1;
u8 status = 0;
uint i = 0;
uint txnum, rxnum;
ret = __spinor_wren();
if (-1 == ret)
{
return -1;
}
txnum = 4;
rxnum = 0;
sdata[0] = SPINOR_SE;
sdata[1] = (blk_addr >> 16) & 0xff;
sdata[2] = (blk_addr >> 8 ) & 0xff;
sdata[3] = blk_addr & 0xff;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(txnum, (void *)sdata, rxnum, 0);
if (-1 == ret)
{
return -1;
}
do
{
ret = __spinor_rdsr(&status);
if (-1 == ret)
{
return -1;
}
for (i = 0; i < 100; i++);
} while (status & 0x01);
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_erase_all(void)
{
uint sdata = 0;
int ret = -1;
u8 status = 0;
uint txnum, rxnum;
printf("begin to erase all .");
int count = 0;
ret = __spinor_wren();
if (-1 == ret)
{
return -1;
}
txnum = 1;
rxnum = 0;
sdata = SPINOR_BE;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(txnum, (void*)&sdata, rxnum, 0);
if (ret==-1)
{
return -1;
}
do
{
ret = __spinor_rdsr(&status);
if (-1 == ret)
{
return -1;
}
count ++;
__msdelay(1000);
printf(".");
} while (status & 0x01);
printf("\nstatus = %d\n",status);
printf("count = %d \n",count);
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_pp(uint page_addr, void *buf, uint len)
{
u8 sdata[300];
u8 status = 0;
uint i = 0;
int ret = -1;
uint txnum = 0;
uint rxnum = 0;
if (len > 256)
{
return -1;
}
ret = __spinor_wren();
if (ret < 0)
{
return -1;
}
memset((void *)sdata, 0xff, sizeof(sdata)/sizeof(sdata[0]));
spinor_config_addr_mode(sdata,page_addr,&txnum,SPINOR_PP);
memcpy((void *)(sdata+txnum), buf, len);
txnum += len;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(txnum, (void *)sdata, rxnum, 0);
if (-1 == ret)
{
return -1;
}
do
{
ret = __spinor_rdsr(&status);
if (ret < 0)
{
return -1;
}
for (i=0; i < 100; i++);
} while (status & 0x01);
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_read_id(uint *id)
{
uint sdata = SPINOR_RDID;
int txnum ;
txnum = 1;
spic_config_dual_mode(0, 0, 0, txnum);
return spic_rw(1, (void *)&sdata, 3, (void *)id);
}
static int spi_nor_fast_read_dual_output(uint start, uint sector_cnt, void *buf)
{
uint page_addr;
uint rbyte_cnt;
u8 sdata[5] = {0};
int ret = 0;
uint tmp_cnt, tmp_offset = 0;
void *tmp_buf;
uint txnum, rxnum;
txnum = 5;
while (sector_cnt)
{
if (sector_cnt > 127)
{
tmp_cnt = 127;
}
else
{
tmp_cnt = sector_cnt;
}
page_addr = (start + tmp_offset) * SYSTEM_PAGE_SIZE;
rbyte_cnt = tmp_cnt * SYSTEM_PAGE_SIZE;
sdata[0] = SPINOR_FREAD_DUAL_OUT;
sdata[1] = (page_addr >> 16) & 0xff;
sdata[2] = (page_addr >> 8 ) & 0xff;
sdata[3] = page_addr & 0xff;
sdata[4] = 0 ;
rxnum = rbyte_cnt;
tmp_buf = (u8 *)buf + (tmp_offset << 9);
spic_config_dual_mode(0, 1, 0, txnum);
// flush_cache(tmp_buf,rxnum); // guoyingyang debug
if (spic_rw(txnum, (void *)sdata, rxnum, tmp_buf))
{
ret = -1;
break;
}
sector_cnt -= tmp_cnt;
tmp_offset += tmp_cnt;
}
return ret;
}
static int __spinor_sector_normal_read(uint start, uint sector_cnt, void *buf)
{
uint page_addr;
uint rbyte_cnt;
u8 sdata[4] = {0};
int ret = 0;
uint tmp_cnt, tmp_offset = 0;
void *tmp_buf;
uint txnum, rxnum;
txnum = 4;
while (sector_cnt)
{
if (sector_cnt > 127)
{
tmp_cnt = 127;
}
else
{
tmp_cnt = sector_cnt;
}
page_addr = (start + tmp_offset) * SYSTEM_PAGE_SIZE;
rbyte_cnt = tmp_cnt * SYSTEM_PAGE_SIZE;
sdata[0] = SPINOR_READ;
sdata[1] = (page_addr >> 16) & 0xff;
sdata[2] = (page_addr >> 8 ) & 0xff;
sdata[3] = page_addr & 0xff;
rxnum = rbyte_cnt;
tmp_buf = (u8 *)buf + (tmp_offset << 9);
spic_config_dual_mode(0, 0, 0, txnum);
if (spic_rw(txnum, (void *)sdata, rxnum, tmp_buf))
{
ret = -1;
break;
}
sector_cnt -= tmp_cnt;
tmp_offset += tmp_cnt;
}
return ret;
}
static int __spinor_sector_read(uint start, uint sector_cnt, void *buf)
{
int ret = 0;
if (quad_flag == 1)
ret = spi_nor_fast_read_quad_output(start, sector_cnt, buf);
else if (spi_freq >= SPI_NORMAL_FRQ)
ret = spi_nor_fast_read_dual_output( start,sector_cnt,buf);
else
ret = __spinor_sector_normal_read( start,sector_cnt,buf);
return ret;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
static int __spinor_sector_write(uint sector_start, uint sector_cnt, void *buf)
{
uint page_addr = sector_start * SYSTEM_PAGE_SIZE;
uint nor_page_cnt = sector_cnt * (NPAGE_IN_1SYSPAGE);
uint i = 0;
int ret = -1;
printf("start = 0x%x, cnt=0x%x\n", sector_start, sector_cnt);
// printf("nor_page_cnt=%d\n", nor_page_cnt);
for (i = 0; i < nor_page_cnt; i++)
{
// //printf("spinor program page : 0x%x\n", page_addr + SPINOR_PAGE_SIZE * i);
// if((i & 0x1ff) == 0x1ff)
// {
// printf("current cnt=%d\n", i);
// }
ret = __spinor_pp(page_addr + SPINOR_PAGE_SIZE * i, (void *)((uint)buf + SPINOR_PAGE_SIZE * i), SPINOR_PAGE_SIZE);
if (-1 == ret)
{
return -1;
}
}
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_init(int stage)
{
int i, id_size, spi_size = 0;
u32 id = 0;
spi_size = spinor_size();
if(spinor_flash_inited)
{
puts("sunxi spinor is already inited\n");
return 0;
}
else
{
puts("sunxi spinor is initing...");
if(spic_init(0))
{
puts("Fail\n");
return -1;
}
else
{
puts("OK\n");
}
}
__spinor_read_id(&id);
if(!id)
return -1;
printf("spinor id:0x%x\n",id);
spinor_flash_inited ++;
if(spi_size > 16*1024*1024/512)
{
spinor_4bytes_addr_mode = 1;
spinor_enter_4bytes_addr(ENABLE_4BYTES);
}
if(uboot_spare_head.boot_data.work_mode != WORK_MODE_BOOT)
{
spinor_store_buffer = (char *)malloc(SPINOR_STORE_BUFFER_SIZE);
if(!spinor_store_buffer)
{
puts("memory malloced fail for store buffer\n");
return -1;
}
}
spinor_write_cache = (char *)malloc_noncache(64 * 1024);
if(!spinor_write_cache)
{
puts("memory malloced fail for spinor data buffer\n");
if(spinor_store_buffer)
{
free(spinor_store_buffer);
return -1;
}
}
id_size = ARRAY_SIZE(spi_nor_ids);
for (i = 0; i < id_size; i++) {
if ((id == spi_nor_ids[i].id) && (spi_nor_ids[i].flags == 1)) {
quad_flag = 1;
if (set_quad_mode(spi_nor_ids[i].mode_cmd1, spi_nor_ids[i].mode_cmd2))
return -1;
}
}
script_parser_fetch("boot_spi_board0", "boot_spi_speed_hz", &spi_freq, 1);
printf("spi_freq @sys_cfg: spi_freq = %d\n",spi_freq);
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_exit(int force)
{
int need_exit = 0;
int ret = 0;
if(!spinor_flash_inited)
{
printf("sunxi spinor has not inited\n");
return -1;
}
if(force == 1)
{
if(spinor_flash_inited)
{
printf("force sunxi spinor exit\n");
spinor_flash_inited = 0;
need_exit = 1;
}
}
else
{
if(spinor_flash_inited)
{
spinor_flash_inited --;
}
if(!spinor_flash_inited)
{
printf("sunxi spinor is exiting\n");
need_exit = 1;
}
}
if(need_exit)
{
//printf("spi cache block == %d \n",spinor_cache_block);
//printf("spi cache addr == %x \n",(uint)spinor_write_cache);
//if((spinor_cache_block >= 0) && (spinor_write_cache))
//{
// if(__spinor_sector_write(spinor_cache_block * SPINOR_BLOCK_SECTORS, SPINOR_BLOCK_SECTORS, spinor_write_cache))
// {
// printf("spinor write cache block fail\n");
// ret = -1;
// }
//}
//?????ر?spinor??????
//spic_exit(0);
}
return ret;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_read(uint start, uint nblock, void *buffer)
{
int tmp_block_index;
tick_printf("spinor read: start 0x%x, sector 0x%x\n", start, nblock);
if(spinor_cache_block < 0)
{
debug("%s %d\n", __FILE__, __LINE__);
if(__spinor_sector_read(start, nblock, buffer))
{
printf("spinor read fail no buffer\n");
return 0;
}
}
else
{
tmp_block_index = start/SPINOR_BLOCK_SECTORS;
if(spinor_cache_block == tmp_block_index)
{
memcpy(buffer, spinor_write_cache + (start % SPINOR_BLOCK_SECTORS) * 512, nblock * 512);
}
else
{
if(__spinor_sector_read(start, nblock, buffer))
{
printf("spinor read fail with buffer\n");
return 0;
}
}
}
return nblock;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_write(uint start, uint nblock, void *buffer)
{
int tmp_block_index;
printf("spinor write: start 0x%x, sector 0x%x\n", start, nblock);
if(spinor_cache_block < 0)
{
spinor_cache_block = start/SPINOR_BLOCK_SECTORS;
if(__spinor_sector_read(spinor_cache_block * SPINOR_BLOCK_SECTORS, SPINOR_BLOCK_SECTORS, spinor_write_cache))
{
printf("spinor read cache block fail\n");
return 0;
}
__spinor_erase_block(spinor_cache_block);
memcpy(spinor_write_cache + (start % SPINOR_BLOCK_SECTORS) * 512, buffer, nblock * 512);
}
else
{
tmp_block_index = start/SPINOR_BLOCK_SECTORS;
if(spinor_cache_block == tmp_block_index)
{
memcpy(spinor_write_cache + (start % SPINOR_BLOCK_SECTORS) * 512, buffer, nblock * 512);
}
else
{
if(__spinor_sector_write(spinor_cache_block * SPINOR_BLOCK_SECTORS, SPINOR_BLOCK_SECTORS, spinor_write_cache))
{
printf("spinor write cache block fail\n");
return 0;
}
spinor_cache_block = tmp_block_index;
if(__spinor_sector_read(spinor_cache_block * SPINOR_BLOCK_SECTORS, SPINOR_BLOCK_SECTORS, spinor_write_cache))
{
printf("spinor read cache block fail\n");
return 0;
}
__spinor_erase_block(spinor_cache_block);
memcpy(spinor_write_cache + (start % SPINOR_BLOCK_SECTORS) * 512, buffer, nblock * 512);
}
}
return nblock;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_flush_cache(void)
{
if((spinor_cache_block >= 0) && (spinor_write_cache))
{
if(__spinor_sector_write(spinor_cache_block * SPINOR_BLOCK_SECTORS, SPINOR_BLOCK_SECTORS, spinor_write_cache))
{
printf("spinor write cache block fail\n");
return -1;
}
}
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters : erase: 0:
*
* 1: erase the all flash
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_erase_all_blocks(int erase)
{
if(erase) //??????Ϊ0????ʾ???????????Զ??ж??Ƿ???Ҫ?????<3F><>?
{
__spinor_erase_all();
}
return 0;
}
int spinor_erase(int erase, void *mbr_buffer)
{
int i = 0,start = 0;
int ret =-1;
unsigned int from, nr,erase_block;
sunxi_mbr_t *mbr = (sunxi_mbr_t *)mbr_buffer;
if (!erase)
return 0;
for (i=0;i<mbr->PartCount;i++)
{
printf("erase %s part\n", mbr->array[i].name);
from = mbr->array[i].addrlo + CONFIG_SPINOR_LOGICAL_OFFSET;
nr = mbr->array[i].lenlo;
printf("from:0x%x,nblock:0x%x \n",from,nr);
for (start=from;start<(from+nr);start+=SPINOR_BLOCK_SECTORS)
{
erase_block = start/SPINOR_BLOCK_SECTORS;
printf("erasing block index:%x (0x%x)\n",erase_block,(from+nr)/SPINOR_BLOCK_SECTORS);
ret = __spinor_erase_block(erase_block);
if (ret <0)
{
printf("erase %s part fail\n", mbr->array[i].name);
return -1;
}
}
}
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
int spinor_size(void)
{
int size = 0;
int ret = -1;
ret = script_parser_fetch("norflash", "size", &size, 1);
if (ret < 0 )
{
size = 8*1024*1024/512;
printf("get flash_size warning\n");
}
else
{
size = size*1024*1024/512;
}
printf("flash size =0x%x sectors\n",size);
return size;
}
static int __spinor_sprite_sector_write(uint start,uint nsector,void* buffer)
{
int sector_index;
debug("start: 0x%x, nsector: 0x%x \n",start,nsector);
if (nsector > SPINOR_BLOCK_SECTORS-start % SPINOR_BLOCK_SECTORS)
{
printf("sector cnt error\n");
return -1;
}
sector_index = start/SPINOR_BLOCK_SECTORS;
if ((start % SPINOR_BLOCK_SECTORS)||(nsector < SPINOR_BLOCK_SECTORS))
{
if (__spinor_sector_read(sector_index * SPINOR_BLOCK_SECTORS, SPINOR_BLOCK_SECTORS, spinor_store_buffer))
{
printf("spinor read sector fail\n");
return -1;
}
}
memcpy((spinor_store_buffer + ((start % SPINOR_BLOCK_SECTORS) * 512)), buffer, nsector*512);
if (__spinor_erase_block(sector_index))
{
printf("erase 0x%x sector fail\n",sector_index);
return -1;
}
if (__spinor_sector_write(sector_index*SPINOR_BLOCK_SECTORS,SPINOR_BLOCK_SECTORS,spinor_store_buffer))
{
printf("spinor write 0x%x sector fail\n",sector_index);
return -1;
}
#ifdef SPINOR_DEBUG
/* verify */
if (__spinor_sector_read(start, nsector, spinor_store_buffer))
{
printf("spinor read sector fail\n");
return -1;
}
if (memcmp(spinor_store_buffer,buffer,nsector*512))
{
printf("***write 0x%x sector fail***\n",sector_index*SPINOR_BLOCK_SECTORS);
return -1;
}
#endif
return SPINOR_BLOCK_SECTORS;
}
int spinor_sprite_write(uint start, uint nblock, void *buffer)
{
uint start_sector;
int ret;
int offset;
int nsector = 0;
int sector_once_write = SPINOR_BLOCK_SECTORS;
if (nblock < SPINOR_BLOCK_SECTORS)
{
sector_once_write = nblock;
}
offset = start%SPINOR_BLOCK_SECTORS;
/* deal with first sector,make it align with 64k */
if (offset)
{
nsector = SPINOR_BLOCK_SECTORS - offset;
ret = __spinor_sprite_sector_write(start, nsector, buffer);
if (ret < 0)
{
printf("spinor sprite write fail\n");
return 0;
}
}
for (start_sector =start+nsector;start_sector<start+nblock;start_sector+= SPINOR_BLOCK_SECTORS)
{
if (start+nblock - start_sector < SPINOR_BLOCK_SECTORS)
sector_once_write = start+nblock - start_sector;
ret = __spinor_sprite_sector_write(start_sector,sector_once_write, (void *)((uint)buffer + (start_sector -start) * 512));
if (ret < 0)
{
printf("spinor sprite write fail\n");
return 0;
}
}
return nblock;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
//static void spinor_dump(void *buffer, int len)
//{
// char *addr;
// int i;
//
// addr = (char *)buffer;
// for(i=0;i<len;i++)
// {
// printf("%02x ", addr[i]);
// if((i & 0x07) == 0x07)
// {
// puts("\n");
// }
// }
//}
int update_boot0_dram_para(char *buffer)
{
boot0_file_head_t *boot0 = (boot0_file_head_t *)buffer;
int i;
uint *addr = (uint *)DRAM_PARA_STORE_ADDR;
//У???????ַ??Ƿ???ȷ
printf("%s\n", boot0->boot_head.magic);
if(strncmp((const char *)boot0->boot_head.magic, BOOT0_MAGIC, MAGIC_SIZE))
{
printf("sunxi sprite: boot0 magic is error\n");
return -1;
}
if(sunxi_spinor_verify_checksum((void *)buffer, boot0->boot_head.length, boot0->boot_head.check_sum))
{
printf("sunxi sprite: boot0 checksum is error\n");
return -1;
}
for(i=0;i<32;i++)
{
printf("dram para[%d] = %x\n", i, addr[i]);
}
memcpy((void *)&boot0->prvt_head.dram_para, (void *)DRAM_PARA_STORE_ADDR, 32 * 4);
set_boot_dram_update_flag(boot0->prvt_head.dram_para);
/* regenerate check sum */
boot0->boot_head.check_sum = sunxi_spinor_generate_checksum(buffer, boot0->boot_head.length, boot0->boot_head.check_sum);
//У???????Ƿ???ȷ
if(sunxi_spinor_verify_checksum((void *)buffer, boot0->boot_head.length, boot0->boot_head.check_sum))
{
printf("sunxi sprite: boot0 checksum is error\n");
return -1;
}
printf("update dram para success \n");
return 0;
}
int spinor_fetch_uboot_length(void *buffer)
{
int length = 0;
if(gd->bootfile_mode == SUNXI_BOOT_FILE_NORMAL)
{
struct spare_boot_head_t *uboot = (struct spare_boot_head_t *)buffer;
printf("uboot magic %s\n", uboot->boot_head.magic);
if(strncmp((const char *)uboot->boot_head.magic, UBOOT_MAGIC, MAGIC_SIZE))
{
printf("sunxi sprite: uboot magic is error\n");
return -1;
}
length = uboot->boot_head.length;
}
else
{
sbrom_toc1_head_info_t *toc1 = (sbrom_toc1_head_info_t *)buffer;
if(gd->bootfile_mode == SUNXI_BOOT_FILE_PKG )
{
printf("uboot_pkg magic 0x%x\n", toc1->magic);
}
else
{
printf("toc magic 0x%x\n", toc1->magic);
}
if(toc1->magic != TOC_MAIN_INFO_MAGIC)
{
printf("sunxi sprite: toc magic is error\n");
return -1;
}
length = toc1->valid_len;
}
return length;
}
int spinor_fetch_boot0_length(void *buffer)
{
int length = 0;
if(gd->bootfile_mode == SUNXI_BOOT_FILE_NORMAL || gd->bootfile_mode == SUNXI_BOOT_FILE_PKG)
{
boot0_file_head_t *boot0 = (boot0_file_head_t *)buffer;
debug("%s\n", boot0->boot_head.magic);
if(strncmp((const char *)boot0->boot_head.magic, BOOT0_MAGIC, MAGIC_SIZE))
{
printf("sunxi sprite: boot0 magic is error\n");
return -1;
}
length = boot0->boot_head.length;
}
else
{
toc0_private_head_t *toc0 = (toc0_private_head_t *)buffer;
debug("%s\n", (char *)toc0->name);
if(strncmp((const char *)toc0->name, TOC0_MAGIC, MAGIC_SIZE))
{
printf("sunxi sprite: toc0 magic is error\n");
return -1;
}
length = toc0->length;
}
return length;
}
int spinor_download_uboot(uint length, void *buffer)
{
int ret = -1;
ret = spinor_sprite_write(UBOOT_START_SECTOR_IN_SPINOR, length/512, buffer);
if (ret < 0)
{
printf("spinor download uboot fail\n");
return -1;
}
return 0;
}
int spinor_download_boot0(uint length, void *buffer)
{
int ret = -1;
if(update_boot0_dram_para(buffer))
{
return -1;
}
ret = spinor_sprite_write(0, length/512, buffer);
if (ret < 0)
{
printf("spinor download boot0 fail\n");
return -1;
}
return 0;
}
int spinor_flash_download_uboot(uint length, void *buffer)
{
int ret = -1;
ret = spinor_write(UBOOT_START_SECTOR_IN_SPINOR, length/512, buffer);
if (ret < 0)
{
printf("spinor download uboot fail\n");
return -1;
}
return 0;
}
int spinor_flash_download_boot0(uint length, void *buffer)
{
int ret = -1;
if(update_boot0_dram_para(buffer))
{
return -1;
}
ret = spinor_write(0, length/512, buffer);
if (ret < 0)
{
printf("spinor download boot0 fail\n");
return -1;
}
return 0;
}
int spinor_read_uboot(uint length, void *buffer)
{
int ret = -1;
ret = spinor_read(UBOOT_START_SECTOR_IN_SPINOR, length/512, buffer);
if (ret < 0)
{
printf("spinor read uboot fail\n");
return -1;
}
return 0;
}
int spinor_read_boot0(uint length, void *buffer)
{
int ret = -1;
ret = spinor_read(0, length/512, buffer);
if (ret < 0)
{
printf("spinor read boot0 fail\n");
return -1;
}
return 0;
}
int spinor_datafinish(void)
{
uint id = 0xff;
int ret;
printf("spinor_datafinish\n");
__spinor_read_id(&id);
printf("spinor id = 0x%x\n", id);
__spinor_wrsr(0);
__spinor_erase_all();
printf("spinor has erasered\n");
if(update_boot0_dram_para(spinor_store_buffer))
{
return -1;
}
flush_cache((unsigned long)spinor_store_buffer,total_write_bytes);
printf("total write bytes = %d\n", total_write_bytes);
ret = __spinor_sector_write(0, total_write_bytes/512, spinor_store_buffer);
if(ret)
{
printf("spinor write img fail\n");
return -1;
}
// buffer = (char *)malloc(8 * 1024 * 1024);
// memset(buffer, 0xff, 8 * 1024 * 1024);
// ret = __spinor_sector_read(0, total_write_bytes/512, buffer);
// if(ret)
// {
// printf("spinor read img fail\n");
//
// return -1;
// }
// printf("spinor read data ok\n");
// for(i=0;i<total_write_bytes;i++)
// {
// if(buffer[i] != spinor_store_buffer[i])
// {
// printf("compare spinor read and write error\n");
// printf("offset %d\n", i);
//
// return -1;
// }
// }
spinor_enter_4bytes_addr(DISABLE_4BYTES);
printf("spinor download data ok\n");
return 0;
}
/*
************************************************************************************************************
*
* function
*
* name :
*
* parmeters :
*
* return :
*
* note :
*
*
************************************************************************************************************
*/
//int nand_get_mbr(char *buffer, int mbr_size)
//{
// memcpy(spinor_mbr, buffer, SUNXI_MBR_SIZE);
// return 0;
//}
#ifdef SPI_NOR_TEST
s32 spi_nor_rw_test(u32 spi_no)
{
const u32 test_len = 256*64;
char* wbuf = (char*)malloc(test_len);
char* rbuf = (char*)malloc(test_len);
u32 i;
s32 ret;
uint id =0xffff;
if(spinor_init(0))
{
printf("spinor init failed \n");
free(wbuf);
free(rbuf);
return -1;
}
if(__spinor_read_id(&id))
{
printf("spinor read id failed \n");
free(wbuf);
free(rbuf);
return -1;
}
__spinor_wrsr(0);
printf("the id is %x\n",id);
printf("bfeore spi_nor_rw_test\n");
__spinor_sector_read(0, 1, rbuf);
for(i = 0; i < 256; i+=4)
{
printf("verify rbuf=0x%x\n",*(u32 *)(rbuf+i));
}
printf("before erase all \n");
ret = __spinor_erase_all();
printf("after erase all \n");
printf("bfeore spi_nor_rw_test,ret=%d\n",ret);
if (!wbuf || !rbuf) {
printf("spi %d malloc buffer failed\n", spi_no);
return -1;
}
for (i=0; i<test_len; i++)
wbuf[i] = i;
i = 0;
ret = __spinor_sector_read( i, 1, rbuf);
if (ret) {
free(wbuf);
printf("spi %d read page %d failed\n", spi_no,i);
return -1;
}
for (i = 0; i < (test_len >> 8)/4; i++)
printf("verify rbuf=[0x%x]\n",*(u32 *)(rbuf+i*4));
// ret = __spinor_erase_block(0); //erase 64K
// if (ret) {
// printf("spi erase ss1 nor failed\n");
// return -1;
// }
for (i=0; i<10; i++)
printf("b rbuf=[0x%x]\n",*(u32 *)(rbuf));
for (i = 0; i < (test_len >> 8); i++) {
printf("begin to write \n");
ret = __spinor_sector_write(i, 1, wbuf);
if (ret) {
printf("spi %d write page %d failed\n", spi_no,i);
free(wbuf);
free(rbuf);
return -1;
}
printf("end to write \n");
memset(rbuf, 0, 256);
printf("begin to read\n");
ret = __spinor_sector_read(i, 1, rbuf);
if (ret) {
printf("spi %d read page %d failed\n", spi_no,i);
free(wbuf);
return -1;
}
printf("end to read \n");
//compare data
if (memcmp(wbuf, rbuf, 256)) {
printf("spi %d page %d read/write failed\n", spi_no, i);
while(1);
return -1;
} else
printf("spi %d page %d read/write ok\n", spi_no, i);
}
free(wbuf);
free(rbuf);
return 0;
}
#endif
u32 try_spi_nor(u32 spino)
{
uint id =0xffff;
if(spinor_init(spino))
{
printf("spinor init failed \n");
return -1;
}
if(__spinor_read_id(&id))
{
printf("spinor read id failed \n");
return -1;
}
if(id == 0xfffff)
{
return -1;
}
printf("spinor id is %x \n",id);
return 0;
}
static void spinor_enter_4bytes_addr(int enable)
{
int command = 0;
if(spinor_4bytes_addr_mode == 1 && enable == 1)
command = 0xB7;
else if(spinor_4bytes_addr_mode == 1 && enable == 0)
command = 0xE9;
else
return ;
spic_config_dual_mode(0, 0, 0, 1);
spic_rw(1, (void*)&command, 0, 0);
return;
}
static void spinor_config_addr_mode(u8 *sdata, uint page_addr, uint *num, u8 cmd)
{
if ((sdata == NULL)||(num == NULL))
{
return;
}
if(spinor_4bytes_addr_mode == 0)
{
*num = 4;
sdata[0] = cmd;
sdata[1] = (page_addr >> 16) & 0xff;
sdata[2] = (page_addr >> 8 ) & 0xff;
sdata[3] = page_addr & 0xff;
}
else if(spinor_4bytes_addr_mode == 1)
{
*num = 5;
sdata[0] = cmd;
sdata[1] = (page_addr >> 24) & 0xff;
sdata[2] = (page_addr >> 16) & 0xff;
sdata[3] = (page_addr >> 8 ) & 0xff;
sdata[4] = page_addr & 0xff;
}
}
static int set_quad_mode(u8 cmd1, u8 cmd2)
{
u8 i = 0;
int ret = 0;
u8 reg[2] = {0};
u8 sdata[3] = {0};
uint txnum, rxnum;
txnum = 3;
rxnum = 0;
ret = __spinor_wren();
if (ret == -1)
goto __err_out_;
sdata[0] = SPINOR_WRSR;
sdata[1] = cmd1;
sdata[2] = cmd2;
spic_config_dual_mode(0, 0, 0, txnum);
ret = spic_rw(txnum, (void *)sdata, rxnum, (void *)0);
if (ret == -1)
goto __err_out_;
do {
ret = __spinor_rdsr(&reg[0]);
if (ret == RET_FAIL)
goto __err_out_;
mdelay(5);
i++;
if (i > 4)
goto __err_out_;
} while (reg[0] & SPINOR_WRSR);
ret = __spinor_rdsr(&reg[1]);
if ((!ret) & (reg[1] & (cmd2 | cmd1)))
printf("Quad Mode Enable OK...\n");
else
goto __err_out_;
return 0;
__err_out_:
return ret;
}
static int spi_nor_fast_read_quad_output(uint start, uint sector_cnt, void *buf)
{
uint page_addr;
uint rbyte_cnt;
int ret = 0;
u8 sdata[5] = {0};
uint tmp_cnt, tmp_offset = 0;
void *tmp_buf;
uint txnum, rxnum;
txnum = 5;
while (sector_cnt) {
if (sector_cnt > 127)
tmp_cnt = 127;
else
tmp_cnt = sector_cnt;
page_addr = (start + tmp_offset) * SYSTEM_PAGE_SIZE;
rbyte_cnt = tmp_cnt * SYSTEM_PAGE_SIZE;
sdata[0] = SPINOR_FREAD_QUAD_OUT;
sdata[1] = (page_addr >> 16) & 0xff;
sdata[2] = (page_addr >> 8) & 0xff;
sdata[3] = page_addr & 0xff;
sdata[4] = 0;
rxnum = rbyte_cnt;
tmp_buf = (u8 *)buf + (tmp_offset << 9);
spic_config_dual_mode(0, 2, 0, txnum);
if (spic_rw(txnum, (void *)sdata, rxnum, (void *)tmp_buf)) {
ret = -1;
break;
}
sector_cnt -= tmp_cnt;
tmp_offset += tmp_cnt;
}
spic_config_dual_mode(0, 0, 0, 0);
return ret;
}