19c3736a3SBoris Brezillon // SPDX-License-Identifier: GPL-2.0 29c3736a3SBoris Brezillon /* 39c3736a3SBoris Brezillon * Copyright (c) 2017 Free Electrons 49c3736a3SBoris Brezillon * 59c3736a3SBoris Brezillon * Authors: 69c3736a3SBoris Brezillon * Boris Brezillon <boris.brezillon@free-electrons.com> 79c3736a3SBoris Brezillon * Peter Pan <peterpandong@micron.com> 89c3736a3SBoris Brezillon */ 99c3736a3SBoris Brezillon 109c3736a3SBoris Brezillon #define pr_fmt(fmt) "nand: " fmt 119c3736a3SBoris Brezillon 129c3736a3SBoris Brezillon #include <linux/module.h> 139c3736a3SBoris Brezillon #include <linux/mtd/nand.h> 149c3736a3SBoris Brezillon 159c3736a3SBoris Brezillon /** 169c3736a3SBoris Brezillon * nanddev_isbad() - Check if a block is bad 179c3736a3SBoris Brezillon * @nand: NAND device 189c3736a3SBoris Brezillon * @pos: position pointing to the block we want to check 199c3736a3SBoris Brezillon * 209c3736a3SBoris Brezillon * Return: true if the block is bad, false otherwise. 219c3736a3SBoris Brezillon */ 229c3736a3SBoris Brezillon bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) 239c3736a3SBoris Brezillon { 249c3736a3SBoris Brezillon if (nanddev_bbt_is_initialized(nand)) { 259c3736a3SBoris Brezillon unsigned int entry; 269c3736a3SBoris Brezillon int status; 279c3736a3SBoris Brezillon 289c3736a3SBoris Brezillon entry = nanddev_bbt_pos_to_entry(nand, pos); 299c3736a3SBoris Brezillon status = nanddev_bbt_get_block_status(nand, entry); 309c3736a3SBoris Brezillon /* Lazy block status retrieval */ 319c3736a3SBoris Brezillon if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { 329c3736a3SBoris Brezillon if (nand->ops->isbad(nand, pos)) 339c3736a3SBoris Brezillon status = NAND_BBT_BLOCK_FACTORY_BAD; 349c3736a3SBoris Brezillon else 359c3736a3SBoris Brezillon status = NAND_BBT_BLOCK_GOOD; 369c3736a3SBoris Brezillon 379c3736a3SBoris Brezillon nanddev_bbt_set_block_status(nand, entry, status); 389c3736a3SBoris Brezillon } 399c3736a3SBoris Brezillon 409c3736a3SBoris Brezillon if (status == NAND_BBT_BLOCK_WORN || 419c3736a3SBoris Brezillon status == NAND_BBT_BLOCK_FACTORY_BAD) 429c3736a3SBoris Brezillon return true; 439c3736a3SBoris Brezillon 449c3736a3SBoris Brezillon return false; 459c3736a3SBoris Brezillon } 469c3736a3SBoris Brezillon 479c3736a3SBoris Brezillon return nand->ops->isbad(nand, pos); 489c3736a3SBoris Brezillon } 499c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_isbad); 509c3736a3SBoris Brezillon 519c3736a3SBoris Brezillon /** 529c3736a3SBoris Brezillon * nanddev_markbad() - Mark a block as bad 539c3736a3SBoris Brezillon * @nand: NAND device 54097ccca7SXiaolei Li * @pos: position of the block to mark bad 559c3736a3SBoris Brezillon * 569c3736a3SBoris Brezillon * Mark a block bad. This function is updating the BBT if available and 579c3736a3SBoris Brezillon * calls the low-level markbad hook (nand->ops->markbad()). 589c3736a3SBoris Brezillon * 599c3736a3SBoris Brezillon * Return: 0 in case of success, a negative error code otherwise. 609c3736a3SBoris Brezillon */ 619c3736a3SBoris Brezillon int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) 629c3736a3SBoris Brezillon { 639c3736a3SBoris Brezillon struct mtd_info *mtd = nanddev_to_mtd(nand); 649c3736a3SBoris Brezillon unsigned int entry; 659c3736a3SBoris Brezillon int ret = 0; 669c3736a3SBoris Brezillon 679c3736a3SBoris Brezillon if (nanddev_isbad(nand, pos)) 689c3736a3SBoris Brezillon return 0; 699c3736a3SBoris Brezillon 709c3736a3SBoris Brezillon ret = nand->ops->markbad(nand, pos); 719c3736a3SBoris Brezillon if (ret) 729c3736a3SBoris Brezillon pr_warn("failed to write BBM to block @%llx (err = %d)\n", 739c3736a3SBoris Brezillon nanddev_pos_to_offs(nand, pos), ret); 749c3736a3SBoris Brezillon 759c3736a3SBoris Brezillon if (!nanddev_bbt_is_initialized(nand)) 769c3736a3SBoris Brezillon goto out; 779c3736a3SBoris Brezillon 789c3736a3SBoris Brezillon entry = nanddev_bbt_pos_to_entry(nand, pos); 799c3736a3SBoris Brezillon ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); 809c3736a3SBoris Brezillon if (ret) 819c3736a3SBoris Brezillon goto out; 829c3736a3SBoris Brezillon 839c3736a3SBoris Brezillon ret = nanddev_bbt_update(nand); 849c3736a3SBoris Brezillon 859c3736a3SBoris Brezillon out: 869c3736a3SBoris Brezillon if (!ret) 879c3736a3SBoris Brezillon mtd->ecc_stats.badblocks++; 889c3736a3SBoris Brezillon 899c3736a3SBoris Brezillon return ret; 909c3736a3SBoris Brezillon } 919c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_markbad); 929c3736a3SBoris Brezillon 939c3736a3SBoris Brezillon /** 949c3736a3SBoris Brezillon * nanddev_isreserved() - Check whether an eraseblock is reserved or not 959c3736a3SBoris Brezillon * @nand: NAND device 969c3736a3SBoris Brezillon * @pos: NAND position to test 979c3736a3SBoris Brezillon * 989c3736a3SBoris Brezillon * Checks whether the eraseblock pointed by @pos is reserved or not. 999c3736a3SBoris Brezillon * 1009c3736a3SBoris Brezillon * Return: true if the eraseblock is reserved, false otherwise. 1019c3736a3SBoris Brezillon */ 1029c3736a3SBoris Brezillon bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) 1039c3736a3SBoris Brezillon { 1049c3736a3SBoris Brezillon unsigned int entry; 1059c3736a3SBoris Brezillon int status; 1069c3736a3SBoris Brezillon 1079c3736a3SBoris Brezillon if (!nanddev_bbt_is_initialized(nand)) 1089c3736a3SBoris Brezillon return false; 1099c3736a3SBoris Brezillon 1109c3736a3SBoris Brezillon /* Return info from the table */ 1119c3736a3SBoris Brezillon entry = nanddev_bbt_pos_to_entry(nand, pos); 1129c3736a3SBoris Brezillon status = nanddev_bbt_get_block_status(nand, entry); 1139c3736a3SBoris Brezillon return status == NAND_BBT_BLOCK_RESERVED; 1149c3736a3SBoris Brezillon } 1159c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_isreserved); 1169c3736a3SBoris Brezillon 1179c3736a3SBoris Brezillon /** 1189c3736a3SBoris Brezillon * nanddev_erase() - Erase a NAND portion 1199c3736a3SBoris Brezillon * @nand: NAND device 120097ccca7SXiaolei Li * @pos: position of the block to erase 1219c3736a3SBoris Brezillon * 122097ccca7SXiaolei Li * Erases the block if it's not bad. 1239c3736a3SBoris Brezillon * 1249c3736a3SBoris Brezillon * Return: 0 in case of success, a negative error code otherwise. 1259c3736a3SBoris Brezillon */ 1269c3736a3SBoris Brezillon int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) 1279c3736a3SBoris Brezillon { 1289c3736a3SBoris Brezillon if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { 1299c3736a3SBoris Brezillon pr_warn("attempt to erase a bad/reserved block @%llx\n", 1309c3736a3SBoris Brezillon nanddev_pos_to_offs(nand, pos)); 1319c3736a3SBoris Brezillon return -EIO; 1329c3736a3SBoris Brezillon } 1339c3736a3SBoris Brezillon 1349c3736a3SBoris Brezillon return nand->ops->erase(nand, pos); 1359c3736a3SBoris Brezillon } 1369c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_erase); 1379c3736a3SBoris Brezillon 1389c3736a3SBoris Brezillon /** 1399c3736a3SBoris Brezillon * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices 1409c3736a3SBoris Brezillon * @mtd: MTD device 1419c3736a3SBoris Brezillon * @einfo: erase request 1429c3736a3SBoris Brezillon * 1439c3736a3SBoris Brezillon * This is a simple mtd->_erase() implementation iterating over all blocks 1449c3736a3SBoris Brezillon * concerned by @einfo and calling nand->ops->erase() on each of them. 1459c3736a3SBoris Brezillon * 1469c3736a3SBoris Brezillon * Note that mtd->_erase should not be directly assigned to this helper, 1479c3736a3SBoris Brezillon * because there's no locking here. NAND specialized layers should instead 1489c3736a3SBoris Brezillon * implement there own wrapper around nanddev_mtd_erase() taking the 1499c3736a3SBoris Brezillon * appropriate lock before calling nanddev_mtd_erase(). 1509c3736a3SBoris Brezillon * 1519c3736a3SBoris Brezillon * Return: 0 in case of success, a negative error code otherwise. 1529c3736a3SBoris Brezillon */ 1539c3736a3SBoris Brezillon int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo) 1549c3736a3SBoris Brezillon { 1559c3736a3SBoris Brezillon struct nand_device *nand = mtd_to_nanddev(mtd); 1569c3736a3SBoris Brezillon struct nand_pos pos, last; 1579c3736a3SBoris Brezillon int ret; 1589c3736a3SBoris Brezillon 1599c3736a3SBoris Brezillon nanddev_offs_to_pos(nand, einfo->addr, &pos); 1609c3736a3SBoris Brezillon nanddev_offs_to_pos(nand, einfo->addr + einfo->len - 1, &last); 1619c3736a3SBoris Brezillon while (nanddev_pos_cmp(&pos, &last) <= 0) { 1629c3736a3SBoris Brezillon ret = nanddev_erase(nand, &pos); 1639c3736a3SBoris Brezillon if (ret) { 1649c3736a3SBoris Brezillon einfo->fail_addr = nanddev_pos_to_offs(nand, &pos); 1659c3736a3SBoris Brezillon 1669c3736a3SBoris Brezillon return ret; 1679c3736a3SBoris Brezillon } 1689c3736a3SBoris Brezillon 1699c3736a3SBoris Brezillon nanddev_pos_next_eraseblock(nand, &pos); 1709c3736a3SBoris Brezillon } 1719c3736a3SBoris Brezillon 1729c3736a3SBoris Brezillon return 0; 1739c3736a3SBoris Brezillon } 1749c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_mtd_erase); 1759c3736a3SBoris Brezillon 1769c3736a3SBoris Brezillon /** 177377e517bSBoris Brezillon * nanddev_mtd_max_bad_blocks() - Get the maximum number of bad eraseblock on 178377e517bSBoris Brezillon * a specific region of the NAND device 179377e517bSBoris Brezillon * @mtd: MTD device 180377e517bSBoris Brezillon * @offs: offset of the NAND region 181377e517bSBoris Brezillon * @len: length of the NAND region 182377e517bSBoris Brezillon * 183377e517bSBoris Brezillon * Default implementation for mtd->_max_bad_blocks(). Only works if 184377e517bSBoris Brezillon * nand->memorg.max_bad_eraseblocks_per_lun is > 0. 185377e517bSBoris Brezillon * 186377e517bSBoris Brezillon * Return: a positive number encoding the maximum number of eraseblocks on a 187377e517bSBoris Brezillon * portion of memory, a negative error code otherwise. 188377e517bSBoris Brezillon */ 189377e517bSBoris Brezillon int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len) 190377e517bSBoris Brezillon { 191377e517bSBoris Brezillon struct nand_device *nand = mtd_to_nanddev(mtd); 192377e517bSBoris Brezillon struct nand_pos pos, end; 193377e517bSBoris Brezillon unsigned int max_bb = 0; 194377e517bSBoris Brezillon 195377e517bSBoris Brezillon if (!nand->memorg.max_bad_eraseblocks_per_lun) 196377e517bSBoris Brezillon return -ENOTSUPP; 197377e517bSBoris Brezillon 198377e517bSBoris Brezillon nanddev_offs_to_pos(nand, offs, &pos); 199377e517bSBoris Brezillon nanddev_offs_to_pos(nand, offs + len, &end); 200377e517bSBoris Brezillon 201377e517bSBoris Brezillon for (nanddev_offs_to_pos(nand, offs, &pos); 202377e517bSBoris Brezillon nanddev_pos_cmp(&pos, &end) < 0; 203377e517bSBoris Brezillon nanddev_pos_next_lun(nand, &pos)) 204377e517bSBoris Brezillon max_bb += nand->memorg.max_bad_eraseblocks_per_lun; 205377e517bSBoris Brezillon 206377e517bSBoris Brezillon return max_bb; 207377e517bSBoris Brezillon } 208377e517bSBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_mtd_max_bad_blocks); 209377e517bSBoris Brezillon 210377e517bSBoris Brezillon /** 211*6b0c3b84SMiquel Raynal * nanddev_get_ecc_engine() - Find and get a suitable ECC engine 212*6b0c3b84SMiquel Raynal * @nand: NAND device 213*6b0c3b84SMiquel Raynal */ 214*6b0c3b84SMiquel Raynal static int nanddev_get_ecc_engine(struct nand_device *nand) 215*6b0c3b84SMiquel Raynal { 216*6b0c3b84SMiquel Raynal int engine_type; 217*6b0c3b84SMiquel Raynal 218*6b0c3b84SMiquel Raynal /* Read the user desires in terms of ECC engine/configuration */ 219*6b0c3b84SMiquel Raynal of_get_nand_ecc_user_config(nand); 220*6b0c3b84SMiquel Raynal 221*6b0c3b84SMiquel Raynal engine_type = nand->ecc.user_conf.engine_type; 222*6b0c3b84SMiquel Raynal if (engine_type == NAND_ECC_ENGINE_TYPE_INVALID) 223*6b0c3b84SMiquel Raynal engine_type = nand->ecc.defaults.engine_type; 224*6b0c3b84SMiquel Raynal 225*6b0c3b84SMiquel Raynal switch (engine_type) { 226*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_NONE: 227*6b0c3b84SMiquel Raynal return 0; 228*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_SOFT: 229*6b0c3b84SMiquel Raynal nand->ecc.engine = nand_ecc_get_sw_engine(nand); 230*6b0c3b84SMiquel Raynal break; 231*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_DIE: 232*6b0c3b84SMiquel Raynal nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand); 233*6b0c3b84SMiquel Raynal break; 234*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_HOST: 235*6b0c3b84SMiquel Raynal pr_err("On-host hardware ECC engines not supported yet\n"); 236*6b0c3b84SMiquel Raynal break; 237*6b0c3b84SMiquel Raynal default: 238*6b0c3b84SMiquel Raynal pr_err("Missing ECC engine type\n"); 239*6b0c3b84SMiquel Raynal } 240*6b0c3b84SMiquel Raynal 241*6b0c3b84SMiquel Raynal if (!nand->ecc.engine) 242*6b0c3b84SMiquel Raynal return -EINVAL; 243*6b0c3b84SMiquel Raynal 244*6b0c3b84SMiquel Raynal return 0; 245*6b0c3b84SMiquel Raynal } 246*6b0c3b84SMiquel Raynal 247*6b0c3b84SMiquel Raynal /** 248*6b0c3b84SMiquel Raynal * nanddev_put_ecc_engine() - Dettach and put the in-use ECC engine 249*6b0c3b84SMiquel Raynal * @nand: NAND device 250*6b0c3b84SMiquel Raynal */ 251*6b0c3b84SMiquel Raynal static int nanddev_put_ecc_engine(struct nand_device *nand) 252*6b0c3b84SMiquel Raynal { 253*6b0c3b84SMiquel Raynal switch (nand->ecc.ctx.conf.engine_type) { 254*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_HOST: 255*6b0c3b84SMiquel Raynal pr_err("On-host hardware ECC engines not supported yet\n"); 256*6b0c3b84SMiquel Raynal break; 257*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_NONE: 258*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_SOFT: 259*6b0c3b84SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_DIE: 260*6b0c3b84SMiquel Raynal default: 261*6b0c3b84SMiquel Raynal break; 262*6b0c3b84SMiquel Raynal } 263*6b0c3b84SMiquel Raynal 264*6b0c3b84SMiquel Raynal return 0; 265*6b0c3b84SMiquel Raynal } 266*6b0c3b84SMiquel Raynal 267*6b0c3b84SMiquel Raynal /** 268*6b0c3b84SMiquel Raynal * nanddev_find_ecc_configuration() - Find a suitable ECC configuration 269*6b0c3b84SMiquel Raynal * @nand: NAND device 270*6b0c3b84SMiquel Raynal */ 271*6b0c3b84SMiquel Raynal static int nanddev_find_ecc_configuration(struct nand_device *nand) 272*6b0c3b84SMiquel Raynal { 273*6b0c3b84SMiquel Raynal int ret; 274*6b0c3b84SMiquel Raynal 275*6b0c3b84SMiquel Raynal if (!nand->ecc.engine) 276*6b0c3b84SMiquel Raynal return -ENOTSUPP; 277*6b0c3b84SMiquel Raynal 278*6b0c3b84SMiquel Raynal ret = nand_ecc_init_ctx(nand); 279*6b0c3b84SMiquel Raynal if (ret) 280*6b0c3b84SMiquel Raynal return ret; 281*6b0c3b84SMiquel Raynal 282*6b0c3b84SMiquel Raynal if (!nand_ecc_is_strong_enough(nand)) 283*6b0c3b84SMiquel Raynal pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n", 284*6b0c3b84SMiquel Raynal nand->mtd.name); 285*6b0c3b84SMiquel Raynal 286*6b0c3b84SMiquel Raynal return 0; 287*6b0c3b84SMiquel Raynal } 288*6b0c3b84SMiquel Raynal 289*6b0c3b84SMiquel Raynal /** 290*6b0c3b84SMiquel Raynal * nanddev_ecc_engine_init() - Initialize an ECC engine for the chip 291*6b0c3b84SMiquel Raynal * @nand: NAND device 292*6b0c3b84SMiquel Raynal */ 293*6b0c3b84SMiquel Raynal int nanddev_ecc_engine_init(struct nand_device *nand) 294*6b0c3b84SMiquel Raynal { 295*6b0c3b84SMiquel Raynal int ret; 296*6b0c3b84SMiquel Raynal 297*6b0c3b84SMiquel Raynal /* Look for the ECC engine to use */ 298*6b0c3b84SMiquel Raynal ret = nanddev_get_ecc_engine(nand); 299*6b0c3b84SMiquel Raynal if (ret) { 300*6b0c3b84SMiquel Raynal pr_err("No ECC engine found\n"); 301*6b0c3b84SMiquel Raynal return ret; 302*6b0c3b84SMiquel Raynal } 303*6b0c3b84SMiquel Raynal 304*6b0c3b84SMiquel Raynal /* No ECC engine requested */ 305*6b0c3b84SMiquel Raynal if (!nand->ecc.engine) 306*6b0c3b84SMiquel Raynal return 0; 307*6b0c3b84SMiquel Raynal 308*6b0c3b84SMiquel Raynal /* Configure the engine: balance user input and chip requirements */ 309*6b0c3b84SMiquel Raynal ret = nanddev_find_ecc_configuration(nand); 310*6b0c3b84SMiquel Raynal if (ret) { 311*6b0c3b84SMiquel Raynal pr_err("No suitable ECC configuration\n"); 312*6b0c3b84SMiquel Raynal nanddev_put_ecc_engine(nand); 313*6b0c3b84SMiquel Raynal 314*6b0c3b84SMiquel Raynal return ret; 315*6b0c3b84SMiquel Raynal } 316*6b0c3b84SMiquel Raynal 317*6b0c3b84SMiquel Raynal return 0; 318*6b0c3b84SMiquel Raynal } 319*6b0c3b84SMiquel Raynal EXPORT_SYMBOL_GPL(nanddev_ecc_engine_init); 320*6b0c3b84SMiquel Raynal 321*6b0c3b84SMiquel Raynal /** 322*6b0c3b84SMiquel Raynal * nanddev_ecc_engine_cleanup() - Cleanup ECC engine initializations 323*6b0c3b84SMiquel Raynal * @nand: NAND device 324*6b0c3b84SMiquel Raynal */ 325*6b0c3b84SMiquel Raynal void nanddev_ecc_engine_cleanup(struct nand_device *nand) 326*6b0c3b84SMiquel Raynal { 327*6b0c3b84SMiquel Raynal if (nand->ecc.engine) 328*6b0c3b84SMiquel Raynal nand_ecc_cleanup_ctx(nand); 329*6b0c3b84SMiquel Raynal 330*6b0c3b84SMiquel Raynal nanddev_put_ecc_engine(nand); 331*6b0c3b84SMiquel Raynal } 332*6b0c3b84SMiquel Raynal EXPORT_SYMBOL_GPL(nanddev_ecc_engine_cleanup); 333*6b0c3b84SMiquel Raynal 334*6b0c3b84SMiquel Raynal /** 3359c3736a3SBoris Brezillon * nanddev_init() - Initialize a NAND device 3369c3736a3SBoris Brezillon * @nand: NAND device 3379c3736a3SBoris Brezillon * @ops: NAND device operations 338097ccca7SXiaolei Li * @owner: NAND device owner 3399c3736a3SBoris Brezillon * 340097ccca7SXiaolei Li * Initializes a NAND device object. Consistency checks are done on @ops and 341097ccca7SXiaolei Li * @nand->memorg. Also takes care of initializing the BBT. 3429c3736a3SBoris Brezillon * 3439c3736a3SBoris Brezillon * Return: 0 in case of success, a negative error code otherwise. 3449c3736a3SBoris Brezillon */ 3459c3736a3SBoris Brezillon int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, 3469c3736a3SBoris Brezillon struct module *owner) 3479c3736a3SBoris Brezillon { 3489c3736a3SBoris Brezillon struct mtd_info *mtd = nanddev_to_mtd(nand); 3499c3736a3SBoris Brezillon struct nand_memory_organization *memorg = nanddev_get_memorg(nand); 3509c3736a3SBoris Brezillon 3519c3736a3SBoris Brezillon if (!nand || !ops) 3529c3736a3SBoris Brezillon return -EINVAL; 3539c3736a3SBoris Brezillon 3549c3736a3SBoris Brezillon if (!ops->erase || !ops->markbad || !ops->isbad) 3559c3736a3SBoris Brezillon return -EINVAL; 3569c3736a3SBoris Brezillon 3579c3736a3SBoris Brezillon if (!memorg->bits_per_cell || !memorg->pagesize || 3589c3736a3SBoris Brezillon !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || 3599c3736a3SBoris Brezillon !memorg->planes_per_lun || !memorg->luns_per_target || 3609c3736a3SBoris Brezillon !memorg->ntargets) 3619c3736a3SBoris Brezillon return -EINVAL; 3629c3736a3SBoris Brezillon 3639c3736a3SBoris Brezillon nand->rowconv.eraseblock_addr_shift = 3649c3736a3SBoris Brezillon fls(memorg->pages_per_eraseblock - 1); 3659c3736a3SBoris Brezillon nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + 3669c3736a3SBoris Brezillon nand->rowconv.eraseblock_addr_shift; 3679c3736a3SBoris Brezillon 3689c3736a3SBoris Brezillon nand->ops = ops; 3699c3736a3SBoris Brezillon 3709c3736a3SBoris Brezillon mtd->type = memorg->bits_per_cell == 1 ? 3719c3736a3SBoris Brezillon MTD_NANDFLASH : MTD_MLCNANDFLASH; 3729c3736a3SBoris Brezillon mtd->flags = MTD_CAP_NANDFLASH; 3739c3736a3SBoris Brezillon mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; 3749c3736a3SBoris Brezillon mtd->writesize = memorg->pagesize; 3759c3736a3SBoris Brezillon mtd->writebufsize = memorg->pagesize; 3769c3736a3SBoris Brezillon mtd->oobsize = memorg->oobsize; 3779c3736a3SBoris Brezillon mtd->size = nanddev_size(nand); 3789c3736a3SBoris Brezillon mtd->owner = owner; 3799c3736a3SBoris Brezillon 3809c3736a3SBoris Brezillon return nanddev_bbt_init(nand); 3819c3736a3SBoris Brezillon } 3829c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_init); 3839c3736a3SBoris Brezillon 3849c3736a3SBoris Brezillon /** 3859c3736a3SBoris Brezillon * nanddev_cleanup() - Release resources allocated in nanddev_init() 3869c3736a3SBoris Brezillon * @nand: NAND device 3879c3736a3SBoris Brezillon * 3889c3736a3SBoris Brezillon * Basically undoes what has been done in nanddev_init(). 3899c3736a3SBoris Brezillon */ 3909c3736a3SBoris Brezillon void nanddev_cleanup(struct nand_device *nand) 3919c3736a3SBoris Brezillon { 3929c3736a3SBoris Brezillon if (nanddev_bbt_is_initialized(nand)) 3939c3736a3SBoris Brezillon nanddev_bbt_cleanup(nand); 3949c3736a3SBoris Brezillon } 3959c3736a3SBoris Brezillon EXPORT_SYMBOL_GPL(nanddev_cleanup); 3969c3736a3SBoris Brezillon 3979c3736a3SBoris Brezillon MODULE_DESCRIPTION("Generic NAND framework"); 3989c3736a3SBoris Brezillon MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 3999c3736a3SBoris Brezillon MODULE_LICENSE("GPL v2"); 400