nt9856x/BSP/u-boot/board/novatek/common/nvt_nand_partition.c
2023-03-28 15:07:53 +08:00

521 lines
12 KiB
C

/*
* Author: Will Fu
* Created: Aug, 26, 2015
* Copyright: Novatek Inc.
*
*/
#include <common.h>
#include <linux/mtd/nand.h>
#include <linux/crc32.h>
#include <nvt_nand_partition.h>
#include <nvt_npt.h>
#include <nand.h>
#include <nvt_nand_xbootdat.h>
static struct nand_partition_table g_npt;
static int g_nvt_ptn_init_done = 0;
static int32_t ptn_num = -1;
static int32_t vol_num = -1;
/*** NAND Util ***/
uint32_t nand_get_page_size(void)
{
struct mtd_info *mtd = nand_info[nand_curr_device];
return mtd->writesize;
}
uint32_t nand_get_block_size(void)
{
struct mtd_info *mtd = nand_info[nand_curr_device];
return mtd->erasesize;
}
uint32_t nand_total_blocks(void)
{
struct mtd_info *mtd = nand_info[nand_curr_device];
printf("NAND : total blocks number: %d\n", (uint32_t)(mtd->size >> mtd->erasesize_shift));
return (uint32_t)(mtd->size >> mtd->erasesize_shift);
}
uint32_t nand_calc_block_addr(uint32_t start_block)
{
struct mtd_info *mtd = nand_info[nand_curr_device];
return start_block << mtd->erasesize_shift;
}
uint32_t nand_get_bad_block_size(uint32_t offset, uint32_t length)
{
struct mtd_info *mtd = nand_info[nand_curr_device];
u32 offend, i ,bbcnt;
if ((offset + length) > mtd->size) {
printf("NVT WARNING: %s exceed NAND chip size!", __func__);
offend = mtd->size;
} else {
offend = offset+length;
}
bbcnt = 0;
for(i=offset; i < offend;i += mtd->erasesize) {
if (nand_block_isbad(mtd, i))
bbcnt++;
}
return (bbcnt << mtd->size >> mtd->erasesize_shift);
}
static int32_t _convert_to_big_unit(char *buf, uint64_t size, const char *name)
{
uint64_t mask_M = (1<<20) -1;
uint64_t mask_K = (1<<10) -1;
if ((size & mask_M) == 0) {
sprintf(buf, "%lluM(%s)", size >> 20, name);
return 0;
}
if ((size & mask_K) == 0) {
sprintf(buf, "%lluK(%s)", size >> 10, name);
return 0;
}
sprintf(buf, "%llu(%s)", size , name);
return 0;
}
static int32_t _nvt_nand_get_ptn_env(char *msg)
{
int32_t i = 0;
char buf[128] = {0};
for (i=0; i < ptn_num; i++) {
_convert_to_big_unit(buf, g_npt.npt_part[i].size, g_npt.npt_part[i].name);
strcat(msg, buf);
if (i < ptn_num - 1) {
strcat(msg, ",");
}
}
return 0;
}
static void _nvt_nand_part_env_set(void)
{
char data[1024]= {0};
char msg[1024] = {0};
int ret = 0;
//env_set mtdids 'nand0=atmel_nand'
//env_set mtdparts 'mtdparts=atmel_nand:8M(bootstrap)ro,-(rootfs)'
/* set mtdids */
ret = run_command("env_set mtdids 'nand0=nvt_nand'", 0);
if(ret) {
printf("env_set mtdids fail!\n");
return;
}
/* set mtdparts */
_nvt_nand_get_ptn_env(msg);
sprintf(data, "env_set mtdparts 'mtdparts=nvt_nand:%s'", msg);
ret = run_command(data, 0);
if(ret) {
printf("env_set mtdparts fail!\n");
return;
}
}
int32_t nvt_nand_ptn_get_num_of_table(void)
{
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return -1;
}
return ptn_num;
}
char* nvt_nand_get_ptn_name(int32_t ptn_idx)
{
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return 0;
}
if (ptn_idx < 0 || ptn_idx >= ptn_num) {
printf("%s:[Error] out of partition idx range!\n", __func__);
return 0;
}
return (char *)(g_npt.npt_part[ptn_idx].name);
}
int32_t nvt_nand_get_ptn_index(char *name)
{
int32_t i = 0;
int32_t idx = -1;
char *search_name;
search_name = name;
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return -1;
}
for(i = 0; i < ptn_num; i++) {
if(!strcmp(g_npt.npt_part[i].name, search_name)) {
idx = i;
break;
}
}
if (idx == -1)
printf("%s:[ERROR] '%s' ptn idx not found!\n", __func__, name);
return idx;
}
uint32_t nand_calc_partition_addr(int32_t ptn_idx)
{
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return 0;
}
if (ptn_idx < 0 || ptn_idx >= ptn_num) {
printf("%s:[Error] out of partition idx range!\n", __func__);
return 0;
}
return g_npt.npt_part[ptn_idx].offset;
}
uint32_t nand_calc_partition_size(int32_t ptn_idx)
{
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return 0;
}
if (ptn_idx < 0 || ptn_idx >= ptn_num) {
printf("%s:[Error] out of partition idx range!\n", __func__);
return 0;
}
return g_npt.npt_part[ptn_idx].size;
}
uint32_t nand_calc_partition_block(int32_t ptn_idx)
{
struct mtd_info *mtd = nand_info[nand_curr_device];
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return 0;
}
if (ptn_idx < 0 || ptn_idx >= ptn_num) {
printf("%s:[Error] out of partition idx range!\n", __func__);
return 0;
}
return (uint32_t)(g_npt.npt_part[ptn_idx].offset) >> mtd->erasesize_shift;
}
uint32_t nand_calc_max_image_size(int32_t ptn_idx)
{
uint32_t bad_len,max_len;
if (g_nvt_ptn_init_done != 1) {
printf("%s:[ERROR] no init NPT\n", __func__);
return 0;
}
if (ptn_idx < 0 || ptn_idx >= ptn_num) {
printf("%s:[Error] out of partition idx range!\n", __func__);
return 0;
}
max_len = nand_calc_partition_size(ptn_idx);
bad_len = nand_get_bad_block_size(nand_calc_partition_addr(ptn_idx), nand_calc_partition_size(ptn_idx));
max_len -= bad_len;
return max_len;
}
static int32_t _nvt_nand_read_npt(struct nand_partition_table* npt)
{
int ret = 0;
char cmd[128] = {0};
uint32_t page_size;
uint32_t read_size;
uint32_t npt_partition_sz;
uint32_t npt_partition_addr;
uint32_t verify_crc;
npt_partition_sz = nt72_npt_get_store_blk_nr() * nand_get_block_size();
npt_partition_addr = nt72_npt_get_store_blk_offset() * nand_get_block_size();
page_size = nand_get_page_size();
read_size = sizeof(struct nand_partition_table);
/* ROUND UP page alignment */
if (read_size % page_size) {
read_size = (read_size & ~(page_size - 1)) + page_size;
}
/* Read block */
memset((void *)CONFIG_FWUP_RAM_START, 0xff, npt_partition_sz);
sprintf(cmd, "nand read 0x%x 0x%x 0x%x", (unsigned int)CONFIG_FWUP_RAM_START,
npt_partition_addr, read_size);
ret = run_command(cmd, 0);
if(ret < 0) {
printf("read NPT fail!\n");
goto out;
}
/* Copy to struct */
memcpy(npt, (void *)CONFIG_FWUP_RAM_START, sizeof(struct nand_partition_table));
/* Verify struct */
verify_crc = crc32(0, (const unsigned char *)(npt->npt_part), sizeof(struct nvt_partition)* MAX_NR_PART);
verify_crc = crc32(verify_crc, (const unsigned char *)(npt->npt_vol), sizeof(struct nvt_ubi_vol)* MAX_NR_PART);
if (npt->crc != verify_crc) {
printf("Verify NPT fail!\n");
ret = -1;
goto out;
}
ret = 0;
out:
return ret;
}
static int32_t _nvt_nand_ptn_get_entry_number(struct nand_partition_table *npt)
{
int num = 0;
int i = 0;
for (i = 0; i < MAX_NR_PART; i++) {
if (strlen(npt->npt_part[i].name) == 0) {
break;
}
num ++;
}
return num;
}
static int32_t _nvt_nand_ptn_get_vol_number(struct nand_partition_table *npt)
{
int num = 0;
int i = 0;
for (i = 0; i < MAX_NR_PART; i++) {
if (strlen(npt->npt_vol[i].name) == 0) {
break;
}
num ++;
}
return num;
}
static int32_t _nvt_nand_ptn_check_block_num(void)
{
int32_t i;
uint64_t maxblocks;
uint64_t blocksnum;
struct mtd_info *mtd = nand_info[nand_curr_device];
maxblocks = mtd->size >> mtd->erasesize_shift;
printf("maxblocks: %lld, size: %lld, shitf: %d\n", maxblocks, mtd->size, mtd->erasesize_shift);
blocksnum = g_npt.npt_part[ptn_num-1].offset + g_npt.npt_part[ptn_num-1].size;
blocksnum = blocksnum >> mtd->erasesize_shift;
if (blocksnum < (maxblocks - BBT_MAX_BLOCK_NUM)) {
printf("NVT PTN WARNING: Some Nand blocks not used!!! , use %llu blocks\n",blocksnum);
}
else if(blocksnum > (maxblocks - BBT_MAX_BLOCK_NUM)){
for (i=0 ; i < 5; i++) {
printf("NVT PTN ERROR: Over max Nand blocks!!! , use %llu blocks\n",blocksnum);
}
}
return 0;
}
int32_t nvt_ubi_get_vol_by_name(struct nvt_ubi_vol *vol, const char *vol_name)
{
int i = 0;
for (i = 0; i < vol_num; i ++) {
if (!strcmp(g_npt.npt_vol[i].name, vol_name)) {
memcpy(vol , &(g_npt.npt_vol[i]), sizeof(struct nvt_ubi_vol));
return 0;
}
}
/* Not found */
return -1;
}
static int32_t _nvt_ubi_vol_storage_sync_mtd(struct nand_partition_table *npt, int mtd_idx)
{
char cmd_buf[256] = {0};
int cmd_ret;
int i;
int vol_sum;
int maybe_repartion = 0;
vol_sum = 0;
for (i = 0; i < vol_num; i ++) {
if (npt->npt_vol[i].mtd_idx == mtd_idx)
vol_sum ++;
}
if (vol_sum == 0)
return 0;
sync_start:
if (maybe_repartion == 1) {
/* Do erase partition first */
sprintf(cmd_buf, "nand erase 0x%x 0x%x",nand_calc_partition_addr(mtd_idx),nand_calc_partition_size(mtd_idx));
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("Error: Erase %s fail!\n", nvt_nand_get_ptn_name(mtd_idx));
goto err;
}
}
/* Check mtd_idx */
/* Attach part */
sprintf(cmd_buf, "ubi part %s", nvt_nand_get_ptn_name(mtd_idx));
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("Error: ubi part %s not found!\n", nvt_nand_get_ptn_name(mtd_idx));
goto repart;
}
/* Check each vol with size and id */
for (i = 0; i < vol_num; i ++) {
if (npt->npt_vol[i].mtd_idx != mtd_idx)
continue;
sprintf(cmd_buf, "ubi nvt_checkvol %s 0x%llx %d",
npt->npt_vol[i].name, npt->npt_vol[i].size, npt->npt_vol[i].vol_idx);
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("Error: check vol size/id fail!, Recreate\n");
/* Remove */
sprintf(cmd_buf, "ubi remove %s", npt->npt_vol[i].name);
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
/* Create */
sprintf(cmd_buf, "ubi nvt_create %s 0x%llx d %d",
npt->npt_vol[i].name, npt->npt_vol[i].size, npt->npt_vol[i].vol_idx);
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("[ERROR] Can not create vol! ret=%d\n", cmd_ret);
return -1;
}
}
}
/* Check vol num */
sprintf(cmd_buf, "ubi nvt_checkvolnum %d", vol_sum);
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("Error: check vol num fail!\n");
goto repart;
}
/* sync pass */
return 0;
repart:
/* Removeall */
sprintf(cmd_buf, "ubi nvt_removeall");
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("Error: ubi removeall fail!\n");
maybe_repartion = 1;
goto sync_start;
}
/* Create each vol */
for (i = 0; i < vol_num; i ++) {
if (npt->npt_vol[i].mtd_idx != mtd_idx)
continue;
sprintf(cmd_buf, "ubi nvt_create %s 0x%llx d %d",
npt->npt_vol[i].name, npt->npt_vol[i].size, npt->npt_vol[i].vol_idx);
cmd_ret = run_command(cmd_buf, 0);
printf("'%s' return: %d\n",cmd_buf, cmd_ret);
if (cmd_ret != 0) {
printf("[ERROR] Can not create vol! ret=%d\n", cmd_ret);
goto err;
}
}
return 0;
err:
return -1;
}
static int32_t _nvt_ubi_vol_storage_sync(struct nand_partition_table *npt)
{
int i, ret;
for (i = 0 ; i < ptn_num ; i ++) {
ret = _nvt_ubi_vol_storage_sync_mtd(npt, i);
if (ret != 0)
return -1;
}
return 0;
}
int32_t nvt_nand_show_npt(void)
{
/* Dump MTD Part */
nt72_npt_print_mtd_parts(g_npt.npt_part, ptn_num);
}
/* nvt_nand_ptn_init
* Return value:
* 0: init done
* 1: init read npt fail, by-pass
* -1: something error
*/
int32_t nvt_nand_ptn_init(void)
{
int32_t ret = -1;
/* Reset ptn struct */
g_nvt_ptn_init_done = 0;
memset(&g_npt, 0, sizeof(struct nand_partition_table));
/* Read ptn */
ret = _nvt_nand_read_npt(&g_npt);
if (ret != 0) {
/* by-pass init and return 1 */
printf("Read npt Fail, by-pass partition init!\n");
ret = 1;
goto out;
}
/* Get number of ptn */
ptn_num = _nvt_nand_ptn_get_entry_number(&g_npt);
/* Get number of vol */
vol_num = _nvt_nand_ptn_get_vol_number(&g_npt);
/* Print nand total blocks */
nand_total_blocks();
/* Check partition layout is OK or not */
_nvt_nand_ptn_check_block_num();
/* Set mtd ptn into mtdparts for prepare_kercmd */
_nvt_nand_part_env_set();
g_nvt_ptn_init_done = 1;
/* Sync ubi volume in flash */
ret = _nvt_ubi_vol_storage_sync(&g_npt);
out:
return ret;
}