/** * 作用:得到nandflash相关规格参数的函数接口 * * 参数: * @spec ---把nandflash中的相关规格参数填写在此指针变量中去 * * * 描述:得到nandflash的规格参数 */ int bsp_get_nand_info(struct nand_spec *spec) { struct nandc_host *host = nandc_nand_host; /*检查参数是否正确*/ if(!host) { NAND_TRACE(("ERROR: get nand info error.\n")); goto erro; } if(!nand_isinit()) { NAND_TRACE(("ERROR: nandc driver not init yet!\n")); goto erro; } /*复制nandflash的相关规格参数*/ spec->pagesize = host->nandchip->spec.pagesize; spec->pagenumperblock = (host->nandchip->spec.blocksize / spec->pagesize); spec->addrnum = host->nandchip->spec.addrcycle; spec->ecctype = host->nandchip->ecctype; spec->buswidth = host->nandchip->spec.buswidth; spec->blocksize = host->nandchip->spec.blocksize; spec->sparesize = host->nandchip->spec.sparesize; spec->chipsize = host->nandchip->spec.chipsize; return NANDC_OK; erro: return NANDC_ERROR; }
/** * 作用:对nand标强制擦除 * * 参数: * @blockid ---block块的字符串 * * * 描述:nand强制擦除功能,当如果有软件坏块时也可以用此函数来强制擦除 */ void bsp_nand_erase_force(char *blockid) { int ret = NANDC_ERROR; unsigned int id; char *blk_id = bsp_nand_argument_check(blockid); /* string to int */ ret= str2ul(blk_id, &id); if(ret) { NAND_TRACE(("[%s]ERROR: string to integer failed, ret = 0x%x.\n", __FUNCTION__, ret)); goto ERRO; } /*直接擦除*/ ret = nand_erase(id); if(ret) { NAND_TRACE(("[%s]ERROR: nand check bad failed, ret = 0x%x.\n", __FUNCTION__, ret)); goto ERRO; } else { NAND_TRACE(("SUCCESS: erase block %x forcely.\n", id)); } return; ERRO: return; }
/** * 作用:把字符串转换为数字 * * 参数: * @str ---要转换的字符串 * @digit ---得到的数字值 * * 描述:把字符串转换为数字 */ unsigned int str2ul(char *str, unsigned int *digit) { int ret = NANDC_ERROR; unsigned int tmp_digit = 0; char *temp = str; /*参数不正确*/ if(!str || !digit) { NAND_TRACE(("[%s]ERROR: input is NULL.\n", __FUNCTION__)); goto ERRO; } /*把string转换为数字*/ while(*temp != 0) { if(*temp < '0' || *temp > '9') { NAND_TRACE(("[%s]ERROR: string input is illegal.\n", __FUNCTION__)); goto ERRO; } tmp_digit = tmp_digit * 10 + (*temp - '0'); temp++; } *digit = tmp_digit; return NANDC_OK; ERRO: return ret; }
/** * 作用:擦除所有的yaffs文件系统分区 * * 参数: * 无 * * * 描述:擦除所有的yaffs文件系统分区,在升级的过程中使用,擦除所有的yaffs文件系统的分区 */ void bsp_erase_yaffs_partitons(void) { struct ST_PART_TBL * ptable = NULL; struct ptentry ptn; int ret = NANDC_ERROR; ptable_ensure(); ptable = ptable_get_ram_data(); /*遍历所有的分区信息*/ while(0 != strcmp(PTABLE_END_STR, ptable->name)) { /*查找YAFFS文件系统的分区*/ if(ptable->property & DATA_YAFFS) { /* coverity[buffer_size_warning] */ strncpy(ptn.name, ptable->name, 16); ptn.start = ptable->offset; ptn.length = ptable->capacity; NAND_TRACE(("earsing %s, start 0x%x, length 0x%x\n", ptn.name, ptn.start, ptn.length)); ret = flash_erase(&ptn); if(ret) { NAND_TRACE(("[%s] ERROR: erase %s failed, ret = %d\n", __FUNCTION__, ptn.name, ret)); return; } } ptable++; } }
/** * 作用:读取分区相对偏移地址的标志值,主要是为NV模块用,查询此分区的偏移地址的标志位的值 * * 参数: * @partition_name ---分区名 * @partition_offset ---分区的相对偏移 * @flag ---把检测的分区的标志值存放在此flag中 * 描述:NV模块把一个分区的一个block的最后一页的OOB中存放特定标记值,读此标记值存放在flag中 */ u32 bsp_nand_read_flag_nv(const char *partition_name, u32 partition_offset, unsigned char *flag) { u32 flash_addr; u32 ret = NANDC_ERROR; static unsigned char *buffer = NULL; struct nand_spec spec; struct ST_PART_TBL * ptable = find_partition_by_name((char *)partition_name); /*参数不对*/ if(!ptable) { goto ERRO; } if(!flag) { NAND_TRACE(("argu error.\n")); goto ERRO; } /*得到此分区的nandflash的规格参数*/ ret = bsp_get_nand_info(&spec); if(ret) { goto ERRO; } /*没有内存时分配内存*/ if(!buffer) { buffer = (unsigned char *)himalloc(spec.pagesize + YAFFS_BYTES_PER_SPARE); if(!buffer) { NAND_TRACE(("get ram buffer failed!\n")); goto ERRO; } } memset(buffer, 0xFF, spec.pagesize + YAFFS_BYTES_PER_SPARE); flash_addr = ptable->offset + partition_offset; /*使能ECC功能的带OOB读数据功能*/ ret = nand_read_oob((flash_addr + spec.blocksize - spec.pagesize), (unsigned int)buffer, (spec.pagesize + YAFFS_BYTES_PER_SPARE),YAFFS_BYTES_PER_SPARE ,NULL); if(ret) { NAND_TRACE(("nand read oob failed!\n")); goto ERRO; } *flag = (*(buffer + spec.pagesize) == NV_WRITE_SUCCESS) ? NV_WRITE_SUCCESS : (~NV_WRITE_SUCCESS); return NANDC_OK; ERRO: return ret; }
/** * 作用:打印nandflash相关规格参数的函数接口 * * 输入参数:无 * * * 描述:打印nand->spec 参数值 */ void bsp_show_nand_info(void) { struct nand_spec spec; if(NAND_ERROR == bsp_get_nand_info(&spec)) { NAND_TRACE(("ERROR: nand get spec error.\n")); return; } NAND_TRACE((" ------begin show nand info----------\n")); NAND_TRACE((" chipsize=%dMB,blocksize=%dKB,pagesize=%dKB,sparesize=%d\n", (spec.chipsize/1024/1024),spec.blocksize/1024,spec.pagesize/1024,spec.sparesize)); NAND_TRACE((" pagenumperblock=%d,ecctype=%d,buswidth=%d,addrnum=%d\n", spec.pagenumperblock,spec.ecctype,(0==spec.buswidth?8:16),spec.addrnum)); NAND_TRACE((" ------end show nand info----------\n")); }
/** * 作用:nandc模块提供对外通过分区名来查找分区表的函数接口 * * 参数: * @partition_name ---分区表的名 * 描述:通过分区名返回分区表信息,查找成功返回分区表指针,查找不成功返回NULL * */ struct ST_PART_TBL * find_partition_by_name(const char *partition_name) { struct ST_PART_TBL * ptable = NULL; ptable_ensure(); ptable = ptable_get_ram_data(); /*遍历所有的分区表信息*/ while(0 != strcmp(PTABLE_END_STR, ptable->name)) { /*查找到就退出*/ if(0 == strcmp(partition_name, ptable->name)) { break; } ptable++; } /*如果不为空就表示查找得所要的分区表信息*/ if(0 == strcmp(PTABLE_END_STR, ptable->name)) { NAND_TRACE(("ERROR: can't find partition %s, function %s\n", partition_name, __FUNCTION__)); return NULL; } return ptable; }
/** * 作用:nandc模块按分区名和分区偏移地址来擦除flash操作 * * 参数: * @partition_name ---要擦除的分区名 * @partition_offset ---要擦除的相对偏移地址 * * * 描述:根据分区名和分区的偏移地址来确定Flash的地址,再来擦除一个block,注意是擦除一个block数据块 */ int bsp_nand_erase(const char *partition_name, u32 partition_offset) { u32 flash_addr; u32 block_id, bad_flag; u32 ret = NANDC_ERROR; struct nandc_host * host = NULL; struct ST_PART_TBL * ptable = find_partition_by_name(partition_name); /*参数不正确*/ if(!ptable) { goto ERRO; } /*得到flash的地址信息*/ flash_addr = ptable->offset + partition_offset; host = nandc_nand_host; if(!host) { NAND_TRACE(("ERROR: function %s, line %d\n", __FUNCTION__, __LINE__)); goto ERRO; } /*得到块号并判断是否是坏块*/ block_id = flash_addr / host->nandchip->spec.blocksize; ret = nand_isbad(block_id, &bad_flag); if(ret) { NAND_TRACE(("ERROR: nand quary bad failed, function %s, line %d\n", __FUNCTION__, __LINE__)); goto ERRO; } if(NANDC_BAD_BLOCK == bad_flag) { NAND_TRACE(("ERROR: try to erase a bad block, function %s, line %d\n", __FUNCTION__, __LINE__)); goto ERRO; } /*擦除flash操作*/ return nand_erase(block_id); ERRO: return ret; }
/** * 作用:nandc模块提供对外初始化nandc控制器的函数接口 * * 参数: * 无 * 描述:初始化nandc控制器的数据结构并调用底层的nandc硬件控制器的初始化函数 * */ u32 nand_init(void) { struct nand_bus* handle = NULL; u32 result = NAND_ERROR; handle = &nand_handle; /*如果已经初始化完成了就直接退出来*/ if(NAND_INIT_MAGIC == handle->init_flag) { result = NAND_OK; NAND_TRACE(("!!nand module already inited!!\n")); goto EXIT; } memset((void*)handle, 0x00, sizeof(nand_handle)); /*指向具体的操作nandflash的操作函数,当下面操作具体的nandflash的函数发生了变化时,可以保证对上面提供函数接口不变*/ handle->funcs = &nandc_nand_funcs; /*nandflash硬件控制器的具体初始化函数*/ if(handle->funcs->init) { result = handle->funcs->init(); } else { NAND_TRACE(("ERRO!!nand module init is NULL\n")); result = NAND_ERROR; } /*初始化完成后设置标志位*/ if(NAND_OK == result) { handle->init_flag = NAND_INIT_MAGIC; } else { NAND_TRACE(("ERRO!!nand module init failed\n")); } EXIT: return result; }
/** * 作用:对nand标坏块 * * 参数: * @blockid ---block块的字符串 * * * 描述:nand标坏块 */ void bsp_nand_markbad(char *blockid) { int ret = NANDC_ERROR; unsigned int id, bad_flag; char *blk_id = bsp_nand_argument_check(blockid); /* string to int */ ret= str2ul(blk_id, &id); if(ret) { NAND_TRACE(("ERROR: string to integer failed, ret = 0x%x.\n", ret)); goto EXIT; } /* check block bad. if good, mark bad; else return */ ret = nand_isbad(id, &bad_flag); if(ret) { NAND_TRACE(("[%s]ERROR: nand check bad failed, ret = 0x%x.\n", __FUNCTION__, ret)); goto EXIT; } if(NANDC_GOOD_BLOCK == bad_flag) { ret = nand_bad(id); if(ret) { NAND_TRACE(("[%s]ERROR: nand mark bad failed, ret = 0x%x.\n", __FUNCTION__, ret)); goto EXIT; } } else { NAND_TRACE(("[%s]WARNING: block 0x%x is already bad.\n", __FUNCTION__)); goto EXIT; } return; EXIT: return; }
/** * 作用:nandc模块提供对外得到nandflash相关规格参数的函数接口 * * 参数: * @spec ---把nandflash中的相关参数填写在此指针变量中去 * * 描述:得到nandflash的规格参数 */ u32 nand_get_spec(struct nand_spec *spec) { struct nand_bus* handle; struct nand_interface * nandfuncs; handle = &nand_handle; /*如果没有初始化就返回错误*/ if(NAND_INIT_MAGIC != handle->init_flag) { NAND_TRACE(("error!! balong nand not inited\n")); return NAND_ERROR; } nandfuncs = handle->funcs; /*得到flash的规格参数*/ if(nandfuncs->get_spec) { return nandfuncs->get_spec(spec); } /*走到这里就说明有错误了走错误流程了*/ NAND_TRACE(("error!! get_spec not inited\n")); return NAND_ERROR; }
/** * 作用:nandc模块按分区名和分区相对偏移来确定此块是不是坏块 * * 参数: * @partition_name ---查询数据块的分区名 * @partition_offset ---查询数据块的分区相对偏移地址 * * 描述:根据分区名和分区的偏移地址来确定Flash的地址,再来判断此block是不是坏块 返回值: * 0表示此块是好块 * 1表示是坏块 * 其他的表示有错误 */ int bsp_nand_isbad(const char *partition_name, u32 partition_offset) { u32 flash_addr; u32 bad_flag; u32 ret = NANDC_ERROR; struct nandc_host * host = NULL; struct ST_PART_TBL * ptable = find_partition_by_name(partition_name); if(!ptable) { goto ERRO; } /*得到Flash地址*/ flash_addr = ptable->offset + partition_offset; host = nandc_nand_host; if(!host) { NAND_TRACE(("ERROR: function %s, line %d\n", __FUNCTION__, __LINE__)); goto ERRO; } /*判断此block是否是坏块*/ ret = nand_isbad(flash_addr / host->nandchip->spec.blocksize, &bad_flag); if(ret == 1) { return -6; } else if(ret > 1) { return ret; } if(bad_flag == 1) /* bad block */ { return 1; } else /* good block */ { return 0; } ERRO: return ret; }
/** * \brief Writes the data and/or spare area of a NANDFLASH page, after calculating an * ECC for the data area and storing it in the spare. If no data buffer is * provided, the ECC is read from the existing page spare. If no spare buffer * is provided, the spare area is still written with the ECC information * calculated on the data buffer. * \param nand Pointer to an EccNandFlash instance. * \param block Number of the block to write in. * \param page Number of the page to write inside the given block. * \param data Data area buffer, can be 0. * \param spare Spare area buffer, can be 0. * \return 0 if successful; otherwise returns an error code. */ uint8_t nand_ecc_write_page(const struct _nand_flash *nand, uint16_t block, uint16_t page, void *data, void *spare) { NAND_TRACE("nand_ecc_write_page(B#%d:P#%d)\r\n", block, page); assert(data || spare); if (nand_is_using_pmecc()) { if (spare) return NAND_ERROR_ECC_NOT_COMPATIBLE; return ecc_write_page_with_pmecc(nand, block, page, data); } if (nand_is_using_software_ecc()) return ecc_write_page_with_swecc(nand, block, page, data, spare); if (nand_is_using_no_ecc()) return nand_raw_write_page(nand, block, page, data, spare); return NAND_ERROR_ECC_NOT_COMPATIBLE; }
/****************************************************************************************** * FUNC NAME: * @bsp_update_size_of_lastpart() - external API: * * PARAMETER: * @new_ptable - addr of new ptable * * RETURN: * null * * DESCRIPTION: * update the size of last partition to shared memory * 分区表最后一个分区将flash最后一块空间完全占有,从而导致静态编译的ptable.bin与软件实际使用的ptable不一样, * 升级分区表的时候就认为两个分区不一样,从而导致对yaffs分区的擦除。此处用实际使用的分区表最后一个分区大小 * 覆盖新ptable最后分区大小 * * CALL FUNC: * *****************************************************************************************/ void bsp_update_size_of_lastpart(struct ST_PART_TBL *new_ptable) { struct ST_PART_TBL * old_ptable = NULL; if(!new_ptable) { NAND_TRACE(("[%s]argu error\n", __FUNCTION__)); return; } /*根据分区名得到分区数据结构*/ while(0 != strcmp(PTABLE_END_STR, new_ptable->name)) { new_ptable++; } new_ptable--; old_ptable = find_partition_by_name(new_ptable->name); if(old_ptable) { new_ptable->capacity = old_ptable->capacity; } }
/** * 作用:nandc模块提供对外扫描坏块的函数接口 * * 参数: * @start ---flash开始的地址 * @length ---扫描的长度 * @if_erase ---如果检测是好块根据此参数来决定是否擦除此块 * 描述:扫描flash是否是坏块,根据参数来决定是否擦除 */ u32 nand_scan_bad(FSZ start, FSZ length, u32 if_erase) { u32 addr_block_align; u32 length_block_align; u32 blk_id, is_bad, times, ret; struct nand_spec spec; /*因为是在fastboot中实现,内存的释放是没有实现的,使用静态变量表示一次分配以后都不分配了*/ static u32 block_buf = 0 ; memset(&spec, 0, sizeof(struct nand_spec)); /*通过此函数得到nandflash的规格参数,知道blocksize的大小*/ if(NAND_OK != nand_get_spec(&spec)) { NAND_TRACE(("nand_scan_bad: nand_get_spec failed\n")); return NAND_ERROR; } /*扫描块时要块对齐*/ addr_block_align = start - start%(spec.blocksize); /*得到要扫描的块长度*/ length_block_align = nandc_alige_size(start + length, spec.blocksize) - addr_block_align; /*如果没有分配内存就分配内存*/ if(!block_buf) { block_buf = (u32)himalloc(spec.blocksize); if(NULL == (void*)block_buf)//pclint 58 { NAND_TRACE(("nand_scan_bad: himalloc failed\n")); goto ERRO; } } /*还有块长度需要扫描*/ while(length_block_align > 0) { /*得到块号地址*/ blk_id = addr_block_align/spec.blocksize; if(NAND_OK != nand_isbad(blk_id, &is_bad)) { NAND_TRACE(("nand_scan_bad: nand_isbad failed\n")); goto ERRO; } if(NANDC_GOOD_BLOCK == is_bad) { times = 1; /*读整块数据如果没有出错的话表示是好坏*/ while((NANDC_E_READ == nand_read((FSZ)addr_block_align, block_buf, spec.blocksize, 0)) && (times)) { times--; } if(0 == times) { NAND_TRACE(("nand_scan_bad: find and read error, address:0x%x\n",addr_block_align)); ret = nand_bad(blk_id); if(ret) { NAND_TRACE(("nand_scan_bad:nand check bad failed, ret = 0x%x\n",ret)); } } /*是好块如果要擦除的话就擦除*/ else if(NANDC_TRUE == if_erase) { ret = nand_erase(blk_id); if(ret) { NAND_TRACE(("nand_scan_bad:nand erase failed, ret = 0x%x\n",ret)); } } else { } } else { NAND_TRACE(("nand_scan_bad:find bad block: 0x%x\n",addr_block_align)); } length_block_align -= spec.blocksize; addr_block_align += spec.blocksize; } return NAND_OK; ERRO: return NAND_ERROR; }
/** * 作用:nandc模块所有函数功能通用实现过程 * * 参数: * @param ---包含了要操作nandflash的具体参数信息 * @func_type ---函数的类型,根据此值知道要实现什么功能(读,写) * * * 描述:所有的函数功能都是通过这个函数来处理,通过func_type来确定是要实现什么功能,直接调用具体的函数指针来 * 执行具体的操作,当需要增加新的函数功能时,就在此通过处理流程中增加相关的流程就行,方便后面功能的扩展 * */ u32 nand_run(struct nand_param* param, u32 func_type) { struct nand_bus* handle; struct nand_interface * nandfuncs; u32 result = NAND_OK; handle = &nand_handle; /*判断是否初始化成功*/ if(NAND_INIT_MAGIC != handle->init_flag) { NAND_TRACE(("error!! balong nand not inited\n")); return NAND_ERROR; } /*得到具体的操作的函数指针*/ nandfuncs = handle->funcs; switch (func_type) { case READ_NAND: { /*使能ECC功能的读Flash操作*/ if(nandfuncs->read) { result = nandfuncs->read(param->addr_flash, param->addr_data, param->size_data, param->size_oob, param->skip_length); } else { NAND_TRACE(("error!! func read_random is NULL\n")); result = NAND_ERROR; } } break; case READ_RAW: { /*不使能ECC功能的裸读Flash操作*/ if(nandfuncs->read_page_raw) { result = nandfuncs->read_page_raw(param->number, (u32)param->addr_flash, (u32)param->addr_data, param->size_data); } else { NAND_TRACE(("error!! func read_page_yaffs is NULL\n")); result = NAND_ERROR; } } break; case QUARY_BAD: { /*查询坏块的功能函数*/ if(nandfuncs->quary_bad) { result = nandfuncs->quary_bad(param->number, (u32*)param->addr_data); } else { NAND_TRACE(("error!! func quary_bad is NULL\n")); result = NAND_ERROR; } } break; case ERASE_BLOCK: { /*擦除数据块的功能函数*/ if(nandfuncs->erase_by_id) { result = nandfuncs->erase_by_id(param->number); } else { NAND_TRACE(("error!! func erase_by_id is NULL\n")); result = NAND_ERROR; } } break; case WRITE_NAND: { /*使能ECC功能的写Flash操作,注意write中会自已先擦除Flash再来写,所有用户就不用在写之前擦除了*/ if(nandfuncs->write) { result = nandfuncs->write(param->addr_flash, param->addr_data, param->size_data, param->size_oob, param->skip_length); } else { NAND_TRACE(("error!! func write flash is NULL\n")); result = NAND_ERROR; } } break; case WRITE_RAW: { /*不使能ECC功能的裸写Flash操作*/ if(nandfuncs->write_page_raw) { result = nandfuncs->write_page_raw(param->number, (u32)param->addr_flash, (u32)param->addr_data, param->size_data); } else { NAND_TRACE(("error!! func write_page_raw is NULL\n")); result = NAND_ERROR; } } break; case MARK_BAD: { /*标坏块的功能*/ if(nandfuncs->mark_bad) { result = nandfuncs->mark_bad(param->number ); } else { NAND_TRACE(("error!! func mark_bad is NULL\n")); result = NAND_ERROR; } } break; default: NAND_TRACE(("ERRO!! nand function type:%d not defined\n",func_type)); result = NAND_ERROR; break; } /*返回操作的结果,0表示操作成功,1表示操作失败*/ return result; }