xref: /netbsd-src/sys/dev/nand/nand_bbt.c (revision 56bb44cae5b13a6b74792381ba1e6d930b26aa67)
1 /*	$NetBSD: nand_bbt.c,v 1.1 2011/02/26 18:07:31 ahoka 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 /* Support for Bad Block Tables (BBTs) */
35 
36 #include <sys/param.h>
37 #include <sys/kmem.h>
38 
39 #include "nand.h"
40 #include "nand_bbt.h"
41 
42 void
43 nand_bbt_init(device_t self)
44 {
45 	struct nand_softc *sc = device_private(self);
46 	struct nand_chip *chip = &sc->sc_chip;
47 	struct nand_bbt *bbt = &sc->sc_bbt;
48 
49 	bbt->nbbt_size = chip->nc_size / chip->nc_block_size / 4;
50 	bbt->nbbt_bitmap = kmem_alloc(bbt->nbbt_size, KM_SLEEP);
51 
52 	memset(bbt->nbbt_bitmap, 0xff, bbt->nbbt_size);
53 }
54 
55 void
56 nand_bbt_detach(device_t self)
57 {
58 	struct nand_softc *sc = device_private(self);
59 	struct nand_bbt *bbt = &sc->sc_bbt;
60 
61 	printf("freeing bbt bitmap...");
62 	kmem_free(bbt->nbbt_bitmap, bbt->nbbt_size);
63 	printf("done!\n");
64 }
65 
66 void
67 nand_bbt_scan(device_t self)
68 {
69 	struct nand_softc *sc = device_private(self);
70 	struct nand_chip *chip = &sc->sc_chip;
71 	flash_addr_t i, blocks, addr;
72 
73 	blocks = chip->nc_size / chip->nc_block_size;
74 
75 	aprint_normal_dev(self, "scanning for bad blocks\n");
76 
77 	addr = 0;
78 	for (i = 0; i < blocks; i++) {
79 		if (nand_isfactorybad(self, addr)) {
80 			nand_bbt_block_markfactorybad(self, i);
81 		} else if (nand_iswornoutbad(self, addr)) {
82 			nand_bbt_block_markbad(self, i);
83 		}
84 
85 		addr += chip->nc_block_size;
86 	}
87 }
88 
89 bool
90 nand_bbt_update(device_t self)
91 {
92 	return true;
93 }
94 
95 static bool
96 nand_bbt_page_has_bbt(device_t self, flash_addr_t addr) {
97 	struct nand_softc *sc = device_private(self);
98 	struct nand_chip *chip = &sc->sc_chip;
99 	uint8_t *oob = chip->nc_oob_cache;
100 
101 	nand_read_oob(self, addr, oob);
102 
103 	if (oob[NAND_BBT_OFFSET] == 'B' &&
104 	    oob[NAND_BBT_OFFSET + 1] == 'b' &&
105 	    oob[NAND_BBT_OFFSET + 2] == 't') {
106 		return true;
107 	} else {
108 		return false;
109 	}
110 }
111 
112 static bool
113 nand_bbt_get_bbt_from_page(device_t self, flash_addr_t addr)
114 {
115 	struct nand_softc *sc = device_private(self);
116 	struct nand_chip *chip = &sc->sc_chip;
117 	struct nand_bbt *bbt = &sc->sc_bbt;
118 	uint8_t *bbtp, *buf = chip->nc_page_cache;
119 	size_t left, bbt_pages, i;
120 
121 	bbt_pages = bbt->nbbt_size / chip->nc_page_size;
122 	if (bbt->nbbt_size % chip->nc_page_size)
123 		bbt_pages++;
124 
125 	if (nand_isbad(self, addr)) {
126 		return false;
127 	}
128 
129 	if (nand_bbt_page_has_bbt(self, addr)) {
130 		bbtp = bbt->nbbt_bitmap;
131 		left = bbt->nbbt_size;
132 
133 		for (i = 0; i < bbt_pages; i++) {
134 			nand_read_page(self, addr, buf);
135 
136 			if (i == bbt_pages - 1) {
137 				KASSERT(left <= chip->nc_page_size);
138 				memcpy(bbtp, buf, left);
139 			} else {
140 				memcpy(bbtp, buf, chip->nc_page_size);
141 			}
142 
143 			bbtp += chip->nc_page_size;
144 			left -= chip->nc_page_size;
145 			addr += chip->nc_page_size;
146 		}
147 
148 		return true;
149 	} else {
150 		return false;
151 	}
152 }
153 
154 bool
155 nand_bbt_load(device_t self)
156 {
157 	struct nand_softc *sc = device_private(self);
158 	struct nand_chip *chip = &sc->sc_chip;
159 	flash_addr_t blockaddr;
160 	int n;
161 
162 	blockaddr = chip->nc_size - chip->nc_block_size;
163 	/* XXX currently we check the last 4 blocks */
164 	for (n = 0; n < 4; n++) {
165 		if (nand_bbt_get_bbt_from_page(self, blockaddr)) {
166 			break;
167 		} else {
168 			blockaddr -= chip->nc_block_size;
169 		}
170 	}
171 
172 	return true;
173 }
174 
175 void
176 nand_bbt_block_markbad(device_t self, flash_addr_t block)
177 {
178 	if (nand_bbt_block_isbad(self, block)) {
179 		aprint_error_dev(self,
180 		    "trying to mark block bad already marked in bbt\n");
181 	}
182 	/* XXX check if this is the correct marker */
183 	nand_bbt_block_mark(self, block, NAND_BBT_MARKER_WORNOUT_BAD);
184 }
185 
186 void
187 nand_bbt_block_markfactorybad(device_t self, flash_addr_t block)
188 {
189 	if (nand_bbt_block_isbad(self, block)) {
190 		aprint_error_dev(self,
191 		    "trying to mark block factory bad already"
192 		    " marked in bbt\n");
193 	}
194 	nand_bbt_block_mark(self, block, NAND_BBT_MARKER_FACTORY_BAD);
195 }
196 
197 void
198 nand_bbt_block_mark(device_t self, flash_addr_t block, uint8_t marker)
199 {
200 	struct nand_softc *sc = device_private(self);
201 #ifdef DIAGNOSTIC
202 	struct nand_chip *chip = &sc->sc_chip;
203 #endif
204 	struct nand_bbt *bbt = &sc->sc_bbt;
205 	uint8_t clean;
206 
207 	KASSERT(block < chip->nc_size / chip->nc_block_size);
208 
209 	clean = (~0x03 << ((block % 4) * 2));
210 	marker = (marker << ((block % 4) * 2));
211 
212 	/* set byte containing the 2 bit marker for this block */
213 	bbt->nbbt_bitmap[block / 4] &= clean;
214 	bbt->nbbt_bitmap[block / 4] |= marker;
215 }
216 
217 bool
218 nand_bbt_block_isbad(device_t self, flash_addr_t block)
219 {
220 	struct nand_softc *sc = device_private(self);
221 #ifdef DIAGNOSTIC
222 	struct nand_chip *chip = &sc->sc_chip;
223 #endif
224 	struct nand_bbt *bbt = &sc->sc_bbt;
225 	uint8_t byte, marker;
226 	bool result;
227 
228 	KASSERT(block < chip->nc_size / chip->nc_block_size);
229 
230 	/* get byte containing the 2 bit marker for this block */
231 	byte = bbt->nbbt_bitmap[block / 4];
232 
233 	/* extract the 2 bit marker from the byte */
234 	marker = (byte >> ((block % 4) * 2)) & 0x03;
235 
236 	switch (marker) {
237 	case NAND_BBT_MARKER_FACTORY_BAD:
238 	case NAND_BBT_MARKER_WORNOUT_BAD:
239 	case NAND_BBT_MARKER_RESERVED:
240 		result = true;
241 		break;
242 	case NAND_BBT_MARKER_GOOD:
243 		result = false;
244 		break;
245 	default:
246 		panic("error in marker extraction");
247 	}
248 
249 	return result;
250 }
251