cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

core.c (10521B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2017 Free Electrons
      4 *
      5 * Authors:
      6 *	Boris Brezillon <boris.brezillon@free-electrons.com>
      7 *	Peter Pan <peterpandong@micron.com>
      8 */
      9
     10#define pr_fmt(fmt)	"nand: " fmt
     11
     12#include <linux/module.h>
     13#include <linux/mtd/nand.h>
     14
     15/**
     16 * nanddev_isbad() - Check if a block is bad
     17 * @nand: NAND device
     18 * @pos: position pointing to the block we want to check
     19 *
     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			else
     38				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 bad
     56 * @nand: NAND device
     57 * @pos: position of the block to mark bad
     58 *
     59 * Mark a block bad. This function is updating the BBT if available and
     60 * 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 not
     98 * @nand: NAND device
     99 * @pos: NAND position to test
    100 *
    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 portion
    122 * @nand: NAND device
    123 * @pos: position of the block to erase
    124 *
    125 * Erases the block if it's not bad.
    126 *
    127 * Return: 0 in case of success, a negative error code otherwise.
    128 */
    129int 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}
    139EXPORT_SYMBOL_GPL(nanddev_erase);
    140
    141/**
    142 * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices
    143 * @mtd: MTD device
    144 * @einfo: erase request
    145 *
    146 * This is a simple mtd->_erase() implementation iterating over all blocks
    147 * concerned by @einfo and calling nand->ops->erase() on each of them.
    148 *
    149 * Note that mtd->_erase should not be directly assigned to this helper,
    150 * because there's no locking here. NAND specialized layers should instead
    151 * implement there own wrapper around nanddev_mtd_erase() taking the
    152 * appropriate lock before calling nanddev_mtd_erase().
    153 *
    154 * Return: 0 in case of success, a negative error code otherwise.
    155 */
    156int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo)
    157{
    158	struct nand_device *nand = mtd_to_nanddev(mtd);
    159	struct nand_pos pos, last;
    160	int ret;
    161
    162	nanddev_offs_to_pos(nand, einfo->addr, &pos);
    163	nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last);
    164	while (nanddev_pos_cmp(&pos, &last) <= 0) {
    165		ret = nanddev_erase(nand, &pos);
    166		if (ret) {
    167			einfo->fail_addr = nanddev_pos_to_offs(nand, &pos);
    168
    169			return ret;
    170		}
    171
    172		nanddev_pos_next_eraseblock(nand, &pos);
    173	}
    174
    175	return 0;
    176}
    177EXPORT_SYMBOL_GPL(nanddev_mtd_erase);
    178
    179/**
    180 * nanddev_mtd_max_bad_blocks() - Get the maximum number of bad eraseblock on
    181 *				  a specific region of the NAND device
    182 * @mtd: MTD device
    183 * @offs: offset of the NAND region
    184 * @len: length of the NAND region
    185 *
    186 * Default implementation for mtd->_max_bad_blocks(). Only works if
    187 * nand->memorg.max_bad_eraseblocks_per_lun is > 0.
    188 *
    189 * Return: a positive number encoding the maximum number of eraseblocks on a
    190 * portion of memory, a negative error code otherwise.
    191 */
    192int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len)
    193{
    194	struct nand_device *nand = mtd_to_nanddev(mtd);
    195	struct nand_pos pos, end;
    196	unsigned int max_bb = 0;
    197
    198	if (!nand->memorg.max_bad_eraseblocks_per_lun)
    199		return -ENOTSUPP;
    200
    201	nanddev_offs_to_pos(nand, offs, &pos);
    202	nanddev_offs_to_pos(nand, offs + len, &end);
    203
    204	for (nanddev_offs_to_pos(nand, offs, &pos);
    205	     nanddev_pos_cmp(&pos, &end) < 0;
    206	     nanddev_pos_next_lun(nand, &pos))
    207		max_bb += nand->memorg.max_bad_eraseblocks_per_lun;
    208
    209	return max_bb;
    210}
    211EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks);
    212
    213/**
    214 * nanddev_get_ecc_engine() - Find and get a suitable ECC engine
    215 * @nand: NAND device
    216 */
    217static int nanddev_get_ecc_engine(struct nand_device *nand)
    218{
    219	int engine_type;
    220
    221	/* Read the user desires in terms of ECC engine/configuration */
    222	of_get_nand_ecc_user_config(nand);
    223
    224	engine_type = nand->ecc.user_conf.engine_type;
    225	if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID)
    226		engine_type = nand->ecc.defaults.engine_type;
    227
    228	switch (engine_type) {
    229	case NAND_ECC_ENGINE_TYPE_NONE:
    230		return 0;
    231	case NAND_ECC_ENGINE_TYPE_SOFT:
    232		nand->ecc.engine = nand_ecc_get_sw_engine(nand);
    233		break;
    234	case NAND_ECC_ENGINE_TYPE_ON_DIE:
    235		nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
    236		break;
    237	case NAND_ECC_ENGINE_TYPE_ON_HOST:
    238		nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand);
    239		if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)
    240			return -EPROBE_DEFER;
    241		break;
    242	default:
    243		pr_err("Missing ECC engine type\n");
    244	}
    245
    246	if (!nand->ecc.engine)
    247		return  -EINVAL;
    248
    249	return 0;
    250}
    251
    252/**
    253 * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine
    254 * @nand: NAND device
    255 */
    256static int nanddev_put_ecc_engine(struct nand_device *nand)
    257{
    258	switch (nand->ecc.ctx.conf.engine_type) {
    259	case NAND_ECC_ENGINE_TYPE_ON_HOST:
    260		nand_ecc_put_on_host_hw_engine(nand);
    261		break;
    262	case NAND_ECC_ENGINE_TYPE_NONE:
    263	case NAND_ECC_ENGINE_TYPE_SOFT:
    264	case NAND_ECC_ENGINE_TYPE_ON_DIE:
    265	default:
    266		break;
    267	}
    268
    269	return 0;
    270}
    271
    272/**
    273 * nanddev_find_ecc_configuration() - Find a suitable ECC configuration
    274 * @nand: NAND device
    275 */
    276static int nanddev_find_ecc_configuration(struct nand_device *nand)
    277{
    278	int ret;
    279
    280	if (!nand->ecc.engine)
    281		return -ENOTSUPP;
    282
    283	ret = nand_ecc_init_ctx(nand);
    284	if (ret)
    285		return ret;
    286
    287	if (!nand_ecc_is_strong_enough(nand))
    288		pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
    289			nand->mtd.name);
    290
    291	return 0;
    292}
    293
    294/**
    295 * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip
    296 * @nand: NAND device
    297 */
    298int nanddev_ecc_engine_init(struct nand_device *nand)
    299{
    300	int ret;
    301
    302	/* Look for the ECC engine to use */
    303	ret = nanddev_get_ecc_engine(nand);
    304	if (ret) {
    305		if (ret != -EPROBE_DEFER)
    306			pr_err("No ECC engine found\n");
    307
    308		return ret;
    309	}
    310
    311	/* No ECC engine requested */
    312	if (!nand->ecc.engine)
    313		return 0;
    314
    315	/* Configure the engine: balance user input and chip requirements */
    316	ret = nanddev_find_ecc_configuration(nand);
    317	if (ret) {
    318		pr_err("No suitable ECC configuration\n");
    319		nanddev_put_ecc_engine(nand);
    320
    321		return ret;
    322	}
    323
    324	return 0;
    325}
    326EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init);
    327
    328/**
    329 * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations
    330 * @nand: NAND device
    331 */
    332void nanddev_ecc_engine_cleanup(struct nand_device *nand)
    333{
    334	if (nand->ecc.engine)
    335		nand_ecc_cleanup_ctx(nand);
    336
    337	nanddev_put_ecc_engine(nand);
    338}
    339EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup);
    340
    341/**
    342 * nanddev_init() - Initialize a NAND device
    343 * @nand: NAND device
    344 * @ops: NAND device operations
    345 * @owner: NAND device owner
    346 *
    347 * Initializes a NAND device object. Consistency checks are done on @ops and
    348 * @nand->memorg. Also takes care of initializing the BBT.
    349 *
    350 * Return: 0 in case of success, a negative error code otherwise.
    351 */
    352int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
    353		 struct module *owner)
    354{
    355	struct mtd_info *mtd = nanddev_to_mtd(nand);
    356	struct nand_memory_organization *memorg = nanddev_get_memorg(nand);
    357
    358	if (!nand || !ops)
    359		return -EINVAL;
    360
    361	if (!ops->erase || !ops->markbad || !ops->isbad)
    362		return -EINVAL;
    363
    364	if (!memorg->bits_per_cell || !memorg->pagesize ||
    365	    !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun ||
    366	    !memorg->planes_per_lun || !memorg->luns_per_target ||
    367	    !memorg->ntargets)
    368		return -EINVAL;
    369
    370	nand->rowconv.eraseblock_addr_shift =
    371					fls(memorg->pages_per_eraseblock - 1);
    372	nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) +
    373				       nand->rowconv.eraseblock_addr_shift;
    374
    375	nand->ops = ops;
    376
    377	mtd->type = memorg->bits_per_cell == 1 ?
    378		    MTD_NANDFLASH : MTD_MLCNANDFLASH;
    379	mtd->flags = MTD_CAP_NANDFLASH;
    380	mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock;
    381	mtd->writesize = memorg->pagesize;
    382	mtd->writebufsize = memorg->pagesize;
    383	mtd->oobsize = memorg->oobsize;
    384	mtd->size = nanddev_size(nand);
    385	mtd->owner = owner;
    386
    387	return nanddev_bbt_init(nand);
    388}
    389EXPORT_SYMBOL_GPL(nanddev_init);
    390
    391/**
    392 * nanddev_cleanup() - Release resources allocated in nanddev_init()
    393 * @nand: NAND device
    394 *
    395 * Basically undoes what has been done in nanddev_init().
    396 */
    397void nanddev_cleanup(struct nand_device *nand)
    398{
    399	if (nanddev_bbt_is_initialized(nand))
    400		nanddev_bbt_cleanup(nand);
    401}
    402EXPORT_SYMBOL_GPL(nanddev_cleanup);
    403
    404MODULE_DESCRIPTION("Generic NAND framework");
    405MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
    406MODULE_LICENSE("GPL v2");