1/*	$NetBSD: nand_bbt.c,v 1.3 2011/04/26 13:38:13 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/*
35 * Implementation of Bad Block Tables (BBTs).
36 */
37
38#include <sys/cdefs.h>
39__KERNEL_RCSID(0, "$NetBSD$");
40
41#include <sys/param.h>
42#include <sys/kmem.h>
43
44#include "nand.h"
45#include "nand_bbt.h"
46
47void
48nand_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
60void
61nand_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
69void
70nand_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
92bool
93nand_bbt_update(device_t self)
94{
95	return true;
96}
97
98static bool
99nand_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
115static bool
116nand_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
157bool
158nand_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
178void
179nand_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
189void
190nand_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
200void
201nand_bbt_block_mark(device_t self, flash_off_t block, uint8_t marker)
202{
203	struct nand_softc *sc = device_private(self);
204#ifdef DIAGNOSTIC
205	struct nand_chip *chip = &sc->sc_chip;
206#endif
207	struct nand_bbt *bbt = &sc->sc_bbt;
208	uint8_t clean;
209
210	KASSERT(block < chip->nc_size / chip->nc_block_size);
211
212	clean = (~0x03 << ((block % 4) * 2));
213	marker = (marker << ((block % 4) * 2));
214
215	/* set byte containing the 2 bit marker for this block */
216	bbt->nbbt_bitmap[block / 4] &= clean;
217	bbt->nbbt_bitmap[block / 4] |= marker;
218}
219
220bool
221nand_bbt_block_isbad(device_t self, flash_off_t block)
222{
223	struct nand_softc *sc = device_private(self);
224#ifdef DIAGNOSTIC
225	struct nand_chip *chip = &sc->sc_chip;
226#endif
227	struct nand_bbt *bbt = &sc->sc_bbt;
228	uint8_t byte, marker;
229	bool result;
230
231	KASSERT(block < chip->nc_size / chip->nc_block_size);
232
233	/* get byte containing the 2 bit marker for this block */
234	byte = bbt->nbbt_bitmap[block / 4];
235
236	/* extract the 2 bit marker from the byte */
237	marker = (byte >> ((block % 4) * 2)) & 0x03;
238
239	switch (marker) {
240	case NAND_BBT_MARKER_FACTORY_BAD:
241	case NAND_BBT_MARKER_WORNOUT_BAD:
242	case NAND_BBT_MARKER_RESERVED:
243		result = true;
244		break;
245	case NAND_BBT_MARKER_GOOD:
246		result = false;
247		break;
248	default:
249		panic("error in marker extraction");
250	}
251
252	return result;
253}
254