blob: 1def259944b898a2df6c2b32a0fc603c3503e5c6 [file] [log] [blame]
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
* drivers/nand/logic/aml_nftl_gc.c
*
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
*
*/
#include "aml_nftl.h"
uint32 garbage_collect(struct aml_nftl_part_t* part)
{
uint16 page_num;
_phy_block_info* phy_block;
if(part->free_block_num <= part->gc_strategy.start_gc_free_blocks)
{
part->gc_strategy.process = GC_ON;
}
if(part->gc_strategy.process == GC_ON)
{
if(part->free_block_num < part->gc_strategy.stop_gc_free_blocks)
{
if(part->gc_strategy.flag_gc_block == 0)
{
phy_block = part->invalid_page_head.invalid_page_next;
if(phy_block == NULL)
{
return 1;
}
if(phy_block->invalid_page_count >= (part->nand_chip->pages_per_blk>>1))
{
part->gc_strategy.gc_page = 4;
}
else if(phy_block->invalid_page_count >= (part->nand_chip->pages_per_blk>>2))
{
part->gc_strategy.gc_page = 8;
}
else if(phy_block->invalid_page_count >= (part->nand_chip->pages_per_blk>>3))
{
part->gc_strategy.gc_page = 16;
}
else
{
NPRINT("something is wrong 1! %d:%d\n",part->free_block_num,phy_block->invalid_page_count);
part->gc_strategy.gc_page = 0xffff;
// print_block_invalid_list(part->part_phy_block_invalid_page_head);
}
// if(part->free_block_num <= (part->gc_strategy.start_gc_free_blocks>>1))
// {
// //NPRINT("something is wrong 2! %d:%d\n",part->free_block_num,phy_block->invalid_page_count);
// part->gc_strategy.gc_page = 0xffff;
// }
if(part->free_block_num <= part->cfg->nftl_min_free_block)
{
//NPRINT("something is wrong 2! %d:%d\n",part->free_block_num,phy_block->invalid_page_count);
part->gc_strategy.gc_page = 0xffff;
}
}
page_num = part->gc_strategy.gc_page;
if(garbage_collect_first(part,NULL,page_num) != 0)
{
NPRINT("garbage_collect_first wrong! %d\n",part->free_block_num);
return 1;
}
}
else
{
part->gc_strategy.process = GC_STOP;
part->gc_strategy.flag_gc_block = 0;
part->gc_strategy.gc_page = 0;
}
}
return 0;
}
uint32 prio_garbage_collect_no_last_page(struct aml_nftl_part_t* part,_phy_block_info* block,uint16 page_num)
{
uint32 i = 0,ret = 0;
_phy_block_info* p_phy_block;
_physic_op_par phy_op_par;
p_phy_block = block;
if(i == (part->nand_chip->pages_per_blk-1))
{
part->gc_strategy.flag_gc_block = 0;
}
phy_op_par.phy_page.Page_NO = 0;
phy_op_par.phy_page.blkNO_in_chip = p_phy_block->phy_block.blkNO_in_chip;
phy_op_par.page_bitmap = part->nand_chip->bitmap_per_page;
ret = part->nand_erase_superblk(part,&phy_op_par);
p_phy_block->invalid_page_count = 0;
p_phy_block->erase_count++;
if(ret != 0)
{
part->nand_mark_bad_blk(part,&phy_op_par);
NPRINT("prio_garbage_collect nand_mark_bad_blk!\n");
}
if (ret == 0){
put_phy_block_to_free_list(part,p_phy_block);
NPRINT("%s put_phy_block_to_free_list!\n",__func__);
}
return 0;
}
uint32 prio_garbage_collect(struct aml_nftl_part_t* part,_phy_block_info* block,uint16 page_num)
{
uint32 i,ret = 0;
uint16 invalid_page_count;
uchar spare_data[BYTES_OF_USER_PER_PAGE];
_phy_block_info* p_phy_block;
_physic_op_par phy_op_par;
p_phy_block = block;
phy_op_par.phy_page.Page_NO = part->nand_chip->pages_per_blk - 1;
phy_op_par.phy_page.blkNO_in_chip = p_phy_block->phy_block.blkNO_in_chip;
phy_op_par.page_bitmap = part->nand_chip->bitmap_per_page;
phy_op_par.main_data_addr = part->temp_page_buf;
phy_op_par.spare_data_addr = spare_data;
part->nand_read_page(part,&phy_op_par);
invalid_page_count = 1;
for(i=0;i<(part->nand_chip->pages_per_blk-1);i++) {
/* empty */
}
if(invalid_page_count != p_phy_block->invalid_page_count) {
NPRINT("%s invalid_page num error:[%d] [%d]!!\n", __func__,
invalid_page_count, p_phy_block->invalid_page_count);
print_block_invalid_list(part);
}
if(i == (part->nand_chip->pages_per_blk-1)) {
part->gc_strategy.flag_gc_block = 0;
}
phy_op_par.phy_page.Page_NO = 0;
phy_op_par.phy_page.blkNO_in_chip = p_phy_block->phy_block.blkNO_in_chip;
phy_op_par.page_bitmap = part->nand_chip->bitmap_per_page;
ret = part->nand_erase_superblk(part,&phy_op_par);
p_phy_block->invalid_page_count = 0;
p_phy_block->erase_count++;
if(ret != 0) {
part->nand_mark_bad_blk(part,&phy_op_par);
NPRINT("prio_garbage_collect nand_mark_bad_blk!\n");
}
if (ret == 0){
put_phy_block_to_free_list(part,p_phy_block);
NPRINT("%s put_phy_block_to_free_list!\n",__func__);
}
return 0;
}
uint32 garbage_collect_first(struct aml_nftl_part_t* part,_phy_block_info* block,uint16 page_num)
{
uint32 i = 0;
uint16 invalid_page_count;
uchar spare_data[BYTES_OF_USER_PER_PAGE];
_phy_block_info* p_phy_block;
_physic_op_par phy_op_par;
if(block == NULL) {
p_phy_block = out_phy_block_from_invalid_page_list(part);
} else {
if(block == part->part_current_used_block) {
return 0;
}
p_phy_block = out_phy_block_from_invalid_page_list_by_block(part,block);
}
if(p_phy_block == NULL) {
NPRINT("garbage_collect_first error!!\n");
return 1;
}
phy_op_par.phy_page.Page_NO = part->nand_chip->pages_per_blk - 1;
phy_op_par.phy_page.blkNO_in_chip = p_phy_block->phy_block.blkNO_in_chip;
phy_op_par.page_bitmap = part->nand_chip->bitmap_per_page;
phy_op_par.main_data_addr = part->temp_page_buf;
phy_op_par.spare_data_addr = spare_data;
part->nand_read_page(part,&phy_op_par);
invalid_page_count = 1;
if(invalid_page_count != p_phy_block->invalid_page_count) {
NPRINT("%s invalid_page num error:[%d] [%d]!!\n",
__func__, invalid_page_count, p_phy_block->invalid_page_count);
print_block_invalid_list(part);
}
if(i == (part->nand_chip->pages_per_blk-1)) {
part->gc_strategy.flag_gc_block = 0;
}
put_phy_block_to_invalid_page_list(part,p_phy_block);
adjust_invalid_list(part);
return 0;
}
_prio_gc_node* get_empty_gc_node(_prio_gc *prio_gc)
{
uint16 i;
for(i=0;i<MAX_PRIO_GC_NUM;i++)
{
if(prio_gc->prio_gc_node[i].prio_type == PRIO_NONE)
return &prio_gc->prio_gc_node[i];
}
return NULL;
}
_prio_gc_node *search_gc_node(_prio_gc *prio_gc,_phy_block_info* block)
{
_prio_gc_node * p = &prio_gc->prio_gc_head;
for(p=p->prio_gc_next; p; p=p->prio_gc_next)
{
if(p->phy_block_info == block)
{
return p;
}
}
return NULL;
}
void print_gc_list(_prio_gc *prio_gc)
{
_prio_gc_node * p = &prio_gc->prio_gc_head;
NPRINT("print_gc_list list: \n");
for(p=p->prio_gc_next; p; p=p->prio_gc_next) {
NPRINT("block NO:%4d; \n ",p->phy_block_info->phy_block.blkNO_in_chip);
}
}
int add_to_gc_list_tail(_prio_gc *prio_gc,_prio_gc_node* gc_node)
{
_prio_gc_node * p = &prio_gc->prio_gc_head;
while(p->prio_gc_next != NULL)
p = p->prio_gc_next;
p->prio_gc_next = gc_node;
gc_node->prio_gc_next = NULL;
gc_node->prio_gc_prev = p;
prio_gc->gc_num += 1;
return 0;
}
_prio_gc_node* del_from_gc_list(_prio_gc *prio_gc,_prio_gc_node* gc_node)
{
_prio_gc_node * p = gc_node->prio_gc_prev;
p->prio_gc_next = gc_node->prio_gc_next;
if(gc_node->prio_gc_next != NULL)
gc_node->prio_gc_next->prio_gc_prev = p;
gc_node->prio_gc_next = NULL;
gc_node->prio_gc_prev = NULL;
prio_gc->gc_num -= 1;
return gc_node;
}
void add_prio_gc(struct aml_nftl_part_t* part,_phy_block_info* block,uint16 type)
{
_prio_gc_node * p;
p = search_gc_node(&part->prio_gc,block);
if(p != NULL)
{
return;
}
p = get_empty_gc_node(&part->prio_gc);
if(p == NULL)
{
return;
}
p->phy_block_info = block;
p->prio_type = type;
p->prio_gc_next = NULL;
p->prio_gc_prev = NULL;
add_to_gc_list_tail(&part->prio_gc,p);
return;
}
uint32 do_prio_gc(struct aml_nftl_part_t* part)
{
uint32 ret;
_prio_gc_node * p;
if(part->ftl_status.unusual == FTL_UNUSUAL) {
NPRINT("%s %d: nftl status is unusual. do not write anything\n",__func__,__LINE__);
return 0;
}
if(part->prio_gc.prio_gc_head.prio_gc_next == NULL) {
return 0;
}
p = del_from_gc_list(&part->prio_gc,part->prio_gc.prio_gc_head.prio_gc_next);
if(p->phy_block_info == part->part_current_used_block) {
return 0;
}
part->prio_gc.prio_type_now = p->prio_type;
NPRINT("do_prio_gc block:%d, type:%d\n",p->phy_block_info->phy_block.blkNO_in_chip,p->prio_type);
ret = prio_garbage_collect(part,p->phy_block_info,0xffff);
p->prio_type = PRIO_NONE;
part->prio_gc.prio_type_now = PRIO_NONE;
return ret;
}
uint32 prio_gc_all(struct aml_nftl_part_t* part)
{
uint32 i,ret=0;
for(i=0;i<MAX_PRIO_GC_NUM;i++) {
ret |= do_prio_gc(part);
}
return ret;
}
uint32 do_static_wear_leveling(struct aml_nftl_part_t* part)
{
_phy_block_info* block_ptr = NULL;
_phy_block_info* p;
uint16 erase_max, erase_min;
if(part->s_wl.s_wl_status != WL_ON)
return 0;
if(part->invalid_page_head.invalid_page_next == NULL)
return 0;
erase_max = part->invalid_page_head.invalid_page_next->erase_count;
erase_min = erase_max;
for (p = &part->invalid_page_head; p->invalid_page_next; p = p->invalid_page_next) {
if (p->invalid_page_next->erase_count > erase_max) {
erase_max = p->invalid_page_next->erase_count;
} else if (p->invalid_page_next->erase_count < erase_min) {
erase_min = p->invalid_page_next->erase_count;
block_ptr = p->invalid_page_next;
} else {
continue;
}
}
if ((erase_max - erase_min) >= part->s_wl.erase_span)
add_prio_gc(part,block_ptr,GC_WEAR_LEVELING);
return 0;
}
uint32 gc_one(struct aml_nftl_part_t* part)
{
_phy_block_info* phy_block;
phy_block = part->invalid_page_head.invalid_page_next;
if(phy_block->invalid_page_count >= 10) {
if(garbage_collect_first(part,NULL,0xffff) != 0) {
NPRINT("gc_one error!\n");
} else {
NPRINT("gc_one ok!\n");
}
return 0;
} else {
return 1;
}
}
uint32 gc_all(struct aml_nftl_part_t* part)
{
uint32 ret = 0;
while(!ret)
{
ret = gc_one(part);
}
NPRINT("gc all end\n");
return 0;
}