/* * nand_partitions.c * * Copyright (C) 2019 Allwinner. * * cuizhikui * 2021-09-24 * * SPDX-License-Identifier: GPL-2.0 */ #include "sunxi_nand.h" #include "sunxi_nand_boot.h" #include "sunxi_nand_partitions.h" #include #include "../nand_nftl.h" #include #include 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; }