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