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