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
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	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
219bool
220nand_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