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 #ifndef __UBOOT__ 13 #include <linux/module.h> 14 #endif 15 #include <linux/mtd/nand.h> 16 17 /** 18 * nanddev_isbad() - Check if a block is bad 19 * @nand: NAND device 20 * @pos: position pointing to the block we want to check 21 * 22 * Return: true if the block is bad, false otherwise. 23 */ 24 bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos) 25 { 26 if (nanddev_bbt_is_initialized(nand)) { 27 unsigned int entry; 28 int status; 29 30 entry = nanddev_bbt_pos_to_entry(nand, pos); 31 status = nanddev_bbt_get_block_status(nand, entry); 32 /* Lazy block status retrieval */ 33 if (status == NAND_BBT_BLOCK_STATUS_UNKNOWN) { 34 if (nand->ops->isbad(nand, pos)) 35 status = NAND_BBT_BLOCK_FACTORY_BAD; 36 else 37 status = NAND_BBT_BLOCK_GOOD; 38 39 nanddev_bbt_set_block_status(nand, entry, status); 40 } 41 42 if (status == NAND_BBT_BLOCK_WORN || 43 status == NAND_BBT_BLOCK_FACTORY_BAD) 44 return true; 45 46 return false; 47 } 48 49 return nand->ops->isbad(nand, pos); 50 } 51 EXPORT_SYMBOL_GPL(nanddev_isbad); 52 53 /** 54 * nanddev_markbad() - Mark a block as bad 55 * @nand: NAND device 56 * @pos: position of the block to mark bad 57 * 58 * Mark a block bad. This function is updating the BBT if available and 59 * calls the low-level markbad hook (nand->ops->markbad()). 60 * 61 * Return: 0 in case of success, a negative error code otherwise. 62 */ 63 int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos) 64 { 65 struct mtd_info *mtd = nanddev_to_mtd(nand); 66 unsigned int entry; 67 int ret = 0; 68 69 if (nanddev_isbad(nand, pos)) 70 return 0; 71 72 ret = nand->ops->markbad(nand, pos); 73 if (ret) 74 pr_warn("failed to write BBM to block @%llx (err = %d)\n", 75 nanddev_pos_to_offs(nand, pos), ret); 76 77 if (!nanddev_bbt_is_initialized(nand)) 78 goto out; 79 80 entry = nanddev_bbt_pos_to_entry(nand, pos); 81 ret = nanddev_bbt_set_block_status(nand, entry, NAND_BBT_BLOCK_WORN); 82 if (ret) 83 goto out; 84 85 ret = nanddev_bbt_update(nand); 86 87 out: 88 if (!ret) 89 mtd->ecc_stats.badblocks++; 90 91 return ret; 92 } 93 EXPORT_SYMBOL_GPL(nanddev_markbad); 94 95 /** 96 * nanddev_isreserved() - Check whether an eraseblock is reserved or not 97 * @nand: NAND device 98 * @pos: NAND position to test 99 * 100 * Checks whether the eraseblock pointed by @pos is reserved or not. 101 * 102 * Return: true if the eraseblock is reserved, false otherwise. 103 */ 104 bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos) 105 { 106 unsigned int entry; 107 int status; 108 109 if (!nanddev_bbt_is_initialized(nand)) 110 return false; 111 112 /* Return info from the table */ 113 entry = nanddev_bbt_pos_to_entry(nand, pos); 114 status = nanddev_bbt_get_block_status(nand, entry); 115 return status == NAND_BBT_BLOCK_RESERVED; 116 } 117 EXPORT_SYMBOL_GPL(nanddev_isreserved); 118 119 /** 120 * nanddev_erase() - Erase a NAND portion 121 * @nand: NAND device 122 * @pos: position of the block to erase 123 * 124 * Erases the block if it's not bad. 125 * 126 * Return: 0 in case of success, a negative error code otherwise. 127 */ 128 int nanddev_erase(struct nand_device *nand, const struct nand_pos *pos) 129 { 130 if (nanddev_isbad(nand, pos) || nanddev_isreserved(nand, pos)) { 131 pr_warn("attempt to erase a bad/reserved block @%llx\n", 132 nanddev_pos_to_offs(nand, pos)); 133 return -EIO; 134 } 135 136 return nand->ops->erase(nand, pos); 137 } 138 EXPORT_SYMBOL_GPL(nanddev_erase); 139 140 /** 141 * nanddev_mtd_erase() - Generic mtd->_erase() implementation for NAND devices 142 * @mtd: MTD device 143 * @einfo: erase request 144 * 145 * This is a simple mtd->_erase() implementation iterating over all blocks 146 * 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 instead 150 * implement there own wrapper around nanddev_mtd_erase() taking the 151 * appropriate lock before calling nanddev_mtd_erase(). 152 * 153 * Return: 0 in case of success, a negative error code otherwise. 154 */ 155 int 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 } 176 EXPORT_SYMBOL_GPL(nanddev_mtd_erase); 177 178 /** 179 * nanddev_init() - Initialize a NAND device 180 * @nand: NAND device 181 * @ops: NAND device operations 182 * @owner: NAND device owner 183 * 184 * Initializes a NAND device object. Consistency checks are done on @ops and 185 * @nand->memorg. Also takes care of initializing the BBT. 186 * 187 * Return: 0 in case of success, a negative error code otherwise. 188 */ 189 int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, 190 struct module *owner) 191 { 192 struct mtd_info *mtd = nanddev_to_mtd(nand); 193 struct nand_memory_organization *memorg = nanddev_get_memorg(nand); 194 195 if (!nand || !ops) 196 return -EINVAL; 197 198 if (!ops->erase || !ops->markbad || !ops->isbad) 199 return -EINVAL; 200 201 if (!memorg->bits_per_cell || !memorg->pagesize || 202 !memorg->pages_per_eraseblock || !memorg->eraseblocks_per_lun || 203 !memorg->planes_per_lun || !memorg->luns_per_target || 204 !memorg->ntargets) 205 return -EINVAL; 206 207 nand->rowconv.eraseblock_addr_shift = 208 fls(memorg->pages_per_eraseblock - 1); 209 nand->rowconv.lun_addr_shift = fls(memorg->eraseblocks_per_lun - 1) + 210 nand->rowconv.eraseblock_addr_shift; 211 212 nand->ops = ops; 213 214 mtd->type = memorg->bits_per_cell == 1 ? 215 MTD_NANDFLASH : MTD_MLCNANDFLASH; 216 mtd->flags = MTD_CAP_NANDFLASH; 217 mtd->erasesize = memorg->pagesize * memorg->pages_per_eraseblock; 218 mtd->writesize = memorg->pagesize; 219 mtd->writebufsize = memorg->pagesize; 220 mtd->oobsize = memorg->oobsize; 221 mtd->size = nanddev_size(nand); 222 mtd->owner = owner; 223 224 return nanddev_bbt_init(nand); 225 } 226 EXPORT_SYMBOL_GPL(nanddev_init); 227 228 /** 229 * nanddev_cleanup() - Release resources allocated in nanddev_init() 230 * @nand: NAND device 231 * 232 * Basically undoes what has been done in nanddev_init(). 233 */ 234 void nanddev_cleanup(struct nand_device *nand) 235 { 236 if (nanddev_bbt_is_initialized(nand)) 237 nanddev_bbt_cleanup(nand); 238 } 239 EXPORT_SYMBOL_GPL(nanddev_cleanup); 240 241 MODULE_DESCRIPTION("Generic NAND framework"); 242 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 243 MODULE_LICENSE("GPL v2"); 244