brintos

brintos / linux-shallow public Read only

0
0
Text · 10.2 KiB · 7737b1a Raw
406 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2017 Free Electrons4 *5 * Authors:6 *	Boris Brezillon <boris.brezillon@free-electrons.com>7 *	Peter Pan <peterpandong@micron.com>8 */9 10#define pr_fmt(fmt)	"nand: " fmt11 12#include <linux/module.h>13#include <linux/mtd/nand.h>14 15/**16 * nanddev_isbad() - Check if a block is bad17 * @nand: NAND device18 * @pos: position pointing to the block we want to check19 *20 * Return: true if the block is bad, false otherwise.21 */22bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos)23{24	if (mtd_check_expert_analysis_mode())25		return false;26 27	if (nanddev_bbt_is_initialized(nand)) {28		unsigned int entry;29		int status;30 31		entry = nanddev_bbt_pos_to_entry(nand, pos);32		status = nanddev_bbt_get_block_status(nand, entry);33		/* Lazy block status retrieval */34		if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) {35			if (nand->ops->isbad(nand, pos))36				status = NAND_BBT_BLOCK_FACTORY_BAD;37			else38				status = NAND_BBT_BLOCK_GOOD;39 40			nanddev_bbt_set_block_status(nand, entry, status);41		}42 43		if (status == NAND_BBT_BLOCK_WORN ||44		    status == NAND_BBT_BLOCK_FACTORY_BAD)45			return true;46 47		return false;48	}49 50	return nand->ops->isbad(nand, pos);51}52EXPORT_SYMBOL_GPL(nanddev_isbad);53 54/**55 * nanddev_markbad() - Mark a block as bad56 * @nand: NAND device57 * @pos: position of the block to mark bad58 *59 * Mark a block bad. This function is updating the BBT if available and60 * calls the low-level markbad hook (nand->ops->markbad()).61 *62 * Return: 0 in case of success, a negative error code otherwise.63 */64int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos)65{66	struct mtd_info *mtd = nanddev_to_mtd(nand);67	unsigned int entry;68	int ret = 0;69 70	if (nanddev_isbad(nand, pos))71		return 0;72 73	ret = nand->ops->markbad(nand, pos);74	if (ret)75		pr_warn("failed to write BBM to block @%llx (err = %d)\n",76			nanddev_pos_to_offs(nand, pos), ret);77 78	if (!nanddev_bbt_is_initialized(nand))79		goto out;80 81	entry = nanddev_bbt_pos_to_entry(nand, pos);82	ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN);83	if (ret)84		goto out;85 86	ret = nanddev_bbt_update(nand);87 88out:89	if (!ret)90		mtd->ecc_stats.badblocks++;91 92	return ret;93}94EXPORT_SYMBOL_GPL(nanddev_markbad);95 96/**97 * nanddev_isreserved() - Check whether an eraseblock is reserved or not98 * @nand: NAND device99 * @pos: NAND position to test100 *101 * Checks whether the eraseblock pointed by @pos is reserved or not.102 *103 * Return: true if the eraseblock is reserved, false otherwise.104 */105bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos)106{107	unsigned int entry;108	int status;109 110	if (!nanddev_bbt_is_initialized(nand))111		return false;112 113	/* Return info from the table */114	entry = nanddev_bbt_pos_to_entry(nand, pos);115	status = nanddev_bbt_get_block_status(nand, entry);116	return status == NAND_BBT_BLOCK_RESERVED;117}118EXPORT_SYMBOL_GPL(nanddev_isreserved);119 120/**121 * nanddev_erase() - Erase a NAND portion122 * @nand: NAND device123 * @pos: position of the block to erase124 *125 * Erases the block if it's not bad.126 *127 * Return: 0 in case of success, a negative error code otherwise.128 */129static int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos)130{131	if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) {132		pr_warn("attempt to erase a bad/reserved block @%llx\n",133			nanddev_pos_to_offs(nand, pos));134		return -EIO;135	}136 137	return nand->ops->erase(nand, pos);138}139 140/**141 * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices142 * @mtd: MTD device143 * @einfo: erase request144 *145 * This is a simple mtd->_erase() implementation iterating over all blocks146 * concerned by @einfo and calling nand->ops->erase() on each of them.147 *148 * Note that mtd->_erase should not be directly assigned to this helper,149 * because there's no locking here. NAND specialized layers should instead150 * implement there own wrapper around nanddev_mtd_erase() taking the151 * appropriate lock before calling nanddev_mtd_erase().152 *153 * Return: 0 in case of success, a negative error code otherwise.154 */155int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)156{157	struct nand_device *nand = mtd_to_nanddev(mtd);158	struct nand_pos pos, last;159	int ret;160 161	nanddev_offs_to_pos(nand, einfo->addr, &pos);162	nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last);163	while (nanddev_pos_cmp(&pos, &last) <= 0) {164		ret = nanddev_erase(nand, &pos);165		if (ret) {166			einfo->fail_addr = nanddev_pos_to_offs(nand, &pos);167 168			return ret;169		}170 171		nanddev_pos_next_eraseblock(nand, &pos);172	}173 174	return 0;175}176EXPORT_SYMBOL_GPL(nanddev_mtd_erase);177 178/**179 * nanddev_mtd_max_bad_blocks() - Get the maximum number of bad eraseblock on180 *				  a specific region of the NAND device181 * @mtd: MTD device182 * @offs: offset of the NAND region183 * @len: length of the NAND region184 *185 * Default implementation for mtd->_max_bad_blocks(). Only works if186 * nand->memorg.max_bad_eraseblocks_per_lun is > 0.187 *188 * Return: a positive number encoding the maximum number of eraseblocks on a189 * portion of memory, a negative error code otherwise.190 */191int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)192{193	struct nand_device *nand = mtd_to_nanddev(mtd);194	struct nand_pos pos, end;195	unsigned int max_bb = 0;196 197	if (!nand->memorg.max_bad_eraseblocks_per_lun)198		return -ENOTSUPP;199 200	nanddev_offs_to_pos(nand, offs, &pos);201	nanddev_offs_to_pos(nand, offs + len, &end);202 203	for (nanddev_offs_to_pos(nand, offs, &pos);204	     nanddev_pos_cmp(&pos, &end) < 0;205	     nanddev_pos_next_lun(nand, &pos))206		max_bb += nand->memorg.max_bad_eraseblocks_per_lun;207 208	return max_bb;209}210EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);211 212/**213 * nanddev_get_ecc_engine() - Find and get a suitable ECC engine214 * @nand: NAND device215 */216static int nanddev_get_ecc_engine(struct nand_device *nand)217{218	int engine_type;219 220	/* Read the user desires in terms of ECC engine/configuration */221	of_get_nand_ecc_user_config(nand);222 223	engine_type = nand->ecc.user_conf.engine_type;224	if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID)225		engine_type = nand->ecc.defaults.engine_type;226 227	switch (engine_type) {228	case NAND_ECC_ENGINE_TYPE_NONE:229		return 0;230	case NAND_ECC_ENGINE_TYPE_SOFT:231		nand->ecc.engine = nand_ecc_get_sw_engine(nand);232		break;233	case NAND_ECC_ENGINE_TYPE_ON_DIE:234		nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);235		break;236	case NAND_ECC_ENGINE_TYPE_ON_HOST:237		nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand);238		if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)239			return -EPROBE_DEFER;240		break;241	default:242		pr_err("Missing ECC engine type\n");243	}244 245	if (!nand->ecc.engine)246		return  -EINVAL;247 248	return 0;249}250 251/**252 * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine253 * @nand: NAND device254 */255static int nanddev_put_ecc_engine(struct nand_device *nand)256{257	switch (nand->ecc.ctx.conf.engine_type) {258	case NAND_ECC_ENGINE_TYPE_ON_HOST:259		nand_ecc_put_on_host_hw_engine(nand);260		break;261	case NAND_ECC_ENGINE_TYPE_NONE:262	case NAND_ECC_ENGINE_TYPE_SOFT:263	case NAND_ECC_ENGINE_TYPE_ON_DIE:264	default:265		break;266	}267 268	return 0;269}270 271/**272 * nanddev_find_ecc_configuration() - Find a suitable ECC configuration273 * @nand: NAND device274 */275static int nanddev_find_ecc_configuration(struct nand_device *nand)276{277	int ret;278 279	if (!nand->ecc.engine)280		return -ENOTSUPP;281 282	ret = nand_ecc_init_ctx(nand);283	if (ret)284		return ret;285 286	if (!nand_ecc_is_strong_enough(nand))287		pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",288			nand->mtd.name);289 290	return 0;291}292 293/**294 * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip295 * @nand: NAND device296 */297int nanddev_ecc_engine_init(struct nand_device *nand)298{299	int ret;300 301	/* Look for the ECC engine to use */302	ret = nanddev_get_ecc_engine(nand);303	if (ret) {304		if (ret != -EPROBE_DEFER)305			pr_err("No ECC engine found\n");306 307		return ret;308	}309 310	/* No ECC engine requested */311	if (!nand->ecc.engine)312		return 0;313 314	/* Configure the engine: balance user input and chip requirements */315	ret = nanddev_find_ecc_configuration(nand);316	if (ret) {317		pr_err("No suitable ECC configuration\n");318		nanddev_put_ecc_engine(nand);319 320		return ret;321	}322 323	return 0;324}325EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init);326 327/**328 * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations329 * @nand: NAND device330 */331void nanddev_ecc_engine_cleanup(struct nand_device *nand)332{333	if (nand->ecc.engine)334		nand_ecc_cleanup_ctx(nand);335 336	nanddev_put_ecc_engine(nand);337}338EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup);339 340/**341 * nanddev_init() - Initialize a NAND device342 * @nand: NAND device343 * @ops: NAND device operations344 * @owner: NAND device owner345 *346 * Initializes a NAND device object. Consistency checks are done on @ops and347 * @nand->memorg. Also takes care of initializing the BBT.348 *349 * Return: 0 in case of success, a negative error code otherwise.350 */351int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,352		 struct module *owner)353{354	struct mtd_info *mtd = nanddev_to_mtd(nand);355	struct nand_memory_organization *memorg = nanddev_get_memorg(nand);356 357	if (!nand || !ops)358		return -EINVAL;359 360	if (!ops->erase || !ops->markbad || !ops->isbad)361		return -EINVAL;362 363	if (!memorg->bits_per_cell || !memorg->pagesize ||364	    !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun ||365	    !memorg->planes_per_lun || !memorg->luns_per_target ||366	    !memorg->ntargets)367		return -EINVAL;368 369	nand->rowconv.eraseblock_addr_shift =370					fls(memorg->pages_per_eraseblock - 1);371	nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) +372				       nand->rowconv.eraseblock_addr_shift;373 374	nand->ops = ops;375 376	mtd->type = memorg->bits_per_cell == 1 ?377		    MTD_NANDFLASH : MTD_MLCNANDFLASH;378	mtd->flags = MTD_CAP_NANDFLASH;379	mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock;380	mtd->writesize = memorg->pagesize;381	mtd->writebufsize = memorg->pagesize;382	mtd->oobsize = memorg->oobsize;383	mtd->size = nanddev_size(nand);384	mtd->owner = owner;385 386	return nanddev_bbt_init(nand);387}388EXPORT_SYMBOL_GPL(nanddev_init);389 390/**391 * nanddev_cleanup() - Release resources allocated in nanddev_init()392 * @nand: NAND device393 *394 * Basically undoes what has been done in nanddev_init().395 */396void nanddev_cleanup(struct nand_device *nand)397{398	if (nanddev_bbt_is_initialized(nand))399		nanddev_bbt_cleanup(nand);400}401EXPORT_SYMBOL_GPL(nanddev_cleanup);402 403MODULE_DESCRIPTION("Generic NAND framework");404MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");405MODULE_LICENSE("GPL v2");406