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