xref: /netbsd-src/sys/dev/nand/nand_bbt.c (revision df8b6b33518952d1e7bd12b718c9c552e99bd2e7)
1 /*	$NetBSD: nand_bbt.c,v 1.8 2018/02/08 07:48:19 mrg Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011 Department of Software Engineering,
5  *		      University of Szeged, Hungary
6  * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by the Department of Software Engineering, University of Szeged, Hungary
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /*
35  * Implementation of Bad Block Tables (BBTs).
36  */
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: nand_bbt.c,v 1.8 2018/02/08 07:48:19 mrg Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/kmem.h>
43 
44 #include "nand.h"
45 #include "nand_bbt.h"
46 
47 void
nand_bbt_init(device_t self)48 nand_bbt_init(device_t self)
49 {
50 	struct nand_softc *sc = device_private(self);
51 	struct nand_chip *chip = &sc->sc_chip;
52 	struct nand_bbt *bbt = &sc->sc_bbt;
53 
54 	bbt->nbbt_size = chip->nc_size / chip->nc_block_size / 4;
55 	bbt->nbbt_bitmap = kmem_alloc(bbt->nbbt_size, KM_SLEEP);
56 
57 	memset(bbt->nbbt_bitmap, 0xff, bbt->nbbt_size);
58 }
59 
60 void
nand_bbt_detach(device_t self)61 nand_bbt_detach(device_t self)
62 {
63 	struct nand_softc *sc = device_private(self);
64 	struct nand_bbt *bbt = &sc->sc_bbt;
65 
66 	kmem_free(bbt->nbbt_bitmap, bbt->nbbt_size);
67 }
68 
69 void
nand_bbt_scan(device_t self)70 nand_bbt_scan(device_t self)
71 {
72 	struct nand_softc *sc = device_private(self);
73 	struct nand_chip *chip = &sc->sc_chip;
74 	flash_off_t i, blocks, addr;
75 
76 	blocks = chip->nc_size / chip->nc_block_size;
77 
78 	aprint_normal_dev(self, "scanning for bad blocks\n");
79 
80 	addr = 0;
81 	for (i = 0; i < blocks; i++) {
82 		if (nand_isfactorybad(self, addr)) {
83 			nand_bbt_block_markfactorybad(self, i);
84 		} else if (nand_iswornoutbad(self, addr)) {
85 			nand_bbt_block_markbad(self, i);
86 		}
87 
88 		addr += chip->nc_block_size;
89 	}
90 }
91 
92 bool
nand_bbt_update(device_t self)93 nand_bbt_update(device_t self)
94 {
95 	return true;
96 }
97 
98 static bool
nand_bbt_page_has_bbt(device_t self,flash_off_t addr)99 nand_bbt_page_has_bbt(device_t self, flash_off_t addr) {
100 	struct nand_softc *sc = device_private(self);
101 	struct nand_chip *chip = &sc->sc_chip;
102 	uint8_t *oob = chip->nc_oob_cache;
103 
104 	nand_read_oob(self, addr, oob);
105 
106 	if (oob[NAND_BBT_OFFSET] == 'B' &&
107 	    oob[NAND_BBT_OFFSET + 1] == 'b' &&
108 	    oob[NAND_BBT_OFFSET + 2] == 't') {
109 		return true;
110 	} else {
111 		return false;
112 	}
113 }
114 
115 static bool
nand_bbt_get_bbt_from_page(device_t self,flash_off_t addr)116 nand_bbt_get_bbt_from_page(device_t self, flash_off_t addr)
117 {
118 	struct nand_softc *sc = device_private(self);
119 	struct nand_chip *chip = &sc->sc_chip;
120 	struct nand_bbt *bbt = &sc->sc_bbt;
121 	uint8_t *bbtp, *buf = chip->nc_page_cache;
122 	size_t left, bbt_pages, i;
123 
124 	bbt_pages = bbt->nbbt_size / chip->nc_page_size;
125 	if (bbt->nbbt_size % chip->nc_page_size)
126 		bbt_pages++;
127 
128 	if (nand_isbad(self, addr)) {
129 		return false;
130 	}
131 
132 	if (nand_bbt_page_has_bbt(self, addr)) {
133 		bbtp = bbt->nbbt_bitmap;
134 		left = bbt->nbbt_size;
135 
136 		for (i = 0; i < bbt_pages; i++) {
137 			nand_read_page(self, addr, buf);
138 
139 			if (i == bbt_pages - 1) {
140 				KASSERT(left <= chip->nc_page_size);
141 				memcpy(bbtp, buf, left);
142 			} else {
143 				memcpy(bbtp, buf, chip->nc_page_size);
144 			}
145 
146 			bbtp += chip->nc_page_size;
147 			left -= chip->nc_page_size;
148 			addr += chip->nc_page_size;
149 		}
150 
151 		return true;
152 	} else {
153 		return false;
154 	}
155 }
156 
157 bool
nand_bbt_load(device_t self)158 nand_bbt_load(device_t self)
159 {
160 	struct nand_softc *sc = device_private(self);
161 	struct nand_chip *chip = &sc->sc_chip;
162 	flash_off_t blockaddr;
163 	int n;
164 
165 	blockaddr = chip->nc_size - chip->nc_block_size;
166 	/* XXX currently we check the last 4 blocks */
167 	for (n = 0; n < 4; n++) {
168 		if (nand_bbt_get_bbt_from_page(self, blockaddr)) {
169 			break;
170 		} else {
171 			blockaddr -= chip->nc_block_size;
172 		}
173 	}
174 
175 	return true;
176 }
177 
178 void
nand_bbt_block_markbad(device_t self,flash_off_t block)179 nand_bbt_block_markbad(device_t self, flash_off_t block)
180 {
181 	if (nand_bbt_block_isbad(self, block)) {
182 		aprint_error_dev(self,
183 		    "trying to mark block bad already marked in bbt\n");
184 	}
185 	/* XXX check if this is the correct marker */
186 	nand_bbt_block_mark(self, block, NAND_BBT_MARKER_WORNOUT_BAD);
187 }
188 
189 void
nand_bbt_block_markfactorybad(device_t self,flash_off_t block)190 nand_bbt_block_markfactorybad(device_t self, flash_off_t block)
191 {
192 	if (nand_bbt_block_isbad(self, block)) {
193 		aprint_error_dev(self,
194 		    "trying to mark block factory bad already"
195 		    " marked in bbt\n");
196 	}
197 	nand_bbt_block_mark(self, block, NAND_BBT_MARKER_FACTORY_BAD);
198 }
199 
200 void
nand_bbt_block_mark(device_t self,flash_off_t block,uint8_t marker)201 nand_bbt_block_mark(device_t self, flash_off_t block, uint8_t marker)
202 {
203 	struct nand_softc *sc = device_private(self);
204 	struct nand_chip *chip = &sc->sc_chip;
205 	struct nand_bbt *bbt = &sc->sc_bbt;
206 	uint8_t clean;
207 
208 	__USE(chip);
209 	KASSERT(block < chip->nc_size / chip->nc_block_size);
210 
211 	clean = (0xfc << ((block % 4) * 2));
212 	marker = (marker << ((block % 4) * 2));
213 
214 	/* set byte containing the 2 bit marker for this block */
215 	bbt->nbbt_bitmap[block / 4] &= clean;
216 	bbt->nbbt_bitmap[block / 4] |= marker;
217 }
218 
219 bool
nand_bbt_block_isbad(device_t self,flash_off_t block)220 nand_bbt_block_isbad(device_t self, flash_off_t block)
221 {
222 	struct nand_softc *sc = device_private(self);
223 	struct nand_chip *chip = &sc->sc_chip;
224 	struct nand_bbt *bbt = &sc->sc_bbt;
225 	uint8_t byte, marker;
226 	bool result;
227 
228 	__USE(chip);
229 	KASSERT(block < chip->nc_size / chip->nc_block_size);
230 
231 	/* get byte containing the 2 bit marker for this block */
232 	byte = bbt->nbbt_bitmap[block / 4];
233 
234 	/* extract the 2 bit marker from the byte */
235 	marker = (byte >> ((block % 4) * 2)) & 0x03;
236 
237 	switch (marker) {
238 	case NAND_BBT_MARKER_FACTORY_BAD:
239 	case NAND_BBT_MARKER_WORNOUT_BAD:
240 	case NAND_BBT_MARKER_RESERVED:
241 		result = true;
242 		break;
243 	case NAND_BBT_MARKER_GOOD:
244 		result = false;
245 		break;
246 	default:
247 		panic("error in marker extraction");
248 	}
249 
250 	return result;
251 }
252