162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2017 Free Electrons 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Boris Brezillon <boris.brezillon@free-electrons.com> 762306a36Sopenharmony_ci * Peter Pan <peterpandong@micron.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "nand: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mtd/nand.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/** 1662306a36Sopenharmony_ci * nanddev_isbad() - Check if a block is bad 1762306a36Sopenharmony_ci * @nand: NAND device 1862306a36Sopenharmony_ci * @pos: position pointing to the block we want to check 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Return: true if the block is bad, false otherwise. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cibool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci if (mtd_check_expert_analysis_mode()) 2562306a36Sopenharmony_ci return false; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (nanddev_bbt_is_initialized(nand)) { 2862306a36Sopenharmony_ci unsigned int entry; 2962306a36Sopenharmony_ci int status; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci entry = nanddev_bbt_pos_to_entry(nand, pos); 3262306a36Sopenharmony_ci status = nanddev_bbt_get_block_status(nand, entry); 3362306a36Sopenharmony_ci /* Lazy block status retrieval */ 3462306a36Sopenharmony_ci if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { 3562306a36Sopenharmony_ci if (nand->ops->isbad(nand, pos)) 3662306a36Sopenharmony_ci status = NAND_BBT_BLOCK_FACTORY_BAD; 3762306a36Sopenharmony_ci else 3862306a36Sopenharmony_ci status = NAND_BBT_BLOCK_GOOD; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci nanddev_bbt_set_block_status(nand, entry, status); 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (status == NAND_BBT_BLOCK_WORN || 4462306a36Sopenharmony_ci status == NAND_BBT_BLOCK_FACTORY_BAD) 4562306a36Sopenharmony_ci return true; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return false; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return nand->ops->isbad(nand, pos); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_isbad); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/** 5562306a36Sopenharmony_ci * nanddev_markbad() - Mark a block as bad 5662306a36Sopenharmony_ci * @nand: NAND device 5762306a36Sopenharmony_ci * @pos: position of the block to mark bad 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Mark a block bad. This function is updating the BBT if available and 6062306a36Sopenharmony_ci * calls the low-level markbad hook (nand->ops->markbad()). 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ciint nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct mtd_info *mtd = nanddev_to_mtd(nand); 6762306a36Sopenharmony_ci unsigned int entry; 6862306a36Sopenharmony_ci int ret = 0; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (nanddev_isbad(nand, pos)) 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = nand->ops->markbad(nand, pos); 7462306a36Sopenharmony_ci if (ret) 7562306a36Sopenharmony_ci pr_warn("failed to write BBM to block @%llx (err = %d)\n", 7662306a36Sopenharmony_ci nanddev_pos_to_offs(nand, pos), ret); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!nanddev_bbt_is_initialized(nand)) 7962306a36Sopenharmony_ci goto out; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci entry = nanddev_bbt_pos_to_entry(nand, pos); 8262306a36Sopenharmony_ci ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); 8362306a36Sopenharmony_ci if (ret) 8462306a36Sopenharmony_ci goto out; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = nanddev_bbt_update(nand); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciout: 8962306a36Sopenharmony_ci if (!ret) 9062306a36Sopenharmony_ci mtd->ecc_stats.badblocks++; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_markbad); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * nanddev_isreserved() - Check whether an eraseblock is reserved or not 9862306a36Sopenharmony_ci * @nand: NAND device 9962306a36Sopenharmony_ci * @pos: NAND position to test 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * Checks whether the eraseblock pointed by @pos is reserved or not. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Return: true if the eraseblock is reserved, false otherwise. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cibool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci unsigned int entry; 10862306a36Sopenharmony_ci int status; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!nanddev_bbt_is_initialized(nand)) 11162306a36Sopenharmony_ci return false; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Return info from the table */ 11462306a36Sopenharmony_ci entry = nanddev_bbt_pos_to_entry(nand, pos); 11562306a36Sopenharmony_ci status = nanddev_bbt_get_block_status(nand, entry); 11662306a36Sopenharmony_ci return status == NAND_BBT_BLOCK_RESERVED; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_isreserved); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/** 12162306a36Sopenharmony_ci * nanddev_erase() - Erase a NAND portion 12262306a36Sopenharmony_ci * @nand: NAND device 12362306a36Sopenharmony_ci * @pos: position of the block to erase 12462306a36Sopenharmony_ci * 12562306a36Sopenharmony_ci * Erases the block if it's not bad. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { 13262306a36Sopenharmony_ci pr_warn("attempt to erase a bad/reserved block @%llx\n", 13362306a36Sopenharmony_ci nanddev_pos_to_offs(nand, pos)); 13462306a36Sopenharmony_ci return -EIO; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return nand->ops->erase(nand, pos); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices 14262306a36Sopenharmony_ci * @mtd: MTD device 14362306a36Sopenharmony_ci * @einfo: erase request 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * This is a simple mtd->_erase() implementation iterating over all blocks 14662306a36Sopenharmony_ci * concerned by @einfo and calling nand->ops->erase() on each of them. 14762306a36Sopenharmony_ci * 14862306a36Sopenharmony_ci * Note that mtd->_erase should not be directly assigned to this helper, 14962306a36Sopenharmony_ci * because there's no locking here. NAND specialized layers should instead 15062306a36Sopenharmony_ci * implement there own wrapper around nanddev_mtd_erase() taking the 15162306a36Sopenharmony_ci * appropriate lock before calling nanddev_mtd_erase(). 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ciint nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct nand_device *nand = mtd_to_nanddev(mtd); 15862306a36Sopenharmony_ci struct nand_pos pos, last; 15962306a36Sopenharmony_ci int ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci nanddev_offs_to_pos(nand, einfo->addr, &pos); 16262306a36Sopenharmony_ci nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last); 16362306a36Sopenharmony_ci while (nanddev_pos_cmp(&pos, &last) <= 0) { 16462306a36Sopenharmony_ci ret = nanddev_erase(nand, &pos); 16562306a36Sopenharmony_ci if (ret) { 16662306a36Sopenharmony_ci einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci nanddev_pos_next_eraseblock(nand, &pos); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_mtd_erase); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/** 17962306a36Sopenharmony_ci * nanddev_mtd_max_bad_blocks() - Get the maximum number of bad eraseblock on 18062306a36Sopenharmony_ci * a specific region of the NAND device 18162306a36Sopenharmony_ci * @mtd: MTD device 18262306a36Sopenharmony_ci * @offs: offset of the NAND region 18362306a36Sopenharmony_ci * @len: length of the NAND region 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * Default implementation for mtd->_max_bad_blocks(). Only works if 18662306a36Sopenharmony_ci * nand->memorg.max_bad_eraseblocks_per_lun is > 0. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * Return: a positive number encoding the maximum number of eraseblocks on a 18962306a36Sopenharmony_ci * portion of memory, a negative error code otherwise. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ciint nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct nand_device *nand = mtd_to_nanddev(mtd); 19462306a36Sopenharmony_ci struct nand_pos pos, end; 19562306a36Sopenharmony_ci unsigned int max_bb = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!nand->memorg.max_bad_eraseblocks_per_lun) 19862306a36Sopenharmony_ci return -ENOTSUPP; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci nanddev_offs_to_pos(nand, offs, &pos); 20162306a36Sopenharmony_ci nanddev_offs_to_pos(nand, offs + len, &end); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (nanddev_offs_to_pos(nand, offs, &pos); 20462306a36Sopenharmony_ci nanddev_pos_cmp(&pos, &end) < 0; 20562306a36Sopenharmony_ci nanddev_pos_next_lun(nand, &pos)) 20662306a36Sopenharmony_ci max_bb += nand->memorg.max_bad_eraseblocks_per_lun; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return max_bb; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * nanddev_get_ecc_engine() - Find and get a suitable ECC engine 21462306a36Sopenharmony_ci * @nand: NAND device 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int nanddev_get_ecc_engine(struct nand_device *nand) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci int engine_type; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Read the user desires in terms of ECC engine/configuration */ 22162306a36Sopenharmony_ci of_get_nand_ecc_user_config(nand); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci engine_type = nand->ecc.user_conf.engine_type; 22462306a36Sopenharmony_ci if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID) 22562306a36Sopenharmony_ci engine_type = nand->ecc.defaults.engine_type; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci switch (engine_type) { 22862306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 22962306a36Sopenharmony_ci return 0; 23062306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 23162306a36Sopenharmony_ci nand->ecc.engine = nand_ecc_get_sw_engine(nand); 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_DIE: 23462306a36Sopenharmony_ci nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand); 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 23762306a36Sopenharmony_ci nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand); 23862306a36Sopenharmony_ci if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER) 23962306a36Sopenharmony_ci return -EPROBE_DEFER; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci default: 24262306a36Sopenharmony_ci pr_err("Missing ECC engine type\n"); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (!nand->ecc.engine) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/** 25262306a36Sopenharmony_ci * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine 25362306a36Sopenharmony_ci * @nand: NAND device 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_cistatic int nanddev_put_ecc_engine(struct nand_device *nand) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci switch (nand->ecc.ctx.conf.engine_type) { 25862306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 25962306a36Sopenharmony_ci nand_ecc_put_on_host_hw_engine(nand); 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 26262306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 26362306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_DIE: 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/** 27262306a36Sopenharmony_ci * nanddev_find_ecc_configuration() - Find a suitable ECC configuration 27362306a36Sopenharmony_ci * @nand: NAND device 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic int nanddev_find_ecc_configuration(struct nand_device *nand) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci int ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!nand->ecc.engine) 28062306a36Sopenharmony_ci return -ENOTSUPP; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci ret = nand_ecc_init_ctx(nand); 28362306a36Sopenharmony_ci if (ret) 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!nand_ecc_is_strong_enough(nand)) 28762306a36Sopenharmony_ci pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", 28862306a36Sopenharmony_ci nand->mtd.name); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/** 29462306a36Sopenharmony_ci * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip 29562306a36Sopenharmony_ci * @nand: NAND device 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ciint nanddev_ecc_engine_init(struct nand_device *nand) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci int ret; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Look for the ECC engine to use */ 30262306a36Sopenharmony_ci ret = nanddev_get_ecc_engine(nand); 30362306a36Sopenharmony_ci if (ret) { 30462306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 30562306a36Sopenharmony_ci pr_err("No ECC engine found\n"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* No ECC engine requested */ 31162306a36Sopenharmony_ci if (!nand->ecc.engine) 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Configure the engine: balance user input and chip requirements */ 31562306a36Sopenharmony_ci ret = nanddev_find_ecc_configuration(nand); 31662306a36Sopenharmony_ci if (ret) { 31762306a36Sopenharmony_ci pr_err("No suitable ECC configuration\n"); 31862306a36Sopenharmony_ci nanddev_put_ecc_engine(nand); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci return ret; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_ecc_engine_init); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/** 32862306a36Sopenharmony_ci * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations 32962306a36Sopenharmony_ci * @nand: NAND device 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_civoid nanddev_ecc_engine_cleanup(struct nand_device *nand) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci if (nand->ecc.engine) 33462306a36Sopenharmony_ci nand_ecc_cleanup_ctx(nand); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci nanddev_put_ecc_engine(nand); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/** 34162306a36Sopenharmony_ci * nanddev_init() - Initialize a NAND device 34262306a36Sopenharmony_ci * @nand: NAND device 34362306a36Sopenharmony_ci * @ops: NAND device operations 34462306a36Sopenharmony_ci * @owner: NAND device owner 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * Initializes a NAND device object. Consistency checks are done on @ops and 34762306a36Sopenharmony_ci * @nand->memorg. Also takes care of initializing the BBT. 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * Return: 0 in case of success, a negative error code otherwise. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ciint nanddev_init(struct nand_device *nand, const struct nand_ops *ops, 35262306a36Sopenharmony_ci struct module *owner) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct mtd_info *mtd = nanddev_to_mtd(nand); 35562306a36Sopenharmony_ci struct nand_memory_organization *memorg = nanddev_get_memorg(nand); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!nand || !ops) 35862306a36Sopenharmony_ci return -EINVAL; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!ops->erase || !ops->markbad || !ops->isbad) 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (!memorg->bits_per_cell || !memorg->pagesize || 36462306a36Sopenharmony_ci !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || 36562306a36Sopenharmony_ci !memorg->planes_per_lun || !memorg->luns_per_target || 36662306a36Sopenharmony_ci !memorg->ntargets) 36762306a36Sopenharmony_ci return -EINVAL; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci nand->rowconv.eraseblock_addr_shift = 37062306a36Sopenharmony_ci fls(memorg->pages_per_eraseblock - 1); 37162306a36Sopenharmony_ci nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + 37262306a36Sopenharmony_ci nand->rowconv.eraseblock_addr_shift; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci nand->ops = ops; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci mtd->type = memorg->bits_per_cell == 1 ? 37762306a36Sopenharmony_ci MTD_NANDFLASH : MTD_MLCNANDFLASH; 37862306a36Sopenharmony_ci mtd->flags = MTD_CAP_NANDFLASH; 37962306a36Sopenharmony_ci mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; 38062306a36Sopenharmony_ci mtd->writesize = memorg->pagesize; 38162306a36Sopenharmony_ci mtd->writebufsize = memorg->pagesize; 38262306a36Sopenharmony_ci mtd->oobsize = memorg->oobsize; 38362306a36Sopenharmony_ci mtd->size = nanddev_size(nand); 38462306a36Sopenharmony_ci mtd->owner = owner; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return nanddev_bbt_init(nand); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_init); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/** 39162306a36Sopenharmony_ci * nanddev_cleanup() - Release resources allocated in nanddev_init() 39262306a36Sopenharmony_ci * @nand: NAND device 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Basically undoes what has been done in nanddev_init(). 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_civoid nanddev_cleanup(struct nand_device *nand) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci if (nanddev_bbt_is_initialized(nand)) 39962306a36Sopenharmony_ci nanddev_bbt_cleanup(nand); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nanddev_cleanup); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ciMODULE_DESCRIPTION("Generic NAND framework"); 40462306a36Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 40562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 406