blob: d64314daf1be53f13daeacc5826c3f542e201f04 [file] [log] [blame]
#include "io.h"
#include "string.h"
#include "debug.h"
#include "flash_adaptor.h"
static int g_block_size = 0;
static int g_page_size = 0;
static int g_addr_cycle = 0;
/*################################################################*/
/*##############below are the real function #################################*/
#ifdef ENABLE_EMMC
#include "sdmmc_api.h"
#ifndef UNUSED
#define UNUSED(var) do { (void)(var); } while(0)
#endif
static unsigned int current_part = 0xF;
extern int do_emmcinit(void);
extern int do_emmcread(unsigned int start, unsigned int blks, unsigned char * buffer);
extern int do_emmc_switch_part(UINT32 PartitionNumber);
extern long long do_emmc_capacity(void);
static void init_emmc()
{
int ret = 0;
g_block_size = 0x80000;
g_page_size = 8192;
g_addr_cycle = 0;
ret = do_emmcinit() ;
if (ret) {
ERR("emmc init fail.\n");
//FIXME
//reset_soc();
}
}
// part 0(user area), 1(b1), 2(b2)
static int switch_emmc_part(unsigned int part)
{
if(part > 2)
return FLASH_SWITCH_PART_NOEXIST;
if(current_part != part)
current_part = part;
else
return 0; // if we have switched to this part, just return
if(do_emmc_switch_part(part)){
ERR("EMMC: switch to boot partion %d error.\n", part);
return FLASH_OP_ERR;
}
return 0;
}
static long long read_emmc(long long start, unsigned int size, unsigned char *buff)
{
long long ret;
int cur, offset = 0;
int blks = (size + SDIO_BLK_SIZE - 1) / SDIO_BLK_SIZE;
if(start % SDIO_BLK_SIZE) {
ERR("The start address should be %d bytes aligned for EMMC.\n", SDIO_BLK_SIZE);
return -1;
}
do {
/*
* The 65535 constraint comes from some hardware has
* only 16 bit width block number counter
*/
cur = (blks > 65535) ? 65535 : blks;
ret = do_emmcread(start + offset * SDIO_BLK_SIZE, cur, buff + offset * SDIO_BLK_SIZE);
if (ret != 0){
ERR("EMMC: read emmc image fail.\n");
return ret;
} else {
//ADAP_PRN(PRN_RES, "start 0x%08x, size 0x%08x, buff 0x%08x\n",
// (start + offset * SDIO_BLK_SIZE), cur, (buff + offset * SDIO_BLK_SIZE));
}
blks -= cur;
offset += cur;
} while (blks > 0);
return 0;
}
extern int do_emmcwrite(unsigned int start, unsigned int blks, unsigned char * buffer);
static long long write_emmc(long long start, unsigned int size, unsigned char *buff)
{
long long ret;
unsigned int blks = (size + SDIO_BLK_SIZE - 1) / SDIO_BLK_SIZE;
ret = do_emmcwrite(start, blks, buff);
return ret;
}
static unsigned int emmc_dev_id_inc(unsigned int dev_id)
{
dev_id++;
#ifndef CONFIG_GPT
/* if we reach EBR area, need add 1 to skip it */
if (dev_id == 4)
dev_id++;
#endif
return dev_id;
}
#endif
#ifdef ENABLE_NAND
#include "nand_priv.h"
// nand is a problem because there is gap between the driver in sys_init and bootloader
#ifdef NAND_SECURE_BOOT // for sys_init
#include "memmap.h"
#include "global.h"
extern int mv_nand_chip_reinit(void);
extern int mv_nand_reset_chip(int getchip);
#endif
#define CRC32_SIZE (4)
extern struct mv_nand_data nand_data ;
extern int init_nand_data(struct mv_nand_data * board);
extern int mv_nand_block_bad(loff_t ofs, int getchip);
extern int mv_nand_read_block(long long nand_start, char* data_buf, int data_size);
//FIXME: different funciton in sys_init and bootloader
unsigned char partition_type_s[16];
//FIXME: i don't like such fixed address
#define NW_START 0x01F00000
static void init_nand()
{
unsigned int nand_param_buff[4];
nand_data.szofpg = g_page_size;
nand_data.t = g_addr_cycle;
nand_data.szofblk = g_block_size;
if((g_page_size != 512) && (g_page_size != 2048) &&
(g_page_size != 4096) && (g_page_size != 8192)) {
memcpy((char*)nand_param_buff, (char *)(NW_START + 16), 16);
nand_data.szofpg = nand_param_buff[0];
nand_data.t = nand_param_buff[1];
nand_data.szofblk = nand_param_buff[2];
set_flash_parameters(nand_data.szofblk, nand_data.szofpg, nand_data.t);
NOTICE("[%s:%d] get nand parameters from tz_loder (block_size=%d, page_size=%d, ecc=%d)\n",
__func__, __LINE__, nand_data.szofblk, nand_data.szofpg, nand_data.t);
} else {
NOTICE("\nNAND block size %d, page size %d, ecc_strength %d\n",
nand_data.szofblk, nand_data.szofpg, nand_data.t);
}
//FIXME: this is for nand driver of google
strncpy((CHAR *)partition_type_s, "eslc", 4);
init_nand_data(&nand_data);
}
//part 1 - 8 for nand
static int switch_nand_part(unsigned int part)
{
if(part > 8)
return FLASH_SWITCH_PART_NOEXIST;
//if part = 0, return 0
return (part * nand_data.szofblk);
}
static long long read_nand(long long start, unsigned int size, unsigned char *buff)
{
long long ret = 0;
int block_num = 0;
int data_size = size;
unsigned int block_start_addr = 0, read_addr = start;
char * data_buf = (char *)buff;
unsigned int i = 0;
unsigned int block_size;
block_size = get_block_size();
block_start_addr = start & (~(block_size - 1));
block_num = (data_size / block_size);
//read the data in first block
do {
if(mv_nand_block_bad((loff_t)block_start_addr, 0)) {
ERR("bad block found 0x%08x!!!\n", block_start_addr);
if(block_start_addr <= (8 * block_size))// the first 9 blocks are defined as boot blocks
return -1;
block_start_addr += block_size;
start += block_size;
continue;
}
ret = mv_nand_read_block(start, data_buf, size);//return the size of read
if(size == ret) // read finish
return 0;
if(ret < 0) // error
return ret;
//block_start_addr = start + ret;
data_buf += ret;
data_size = size - ret; // remain size
read_addr = start + ret;
break;
}while(1);
for(i = 0; i < (unsigned int)block_num;) {
//2. check if the block is bad
if(mv_nand_block_bad((loff_t)read_addr, 0)) {
ERR("bad block found 0x%08x!!!\n", read_addr);
read_addr += block_size;
continue;
}
ret = mv_nand_read_block(read_addr, data_buf, block_size);
if(ret < 0)
return ret;
data_buf += block_size; // move data buffer forward
read_addr += block_size; // move read address forward
data_size -= block_size; // decrease the size
i++;
}
//read the rest data
if(data_size > 0) {
do {
if(mv_nand_block_bad((loff_t)read_addr, 0)) {
ERR("bad block found 0x%08x!!!\n", read_addr);
read_addr += block_size;
continue;
}
ret = mv_nand_read_block(read_addr, data_buf, data_size);
if(ret < 0)
return ret;
break;
} while(1);
}
return 0;
}
static unsigned int nand_dev_id_inc(unsigned int dev_id)
{
dev_id++;
return dev_id;
}
#endif
#ifdef NOR_BOOT
extern void SPIReadFlash(unsigned int, unsigned int, unsigned int);
static void init_nor()
{
g_block_size = 0x10000;
g_page_size = 512;
g_addr_cycle = 0;
//NULL
}
static int switch_nor_part(unsigned int part)
{
if(part > 1)
return FLASH_SWITCH_PART_NOEXIST;
//if part = 0, return 0
return 0;
}
static long long read_nor(unsigned int start, unsigned int size, unsigned char *buff)
{
//we read spi 4bytes by 4bytes
int mod = size % 4;
size = size/4;
if(mod) {
size++;
}
SPIReadFlash((start + 0xF0000000), size,(uintptr_t)buff);
return 0;
}
static unsigned int nor_dev_id_inc(unsigned int dev_id)
{
dev_id++;
return dev_id;
}
#endif
/*##################################################################*/
/*##############below are the unified function #################################*/
/*##################################################################*/
void set_flash_parameters(int blocksize, int pagesize, int addrcycle)
{
#ifdef ENABLE_EMMC
g_block_size = 0x80000;
g_page_size = 8192;
g_addr_cycle = 0;
UNUSED(blocksize);
UNUSED(pagesize);
UNUSED(addrcycle);
#endif
#ifdef ENABLE_NAND
g_block_size = blocksize;
g_page_size = pagesize;
g_addr_cycle = addrcycle;
#endif
#ifdef NOR_BOOT
g_block_size = 0x10000;
g_page_size = 512;
g_addr_cycle = 0;
UNUSED(blocksize);
UNUSED(pagesize);
UNUSED(addrcycle);
#endif
INFO("block: %d, page: %d, cycle: %d\n", g_block_size, g_page_size, g_addr_cycle);
}
inline int get_block_size()
{
return g_block_size;
}
int get_page_size()
{
return g_page_size;
}
int get_addr_cycle()
{
return g_addr_cycle;
}
int get_boot_partition_number(void)
{
#ifdef ENABLE_EMMC
return 2;
#endif
#ifdef ENABLE_NAND
return 8;
#endif
#ifdef NOR_BOOT
return 1;
#endif
}
void init_flash()
{
#ifdef ENABLE_EMMC
set_flash_parameters(0, 0, 0);
init_emmc();
#endif
#ifdef ENABLE_NAND
init_nand();
//FIXME: there is something difference between nand init in sys_init and bl
#endif
#ifdef NOR_BOOT
//FIXME: nor driver is only for sys_int->u-boot, not support boot gtv from nor
set_flash_parameters(0, 0, 0);
init_nor();
#endif
}
//return value is the start address in part
//this function is only for reading bootloader.subimg
int switch_flash_part(unsigned int part)
{
#ifdef ENABLE_EMMC
return switch_emmc_part(part);
#endif
#ifdef ENABLE_NAND
return switch_nand_part(part);
#endif
#ifdef NOR_BOOT
return switch_nor_part(part);
#endif
return 0;
}
long long read_flash(long long start, unsigned int size, unsigned char *buff)
{
#ifdef ENABLE_EMMC
return read_emmc(start, size, buff);
#endif
#ifdef ENABLE_NAND
return read_nand(start, size, buff);
//there is something difference about nand init in sys_init
#endif
#ifdef NOR_BOOT
return read_nor(start, size, buff);
#endif
}
long long get_flash_capacity(void)
{
#ifdef ENABLE_EMMC
return do_emmc_capacity();
#else
return 0; // don't support
#endif
}
long long read_flash_from_part(unsigned int part, long long start, unsigned int size, unsigned char *buff)
{
int part_addr = 0;
//switch to specific part
part_addr = switch_flash_part(part);
if(part_addr < 0) {
ERR("flash: error when switch to boot partion %d(err = %d).\n", part, part_addr);
return FLASH_OP_ERR;
}
return read_flash((part_addr + start), size, buff);
}
long long write_flash(long long start, unsigned int size, unsigned char *buff)
{
#ifdef ENABLE_EMMC
return write_emmc(start, size, buff);
#else
// don't support now
ERR("flash: not support API %s !\n", __func__);
return 0;
#endif
}
unsigned int flash_dev_id_inc(unsigned int dev_id)
{
#ifdef ENABLE_EMMC
return emmc_dev_id_inc(dev_id);
#endif
#ifdef ENABLE_NAND
return nand_dev_id_inc(dev_id);
//there is something difference about nand init in sys_init
#endif
#ifdef NOR_BOOT
return nor_dev_id_inc(dev_id);
#endif
}
#if 0
static flash_adaptor_t flash_adaptor_interface = {
get_block_size,
get_page_size,
get_addr_cycle,
get_boot_partition_number,
init_flash,
switch_flash_part,
flash_dev_id_inc,
read_flash
};
void * get_flash_adaptor_if(void)
{
return (void *)(&flash_adaptor_interface);
}
#endif