| #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 |