sdk-hwV1.3/lichee/linux-4.9/modules/nand/common1/phy-nand/nand-partition3/nand_partitions.c

1087 lines
32 KiB
C

/*
* nand_partitions.c
*
* Copyright (C) 2019 Allwinner.
*
* cuizhikui <cuizhikui@allwinnertech.com>
* 2021-09-24
*
* SPDX-License-Identifier: GPL-2.0
*/
#include "sunxi_nand.h"
#include "sunxi_nand_boot.h"
#include "sunxi_nand_partitions.h"
#include <linux/kernel.h>
#include "../nand_nftl.h"
#include <linux/types.h>
#include <linux/printk.h>
extern struct nand_partitions *get_nand_parts(void);
extern int nand_physic_lock(void);
extern int nand_physic_unlock(void);
extern int virtual_badblock_check(unsigned int nDieNum, unsigned int nBlkNum);
extern int read_virtual_page(unsigned int nDieNum, unsigned int nBlkNum,
unsigned int nPage, uint64_t SectBitmap, void *pBuf, void *pSpare);
extern int write_virtual_page(unsigned int nDieNum, unsigned int nBlkNum,
unsigned int nPage, uint64_t SectBitmap, void *pBuf, void *pSpare);
extern int erase_virtual_block(unsigned int nDieNum, unsigned int nBlkNum);
extern int nand_wait_all_rb_ready(void);
extern int virtual_badblock_mark(unsigned int nDieNum, unsigned int nBlkNum);
extern int nand_secure_storage_first_build(unsigned int start_block);
extern __u32 nand_get_twoplane_flag(void);
extern unsigned int nand_get_super_chip_page_size(void);
extern unsigned int nand_get_super_chip_spare_size(void);
extern unsigned int nand_get_super_chip_block_size(void);
extern unsigned int nand_get_super_chip_size(void);
extern unsigned int nand_get_super_chip_cnt(void);
struct kernel_status kernelsta;
struct nand_partitions nand_parts;
int free_phy_partition(struct _nand_phy_partition *phy_partition)
{
nand_free(phy_partition->factory_bad_block);
nand_free(phy_partition->new_bad_block);
nand_free(phy_partition);
return 0;
}
int free_nand_partition(struct _nand_partition *nand_partition)
{
free_phy_partition(nand_partition->phy_partition);
nand_free(nand_partition);
return 0;
}
struct nand_partitions *get_nand_parts(void)
{
return &nand_parts;
}
struct _nand_phy_partition *get_head_phy_partition_from_nand_info(struct _nand_info *nand_info)
{
return nand_info->phy_partition_head;
}
void set_cache_level(struct _nand_info *nand_info, unsigned short cache_level)
{
nand_info->cache_level = cache_level;
}
struct _nand_disk *get_disk_from_phy_partition(struct _nand_phy_partition *phy_partition)
{
return phy_partition->disk;
}
struct _nand_phy_partition *get_next_phy_partition(struct _nand_phy_partition *phy_partition)
{
return phy_partition->next_phy_partition;
}
uint16 get_partitionNO(struct _nand_phy_partition *phy_partition)
{
return phy_partition->PartitionNO;
}
uint64_t bitmap_change(unsigned short SectBitmap)
{
uint64_t bitmap = 0;
int i;
if (get_storage_type() == 1) {
bitmap = SectBitmap & 0xff;
} else if (get_storage_type() == 2) {
for (i = 0, bitmap = 0; i < SectBitmap; i++) {
bitmap <<= 1;
bitmap |= 0x01;
}
}
return bitmap;
}
void wait_all_rb(void)
{
nand_physic_lock();
nand_wait_all_rb_ready();
nand_physic_unlock();
}
int page_read(unsigned short nDieNum, unsigned short nBlkNum, unsigned short nPage, unsigned short SectBitmap, void *pBuf, void *pSpare)
{
uint64_t bitmap;
int ret;
nand_physic_lock();
bitmap = bitmap_change(SectBitmap);
ret = read_virtual_page(nDieNum, nBlkNum, nPage, bitmap, pBuf, pSpare);
nand_physic_unlock();
return ret;
}
/*****************************************************************************
*Name :
*Description :
*Parameter :
*Return :
*Note :
*****************************************************************************/
int page_write(unsigned short nDieNum, unsigned short nBlkNum, unsigned short nPage, unsigned short SectBitmap, void *pBuf, void *pSpare)
{
uint64_t bitmap;
int ret;
nand_physic_lock();
bitmap = bitmap_change(SectBitmap);
ret = write_virtual_page(nDieNum, nBlkNum, nPage, bitmap, pBuf, pSpare);
nand_physic_unlock();
return ret;
}
int block_erase(unsigned short nDieNum, unsigned short nBlkNum)
{
int ret;
nand_physic_lock();
ret = erase_virtual_block(nDieNum, nBlkNum);
nand_physic_unlock();
return ret;
}
void change_block_addr(struct _nand_partition *nand, struct _nand_super_block *super_block, unsigned short nBlkNum)
{
super_block->Chip_NO = nand->phy_partition->StartBlock.Chip_NO;
super_block->Block_NO = nand->phy_partition->StartBlock.Block_NO + nBlkNum;
while (super_block->Block_NO >= nand->phy_partition->nand_info->BlkPerChip) {
super_block->Chip_NO++;
super_block->Block_NO -= nand->phy_partition->nand_info->BlkPerChip;
}
}
int nand_erase_superblk(struct _nand_partition *nand, struct _physic_par *p)
{
int ret;
struct _nand_super_block super_block;
change_block_addr(nand, &super_block, p->phy_page.Block_NO);
cur_e_lb_no = p->phy_page.Block_NO;
ret = nand->phy_partition->block_erase(super_block.Chip_NO, super_block.Block_NO);
return ret;
}
int nand_read_page(struct _nand_partition *nand, struct _physic_par *p)
{
int ret;
struct _nand_super_block super_block;
change_block_addr(nand, &super_block, p->phy_page.Block_NO);
ret = nand->phy_partition->page_read(super_block.Chip_NO, super_block.Block_NO, p->phy_page.Page_NO, p->page_bitmap, p->main_data_addr, p->spare_data_addr);
return ret;
}
int nand_write_page(struct _nand_partition *nand, struct _physic_par *p)
{
int ret;
struct _nand_super_block super_block;
if (nand->phy_partition->CrossTalk != 0) {
//wait RB
wait_all_rb();
}
change_block_addr(nand, &super_block, p->phy_page.Block_NO);
cur_w_lb_no = p->phy_page.Block_NO;
cur_w_pb_no = super_block.Block_NO;
cur_w_p_no = p->phy_page.Page_NO;
ret = nand->phy_partition->page_write(super_block.Chip_NO, super_block.Block_NO, p->phy_page.Page_NO, p->page_bitmap, p->main_data_addr, p->spare_data_addr);
return ret;
}
int is_factory_bad_block(struct _nand_info *nand_info, unsigned short nDieNum, unsigned short nBlkNum)
{
uint32_t num, i;
num = FACTORY_BAD_BLOCK_SIZE / sizeof(struct _nand_super_block);
for (i = 0; i < num; i++) {
if ((nand_info->factory_bad_block[i].Chip_NO == nDieNum) && (nand_info->factory_bad_block[i].Block_NO == nBlkNum)) {
return 1;
}
if (nand_info->factory_bad_block[i].Chip_NO == 0xffff) {
break;
}
}
return 0;
}
int is_new_bad_block(struct _nand_info *nand_info, unsigned short nDieNum, unsigned short nBlkNum)
{
uint32_t num, i;
num = PHY_PARTITION_BAD_BLOCK_SIZE / sizeof(struct _nand_super_block);
for (i = 0; i < num; i++) {
if ((nand_info->new_bad_block[i].Chip_NO == nDieNum) && (nand_info->new_bad_block[i].Block_NO == nBlkNum)) {
return 1;
}
if (nand_info->new_bad_block[i].Chip_NO == 0xffff) {
break;
}
}
return 0;
}
int nand_is_blk_good(struct _nand_partition *nand, struct _physic_par *p)
{
int ret;
struct _nand_super_block super_block;
ret = 0;
change_block_addr(nand, &super_block, p->phy_page.Block_NO);
ret = is_factory_bad_block(nand->phy_partition->nand_info, super_block.Chip_NO, super_block.Block_NO);
if (ret != 0) {
return NFTL_NO;
}
ret = is_new_bad_block(nand->phy_partition->nand_info, super_block.Chip_NO, super_block.Block_NO);
if (ret != 0) {
return NFTL_NO;
}
return NFTL_YES;
}
int add_new_bad_block(struct _nand_info *nand_info, unsigned short nDieNum, unsigned short nBlkNum)
{
uint32 num, i;
num = PHY_PARTITION_BAD_BLOCK_SIZE / sizeof(struct _nand_super_block);
for (i = 0; i < num; i++) {
if (nand_info->new_bad_block[i].Chip_NO == 0xffff) {
break;
}
}
if (i < num) {
nand_info->new_bad_block[i].Chip_NO = nDieNum;
nand_info->new_bad_block[i].Block_NO = nBlkNum;
} else {
NFTL_ERR("[NE]Too much new bad block\n");
}
return 0;
}
int nand_mark_bad_blk(struct _nand_partition *nand, struct _physic_par *p)
{
int ret;
struct _nand_super_block super_block;
unsigned char spare[BYTES_OF_USER_PER_PAGE];
memset(spare, 0, BYTES_OF_USER_PER_PAGE);
change_block_addr(nand, &super_block, p->phy_page.Block_NO);
nand_physic_lock();
ret = virtual_badblock_mark(super_block.Chip_NO, super_block.Block_NO);
nand_physic_unlock();
add_new_bad_block(nand->phy_partition->nand_info, super_block.Chip_NO, super_block.Block_NO);
return ret;
}
int nand_get_logic_start_block(struct _nand_info *nand_info, int state)
{
/*boot0 area | uboot area | secure storage area | reserved area | logic area*/
unsigned int block_temp, logic_start_block;
struct _boot_info *boot_info = nand_info->boot;
if (state == KERNEL_IN_BOOT)
return boot_info->logic_start_block;
block_temp = nand_info->boot->uboot_next_block;
block_temp = nand_secure_storage_first_build(block_temp);
block_temp = block_temp + nand_info->boot->physic_block_reserved;
logic_start_block = block_temp;
if (nand_get_twoplane_flag() == 1) {
/*super block unit*/
logic_start_block = logic_start_block / 2;
if (block_temp % 2)
logic_start_block++;
}
pr_info("support two plane:%d , logic start block@%d\n",
nand_get_twoplane_flag(), logic_start_block);
return logic_start_block;
}
/*
*int nand_info_partitions_init(struct _nand_info *nand_info, unsigned char *mbr_data)
*{
*
* int last_num = 0, nums = 0, new_partition;
* PARTITION_MBR *mbr;
* unsigned int part_cnt, i, m;
* int p = 0;
*
* mbr = (PARTITION_MBR *)mbr_data;
*
* memset(nand_info->partition, 0xff, sizeof(nand_info->partition));
*
* nand_info->partition[0].cross_talk = 0;
* nand_info->partition[0].attribute = 0;
* nand_info->partition_nums = 1;
*
* return 0;
*}
*/
int BlockCheck(unsigned short nDieNum, unsigned short nBlkNum)
{
int ret = 0;
nand_physic_lock();
ret = virtual_badblock_check(nDieNum, nBlkNum);
nand_physic_unlock();
return ret;
}
void print_bad_blocks(struct _nand_super_block *badblocks, int len)
{
int i = 0;
for (i = 0; i < len; i++) {
if (badblocks[i].Chip_NO != 0xffff && badblocks[i].Block_NO != 0xffff)
printk("%d-%d ", badblocks[i].Chip_NO, badblocks[i].Block_NO);
}
printk("\n");
}
int nand_scan_phy_partitions_bad_blocks(struct _nand_info *nand_info)
{
int die = 0;
int block = 0;
int factory_bad_blocks = 0;
int new_bad_blocks = 0;
int bad_blocks = 0;
if (nand_info->FirstBuild == 1) {
for (die = nand_info->no_used_block_addr.Chip_NO;
die < nand_info->ChipNum; die++) {
for (block = nand_info->no_used_block_addr.Block_NO;
block < nand_info->BlkPerChip; block++) {
if (kernelsta.nand_bootinfo_state.state == BOOT_INFO_OK) {
/*not erase whole chip to burn would be this case*/
if (is_factory_bad_block(nand_info, die, block) == 1) {
factory_bad_blocks++;
if (factory_bad_blocks == FACTORY_BAD_BLOCK_SIZE / sizeof(struct _nand_super_block)) {
pr_err("[NE]too much factory bad block %d\n", new_bad_blocks);
return -1;
}
} else if (BlockCheck(die, block) != 0) {
nand_info->new_bad_block[new_bad_blocks].Chip_NO = die;
nand_info->new_bad_block[new_bad_blocks].Block_NO = block;
new_bad_blocks++;
if (new_bad_blocks == PHY_PARTITION_BAD_BLOCK_SIZE / sizeof(struct _nand_super_block)) {
pr_err("\n[NE]too much new bad block %d\n", factory_bad_blocks);
return -1;
}
}
} else {
if (BlockCheck(die, block) != 0) {
nand_info->factory_bad_block[factory_bad_blocks].Chip_NO = die;
nand_info->factory_bad_block[factory_bad_blocks].Block_NO = block;
factory_bad_blocks++;
if (factory_bad_blocks == FACTORY_BAD_BLOCK_SIZE / sizeof(struct _nand_super_block)) {
pr_err("\n[NE]too much bad block %d\n", factory_bad_blocks);
return -1;
}
}
}
}
}
bad_blocks = factory_bad_blocks + new_bad_blocks;
if (bad_blocks != 0) {
printk("factory bad block:\n");
print_bad_blocks(nand_info->factory_bad_block, bad_blocks);
}
if (new_bad_blocks != 0) {
printk("new bad block:\n");
print_bad_blocks(nand_info->new_bad_block, new_bad_blocks);
}
} else {
/*scan bad block in nftl when normal boot*/
}
return bad_blocks;
}
static int get_max_free_block_num(struct _nand_info *nand_info)
{
int max_free_block_num, sector_per_block;
uint32_t total_sector;
sector_per_block = nand_info->SectorNumsPerPage * nand_info->PageNumsPerBlk;
total_sector = sector_per_block * nand_info->BlkPerChip;
total_sector *= nand_info->ChipNum;
if (total_sector <= 0x64000) { //less than 200MB {
max_free_block_num = 40;
} else if (total_sector <= 0x96000) { //less than 300MB {
max_free_block_num = 85;
} else if (total_sector <= 0x12c000) { //less than 600MB {
if (sector_per_block >= 2048) {
max_free_block_num = 40;
} else if (sector_per_block >= 1024) {
max_free_block_num = 80;
} else {
max_free_block_num = 180;
}
} else if (total_sector <= 0x258000) { //less than 1200MB{
if (sector_per_block >= 1024) {
max_free_block_num = 170;
} else {
max_free_block_num = 320;
}
} else if (total_sector <= 0xa00000) { //less than 5GB {
if (sector_per_block >= 16384) {
max_free_block_num = 50;
} else if (sector_per_block >= 8182) {
max_free_block_num = 75;
} else if (sector_per_block >= 4096) {
max_free_block_num = 170;
} else {
max_free_block_num = 300;
}
} else if (total_sector <= 0x1400000) { //less than 10GB{
if (sector_per_block >= 16384) {
max_free_block_num = 75;
} else if (sector_per_block >= 8182) {
max_free_block_num = 160;
} else if (sector_per_block >= 4096) {
max_free_block_num = 300;
} else {
max_free_block_num = 600;
}
} else if (total_sector <= 0x2800000) { //less than 20GB {
if (sector_per_block >= 32768) {
max_free_block_num = 70;
} else if (sector_per_block >= 16384) {
max_free_block_num = 150;
} else if (sector_per_block >= 8182) {
max_free_block_num = 160;
} else {
max_free_block_num = 240;
}
} else { //32G {
if (sector_per_block >= 32768) {
max_free_block_num = 140;
} else if (sector_per_block >= 16384) {
max_free_block_num = 250;
} else {
max_free_block_num = 500;
}
}
return max_free_block_num;
}
unsigned int nand_reserve_blocks(struct _nand_info *nand_info,
unsigned int total_good_blocks)
{
unsigned int resv = 0;
unsigned int total_block_nums = 0;
int max_free_block_num = 0;
total_block_nums = total_good_blocks;
resv = total_block_nums / (NORM_RESERVED_BLOCK_RATIO + nand_info->capacity_level * 3);
if (resv < nand_info->mini_free_block_first_reserved) {
resv = nand_info->mini_free_block_first_reserved;
}
max_free_block_num = get_max_free_block_num(nand_info);
if (resv > max_free_block_num) {
resv = max_free_block_num;
}
pr_debug("nand reserve block nums@%u\n", resv);
return resv;
}
/*predefine from sys_partition.fex*/
uint32_t nand_predefine_parts_size(struct nand_partitions *nparts)
{
int i = 0;
uint32_t size = 0;
struct nand_part *np = nparts->parts;
for (i = 0; i < nparts->nc; i++) {
size += np[i].size;
}
return size;
}
uint32_t nand_calc_phy_partitions_size(struct _nand_info *nand_info, int total_blocks, int bad_block_nums)
{
uint32_t partitions_size = 0; /*sector as unit*/
uint32_t total_good_blocks = 0;
uint32_t partitions_blocks = 0;
pr_debug("cal partitions size:[%d-%d]\n", total_blocks, bad_block_nums);
total_good_blocks = total_blocks - bad_block_nums;
partitions_blocks = total_good_blocks - nand_reserve_blocks(nand_info,
total_good_blocks);
partitions_size = partitions_blocks * nand_info->PageNumsPerBlk;
partitions_size *= nand_info->SectorNumsPerPage;
return partitions_size;
}
uint32_t nand_get_phy_partitions_size(struct _nand_info *nand_info, struct _boot_info *boot)
{
uint32_t size = 0;
struct _partition *parts = boot->partition.data;
if (nand_info->bootinfo_is_old == BOOTINFO_OLD) {
size = parts->size;
pr_info("boot info is old, the parts size@%u\n", size);
} else
size = boot->parts.size;
return size;
}
uint32_t nand_calc_udisk_capacity(struct _nand_info *nand_info)
{
struct _nand_phy_partition *phy_partition = NULL;
struct nand_partitions *predef_nand_parts = NULL;
uint32_t defsize = 0, total_avalid_size = 0;
if (nand_info == NULL || nand_info->phy_partition_head == NULL) {
pr_err("%s nand_info or nand info phy_partition_head is null\n",
__func__);
return NOSPACE;
}
phy_partition = nand_info->phy_partition_head;
predef_nand_parts = get_nand_parts();
defsize = nand_predefine_parts_size(predef_nand_parts);
total_avalid_size = phy_partition->TotalSectors;
if (defsize > total_avalid_size) {
pr_err("[NPE] define partitons size@%u greater than avalidable space@%u\n",
defsize, total_avalid_size);
return NOSPACE;
}
return total_avalid_size - defsize;
}
void nand_partitions_phy_range(struct nand_partitions *parts, struct _nand_super_block start,
struct _nand_super_block end)
{
parts->start = start;
parts->end = end;
}
void nand_partitions_append_udisk(struct nand_partitions *parts, unsigned int udisk_size)
{
int p = 0;
for (p = 0; p < parts->nc; p++) {
parts->parts[parts->nc].from += parts->parts[p].size;
}
if (parts->nc == 0)
parts->nc = 1;
parts->parts[parts->nc - 1].size = udisk_size;
/*update total size*/
parts->size += udisk_size;
pr_info("parts size:%u (sect)\n", parts->size);
}
int nand_check_partitions_consistent_legal(struct nand_partitions *new, struct nand_partitions *old)
{
int p = 0;
/*check partitions count*/
pr_debug("new nc:%d old nc:%d\n", new->nc, old->nc);
/*some a100 a133 boards's parts force to one parts*/
if (old->nc == 1 && old->size == new->size) {
pr_info("special machine: old parts count is one, old part size%u is the same with new size@%u\n", old->size, new->size);
return 0;
}
if (new->nc != old->nc) {
pr_err("update parts count is inconsistent [%d-%d]\n", new->nc, old->nc);
return NAND_PARTS_COUNT_INCONSISTENT;
}
for (p = 0; p < new->nc; p++) {
pr_debug("new->parts[%d].from:%u ,old->parts[%d].from:%u\n", p, new->parts[p].from, p, old->parts[p].from);
pr_debug("new->parts[%d].size:%u ,old->parts[%d].size:%u\n", p, new->parts[p].size, p, old->parts[p].size);
if (new->parts[p].from != old->parts[p].from ||
new->parts[p].size != old->parts[p].size) {
pr_err("update parts size is inconsitent\n");
return NAND_PARTS_SIZE_INCONSISTENT;
}
}
return 0;
}
/*for update new version(parts2.0 --> parts3.0)*/
void nand_parts2_to_parts3(struct nand_partitions *parts3, PARTITION_MBR *parts2_mbr, _PARTITION *parts2_parts, unsigned int old_parts_size)
{
int p = 0;
unsigned int size = 0;
parts3->size = old_parts_size;
parts3->nc = parts2_mbr->PartCount;
parts3->cross_talk = parts2_parts->data[0].cross_talk;
parts3->attribute = parts2_parts->data[0].attribute;
parts3->start = parts2_parts->data[0].start;
parts3->end = parts2_parts->data[0].end;
for (p = 0; p < parts2_mbr->PartCount; p++) {
parts3->parts[p].from = parts2_mbr->array[p].addr;
parts3->parts[p].size = parts2_mbr->array[p].len;
size += parts3->parts[p].size;
}
/*udisk size*/
parts3->parts[p - 1].size = parts3->size - size;
pr_info("change parts2 to parts3\n");
}
/*for back to old version(parts3.o --> parts2.0)*/
void nand_parts3_to_parts2(struct nand_partitions *parts3, PARTITION_MBR *parts2_mbr,
_PARTITION *parts2_parts, unsigned int new_parts_size)
{
int p = 0;
if (parts3->nc > MAX_PART_COUNT_PER_FTL) {
parts2_mbr->PartCount = 1;
parts2_parts->data[0].size = new_parts_size;
parts2_parts->data[0].cross_talk = parts3->cross_talk;
parts2_parts->data[0].attribute = parts3->attribute;
parts2_parts->data[0].start = parts3->start;
parts2_parts->data[0].end = parts3->end;
parts2_parts->data[0].nand_disk[0].size = parts2_parts->data[0].size;
return;
}
/*build 2.0 mbr*/
parts2_mbr->PartCount = parts3->nc;
for (p = 0; p < parts3->nc; p++) {
parts2_mbr->array[p].addr = parts3->parts[p].from;
parts2_mbr->array[p].len = parts3->parts[p].size;
}
/*build 2.0 partition*/
parts2_parts->data[0].size = new_parts_size;
parts2_parts->data[0].cross_talk = parts3->cross_talk;
parts2_parts->data[0].attribute = parts3->attribute;
parts2_parts->data[0].start = parts3->start;
parts2_parts->data[0].end = parts3->end;
for (p = 0; p < parts3->nc; p++) {
parts2_parts->data[0].nand_disk[p].size = parts3->parts[p].size;
}
}
int nand_build_boot_info_f(struct _nand_info *nand_info, int state)
{
unsigned int uboot_start_block = 0, uboot_next_block = 0;
int ret;
if (nand_info == NULL) {
pr_err("%s nand_info is null\n", __func__);
return -1;
}
ret = nand_physic_info_read();
if (ret == -ENOMEM) {
pr_err("nand build boot info fail\n");
return -1;
}
nand_info->boot = phyinfo_buf;
if (nand_info->boot->append_new_magic != PARTS3P0_MAGIC) {
nand_info->bootinfo_is_old = BOOTINFO_OLD;
}
nand_info->boot->len = PHY_INFO_SIZE;
nand_info->boot->magic = PHY_INFO_MAGIC;
nand_info->boot->append_new_magic = PARTS3P0_MAGIC; /*parts3p0 -> 0x7061727473337030*/
if (kernelsta.nand_bootinfo_state.state == BOOT_INFO_NOEXIST)
return 0;
if (nand_info->bootinfo_is_old == BOOTINFO_OLD &&
kernelsta.nand_bootinfo_state.state == BOOT_INFO_OK) {
nand_parts2_to_parts3(&nand_info->boot->parts, &nand_info->boot->mbr.data,
&nand_info->boot->partition, nand_info->boot->partition.data[0].size);
} else {
nand_info->bootinfo_is_old = BOOTINFO_NULL;
nand_info->boot->physic_block_reserved = PHYSIC_RECV_BLOCK;
}
if (nand_info->boot->uboot_start_block == 0) {
get_default_uboot_block(&uboot_start_block, &uboot_next_block);
nand_info->boot->uboot_start_block = uboot_start_block;
nand_info->boot->uboot_next_block = uboot_next_block;
}
return 0;
}
int nand_build_boot_info_r(struct _nand_info *nand_info, int state)
{
struct _boot_info *boot_info = nand_info->boot;
if (nand_info == NULL || nand_info->boot == NULL) {
pr_err("%s nand_info or nand_info boot is null\n", __func__);
return -1;
}
if (state == KERNEL_IN_BOOT) {
/*for kernel compatible with old version (parts 2.0) boot*/
nand_parts3_to_parts2(&boot_info->parts, &boot_info->mbr.data,
&boot_info->partition, boot_info->parts.size);
return 0;
}
boot_info->logic_start_block = nand_get_logic_start_block(nand_info, state);
boot_info->no_use_block = boot_info->logic_start_block;
memcpy(boot_info->factory_block.ndata, nand_info->factory_bad_block, FACTORY_BAD_BLOCK_SIZE);
memcpy(&boot_info->parts, &nand_parts, sizeof(struct nand_partitions));
/*for kernel compatible with old version (parts 2.0) boot*/
if (nand_info->bootinfo_is_old == BOOTINFO_NULL) {
nand_parts3_to_parts2(&boot_info->parts, &boot_info->mbr.data,
&boot_info->partition, boot_info->parts.size);
}
return 0;
}
unsigned int get_phy_partition_num(struct _nand_info *nand_info)
{
unsigned int num = 0;
struct _nand_phy_partition *p;
p = nand_info->phy_partition_head;
while (p != NULL) {
num++;
p = p->next_phy_partition;
}
return num;
}
int nand_build_phy_partitions(struct _nand_phy_partition **partition_head, int state)
{
struct _nand_info *nand_info = container_of(partition_head, struct _nand_info, phy_partition_head);
struct _nand_phy_partition *phy_partition = NULL;
struct _partition *part = NULL;
unsigned int total_blocks = 0;
uint32_t partitions_size = 0; /*sector as unit*/
int bad_block_nums = 0;
if (partition_head == NULL) {
pr_err("%s partition_head is null", __func__);
return -1;
}
bad_block_nums = nand_scan_phy_partitions_bad_blocks(nand_info);
if (bad_block_nums == -1) {
pr_err("[NE] too much bad block, build phy partitions fail\n");
return -1;
}
phy_partition = (struct _nand_phy_partition *)nand_malloc(sizeof(struct _nand_phy_partition));
if (phy_partition == NULL) {
pr_err("[NE]%s: malloc fail for phy_partition\n", __func__);
return -ENOMEM;
}
memset(phy_partition, 0, sizeof(struct _nand_phy_partition));
phy_partition->PartitionNO = 0;
part = &nand_info->boot->partition.data[phy_partition->PartitionNO];
total_blocks = nand_info->ChipNum * (nand_info->BlkPerChip - nand_info->no_used_block_addr.Block_NO);
if (state == KERNEL_IN_PRODUCT) {
partitions_size = nand_calc_phy_partitions_size(nand_info, total_blocks, bad_block_nums);
phy_partition->CrossTalk = 0;
phy_partition->Attribute = 0;
phy_partition->StartBlock.Chip_NO = nand_info->no_used_block_addr.Chip_NO;
phy_partition->StartBlock.Block_NO = nand_info->no_used_block_addr.Block_NO;
phy_partition->EndBlock.Chip_NO = nand_info->ChipNum - 1;
phy_partition->EndBlock.Block_NO = nand_info->BlkPerChip - 1;
} else {
partitions_size = nand_get_phy_partitions_size(nand_info, nand_info->boot);
phy_partition->CrossTalk = part->cross_talk;
phy_partition->Attribute = part->attribute;
phy_partition->StartBlock.Chip_NO = part->start.Chip_NO;
phy_partition->StartBlock.Block_NO = part->start.Block_NO;
phy_partition->EndBlock.Chip_NO = part->end.Chip_NO;
phy_partition->EndBlock.Block_NO = part->end.Block_NO;
}
phy_partition->SectorNumsPerPage = nand_info->SectorNumsPerPage;
phy_partition->BytesUserData = nand_info->BytesUserData;
phy_partition->PageNumsPerBlk = nand_info->PageNumsPerBlk;
phy_partition->TotalBlkNum = total_blocks;
phy_partition->GoodBlockNum = total_blocks - bad_block_nums;
phy_partition->FullBitmapPerPage = phy_partition->SectorNumsPerPage;
phy_partition->FreeBlock = 0;
phy_partition->TotalSectors = partitions_size;
phy_partition->page_read = page_read;
phy_partition->page_write = page_write;
phy_partition->block_erase = block_erase;
phy_partition->disk = nand_info->partition[phy_partition->PartitionNO].nand_disk;
phy_partition->disk->size = phy_partition->TotalSectors;
memcpy(phy_partition->disk->name, "nand0", strlen("nand0"));
phy_partition->disk->name[strlen("nand0")] = '\0';
phy_partition->disk->type = 0;
phy_partition->nand_info = nand_info;
*partition_head = phy_partition;
pr_debug("phy partition: page size: %u(sec)\n", phy_partition->SectorNumsPerPage);
pr_debug("phy partition: user size: %u(B)\n", phy_partition->BytesUserData);
pr_debug("phy partition: blks size: %u(page)\n", phy_partition->PageNumsPerBlk);
pr_debug("phy partition: full bitmap per page: %u\n", phy_partition->FullBitmapPerPage);
pr_debug("phy partition: attribute: %d\n", phy_partition->Attribute);
pr_debug("phy partition: crosstalk: %d\n", phy_partition->CrossTalk);
pr_debug("phy partition: free block: %d\n", phy_partition->FreeBlock);
pr_info("phy partition: start block: %d.%d\n", phy_partition->StartBlock.Chip_NO, phy_partition->StartBlock.Block_NO);
pr_info("phy partition: end block: %d.%d\n", phy_partition->EndBlock.Chip_NO, phy_partition->EndBlock.Block_NO);
pr_info("phy partition: total sectors: %u\n", phy_partition->TotalSectors);
return 0;
}
struct _nand_partition *build_nand_partition(struct _nand_phy_partition *phy_partition)
{
struct _nand_partition *partition;
if (phy_partition == NULL) {
pr_err("%s phy_partition is null", __func__);
return NULL;
}
partition = nand_malloc(sizeof(struct _nand_partition));
if (partition == NULL) {
pr_err("[NE]%s:malloc fail for partition\n", __func__);
return NULL;
}
partition->phy_partition = phy_partition;
partition->sectors_per_page = phy_partition->SectorNumsPerPage;
partition->spare_bytes = phy_partition->BytesUserData;
partition->pages_per_block = phy_partition->PageNumsPerBlk;
partition->bytes_per_page = partition->sectors_per_page;
partition->bytes_per_page <<= 9;
partition->bytes_per_block = partition->bytes_per_page * partition->pages_per_block;
partition->full_bitmap = phy_partition->FullBitmapPerPage;
/*partitions size include udisk part*/
partition->cap_by_sectors = phy_partition->TotalSectors;
partition->cap_by_bytes = partition->cap_by_sectors << 9;
partition->total_blocks = phy_partition->TotalBlkNum - phy_partition->FreeBlock;
/*total blocks include good and bad block*/
partition->total_by_bytes = partition->total_blocks;
partition->total_by_bytes *= partition->bytes_per_block;
partition->nand_erase_superblk = nand_erase_superblk;
partition->nand_read_page = nand_read_page;
partition->nand_write_page = nand_write_page;
partition->nand_is_blk_good = nand_is_blk_good;
partition->nand_mark_bad_blk = nand_mark_bad_blk;
memcpy(partition->name, "nand_partition0", sizeof("nand_partition0"));
partition->name[14] += phy_partition->PartitionNO;
pr_debug("[ND]partitions: name: %s\n", partition->name);
pr_info("partitions: sectors_per_page: %d\n", partition->sectors_per_page);
pr_info("partitions: spare_bytes: %d\n", partition->spare_bytes);
pr_info("partitions: capacity size: %llu(sec)\n", partition->cap_by_sectors);
pr_info("partitions: total size: %llu(sec)\n", partition->total_by_bytes >> 9);
return partition;
}
void set_capacity_level(struct _nand_info *nand_info, unsigned short capacity_level)
{
if (nand_info == NULL) {
pr_err("%s nand_info is null", __func__);
return;
}
if (capacity_level != 0)
nand_info->capacity_level = 1;
else
nand_info->capacity_level = 0;
}
/*@state: boot@0, product@1*/
int nand_info_init(struct _nand_info *nand_info, int state)
{
unsigned int bytes_per_page = 0;
int ret = 0;
unsigned int udisk_size = 0;
if (nand_info == NULL) {
pr_err("%s nand_info is null\n", __func__);
return -1;
}
nand_info->type = MLC_NAND;
nand_info->SectorNumsPerPage = nand_get_super_chip_page_size();
nand_info->BytesUserData = nand_get_super_chip_spare_size();
nand_info->PageNumsPerBlk = nand_get_super_chip_block_size();
nand_info->BlkPerChip = nand_get_super_chip_size();
nand_info->ChipNum = nand_get_super_chip_cnt();
nand_info->FirstBuild = (state == KERNEL_IN_PRODUCT) ? 1 : 0;
nand_info->mini_free_block_first_reserved = MIN_NAND_Free_BLOCK_NUM_V2;
nand_info->mini_free_block_reserved = MIN_NAND_Free_BLOCK_REMAIN;
nand_info->new_bad_page_addr = 0xffff;
memset(nand_info->partition, 0xff, sizeof(nand_info->partition));
nand_info->partition[0].cross_talk = 0;
nand_info->partition[0].attribute = 0;
nand_info->partition_nums = 1;
bytes_per_page = nand_info->SectorNumsPerPage;
bytes_per_page <<= 9;
nand_info->temp_page_buf = nand_malloc(bytes_per_page);
if (nand_info->temp_page_buf == NULL) {
pr_err("%s: malloc fail for temp_page_buf!\n", __func__);
ret = -ENOMEM;
goto out_nomem;
}
nand_info->factory_bad_block = nand_malloc(FACTORY_BAD_BLOCK_SIZE);
if (nand_info->factory_bad_block == NULL) {
pr_err("%s: malloc fail for factory_bad_block!\n", __func__);
ret = -ENOMEM;
goto out_nomem;
}
nand_info->new_bad_block = nand_malloc(PHY_PARTITION_BAD_BLOCK_SIZE);
if (nand_info->new_bad_block == NULL) {
pr_err("%s: malloc fail for new_bad_block!\n", __func__);
ret = -ENOMEM;
goto out_nomem;
}
nand_info->mbr_data = nand_malloc(sizeof(PARTITION_MBR));
if (nand_info->mbr_data == NULL) {
pr_err("%s: malloc fail for mbr_data!\n", __func__);
ret = -ENOMEM;
goto out_nomem;
}
if (nand_build_boot_info_f(nand_info, state) != 0) {
pr_err("%s: build boot info f fail\n", __func__);
goto out;
}
if (kernelsta.nand_bootinfo_state.state == BOOT_INFO_NOEXIST)
state = KERNEL_IN_PRODUCT;
nand_info->no_used_block_addr.Chip_NO = 0;
nand_info->no_used_block_addr.Block_NO = nand_get_logic_start_block(nand_info, state);
if (kernelsta.nand_bootinfo_state.state == BOOT_INFO_OK) {
memcpy(nand_info->factory_bad_block, &nand_info->boot->factory_block, FACTORY_BAD_BLOCK_SIZE);
} else {
memset(nand_info->factory_bad_block, 0xff, FACTORY_BAD_BLOCK_SIZE);
memset(nand_info->new_bad_block, 0xff, PHY_PARTITION_BAD_BLOCK_SIZE);
memset(nand_info->mbr_data, 0xff, sizeof(PARTITION_MBR));
}
if (nand_build_phy_partitions(&(nand_info->phy_partition_head), state) != 0) {
pr_err("%s: build phy partitions fail\n", __func__);
goto out;
}
if ((state == KERNEL_IN_PRODUCT) && ((udisk_size = nand_calc_udisk_capacity(nand_info)) == NOSPACE))
goto out;
else if (state == KERNEL_IN_PRODUCT) {
struct _nand_phy_partition *phy_partition = nand_info->phy_partition_head;
nand_partitions_append_udisk(&nand_parts, udisk_size);
nand_partitions_phy_range(&nand_parts, phy_partition->StartBlock, phy_partition->EndBlock);
}
nand_build_boot_info_r(nand_info, state);
return 0;
out:
out_nomem:
if (nand_info->temp_page_buf)
nand_free(nand_info->temp_page_buf);
if (nand_info->factory_bad_block)
nand_free(nand_info->factory_bad_block);
if (nand_info->new_bad_block)
nand_free(nand_info->new_bad_block);
if (nand_info->mbr_data)
nand_free(nand_info->mbr_data);
return ret;
}