1/* 2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. 3 * 4 * Copyright (C) 2002-2011 Aleph One Ltd. 5 * for Toby Churchill Ltd and Brightstar Engineering 6 * 7 * Created by Charles Manning <charles@aleph1.co.uk> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14#include "yportenv.h" 15 16#include "yaffs_mtdif.h" 17 18#include "linux/mtd/mtd.h" 19#include "linux/types.h" 20#include "linux/time.h" 21#include "linux/mtd/nand.h" 22#include "linux/kernel.h" 23#include "linux/version.h" 24#include "linux/types.h" 25#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) 26#include "uapi/linux/major.h" 27#endif 28 29#include "yaffs_trace.h" 30#include "yaffs_guts.h" 31#include "yaffs_linux.h" 32 33#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) 34#define MTD_OPS_AUTO_OOB MTD_OOB_AUTO 35#endif 36 37 38#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) 39#define mtd_erase(m, ei) (m)->erase(m, ei) 40#define mtd_write_oob(m, addr, pops) (m)->write_oob(m, addr, pops) 41#define mtd_read_oob(m, addr, pops) (m)->read_oob(m, addr, pops) 42#define mtd_block_isbad(m, offs) (m)->block_isbad(m, offs) 43#define mtd_block_markbad(m, offs) (m)->block_markbad(m, offs) 44#endif 45 46 47 48int nandmtd_erase_block(struct yaffs_dev *dev, int block_no) 49{ 50 struct mtd_info *mtd = yaffs_dev_to_mtd(dev); 51 u32 addr = 52 ((loff_t) block_no) * dev->param.total_bytes_per_chunk * 53 dev->param.chunks_per_block; 54 struct erase_info ei; 55 int retval = 0; 56 57 ei.mtd = mtd; 58 ei.addr = addr; 59 ei.len = dev->param.total_bytes_per_chunk * dev->param.chunks_per_block; 60 ei.time = 1000; 61 ei.retries = 2; 62 ei.callback = NULL; 63 ei.priv = (u_long) dev; 64 65 retval = mtd_erase(mtd, &ei); 66 67 if (retval == 0) 68 return YAFFS_OK; 69 70 return YAFFS_FAIL; 71} 72 73 74static int yaffs_mtd_write(struct yaffs_dev *dev, int nand_chunk, 75 const u8 *data, int data_len, 76 const u8 *oob, int oob_len) 77{ 78 struct mtd_info *mtd = yaffs_dev_to_mtd(dev); 79 loff_t addr; 80 struct mtd_oob_ops ops; 81 int retval; 82 83 yaffs_trace(YAFFS_TRACE_MTD, 84 "yaffs_mtd_write(%p, %d, %p, %d, %p, %d)\n", 85 dev, nand_chunk, data, data_len, oob, oob_len); 86 87 if (!data || !data_len) { 88 data = NULL; 89 data_len = 0; 90 } 91 92 if (!oob || !oob_len) { 93 oob = NULL; 94 oob_len = 0; 95 } 96 97 addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; 98 memset(&ops, 0, sizeof(ops)); 99 ops.mode = MTD_OPS_AUTO_OOB; 100 ops.len = (data) ? data_len : 0; 101 ops.ooblen = oob_len; 102 ops.datbuf = (u8 *)data; 103 ops.oobbuf = (u8 *)oob; 104 105 retval = mtd_write_oob(mtd, addr, &ops); 106 if (retval) { 107 yaffs_trace(YAFFS_TRACE_MTD, 108 "write_oob failed, chunk %d, mtd error %d", 109 nand_chunk, retval); 110 } 111 return retval ? YAFFS_FAIL : YAFFS_OK; 112} 113 114static int yaffs_mtd_read(struct yaffs_dev *dev, int nand_chunk, 115 u8 *data, int data_len, 116 u8 *oob, int oob_len, 117 enum yaffs_ecc_result *ecc_result) 118{ 119 struct mtd_info *mtd = yaffs_dev_to_mtd(dev); 120 loff_t addr; 121 struct mtd_oob_ops ops; 122 int retval; 123 124 addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk; 125 memset(&ops, 0, sizeof(ops)); 126 ops.mode = MTD_OPS_AUTO_OOB; 127 ops.len = (data) ? data_len : 0; 128 ops.ooblen = oob_len; 129 ops.datbuf = data; 130 ops.oobbuf = oob; 131 132#if (MTD_VERSION_CODE < MTD_VERSION(2, 6, 20)) 133 /* In MTD 2.6.18 to 2.6.19 nand_base.c:nand_do_read_oob() has a bug; 134 * help it out with ops.len = ops.ooblen when ops.datbuf == NULL. 135 */ 136 ops.len = (ops.datbuf) ? ops.len : ops.ooblen; 137#endif 138 /* Read page and oob using MTD. 139 * Check status and determine ECC result. 140 */ 141 retval = mtd_read_oob(mtd, addr, &ops); 142 if (retval) 143 yaffs_trace(YAFFS_TRACE_MTD, 144 "read_oob failed, chunk %d, mtd error %d", 145 nand_chunk, retval); 146 147 switch (retval) { 148 case 0: 149 /* no error */ 150 if(ecc_result) 151 *ecc_result = YAFFS_ECC_RESULT_NO_ERROR; 152 break; 153 154 case -EUCLEAN: 155 /* MTD's ECC fixed the data */ 156 if(ecc_result) 157 *ecc_result = YAFFS_ECC_RESULT_FIXED; 158 dev->n_ecc_fixed++; 159 break; 160 161 case -EBADMSG: 162 default: 163 /* MTD's ECC could not fix the data */ 164 dev->n_ecc_unfixed++; 165 if(ecc_result) 166 *ecc_result = YAFFS_ECC_RESULT_UNFIXED; 167 return YAFFS_FAIL; 168 } 169 170 return YAFFS_OK; 171} 172 173static int yaffs_mtd_erase(struct yaffs_dev *dev, int block_no) 174{ 175 struct mtd_info *mtd = yaffs_dev_to_mtd(dev); 176 177 loff_t addr; 178 struct erase_info ei; 179 int retval = 0; 180 u32 block_size; 181 182 block_size = dev->param.total_bytes_per_chunk * 183 dev->param.chunks_per_block; 184 addr = ((loff_t) block_no) * block_size; 185 186 ei.mtd = mtd; 187 ei.addr = addr; 188 ei.len = block_size; 189 ei.time = 1000; 190 ei.retries = 2; 191 ei.callback = NULL; 192 ei.priv = (u_long) dev; 193 194 retval = mtd_erase(mtd, &ei); 195 196 if (retval == 0) 197 return YAFFS_OK; 198 199 return YAFFS_FAIL; 200} 201 202static int yaffs_mtd_mark_bad(struct yaffs_dev *dev, int block_no) 203{ 204 struct mtd_info *mtd = yaffs_dev_to_mtd(dev); 205 int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; 206 int retval; 207 208 yaffs_trace(YAFFS_TRACE_BAD_BLOCKS, "marking block %d bad", block_no); 209 210 retval = mtd_block_markbad(mtd, (loff_t) blocksize * block_no); 211 return (retval) ? YAFFS_FAIL : YAFFS_OK; 212} 213 214static int yaffs_mtd_check_bad(struct yaffs_dev *dev, int block_no) 215{ 216 struct mtd_info *mtd = yaffs_dev_to_mtd(dev); 217 int blocksize = dev->param.chunks_per_block * dev->param.total_bytes_per_chunk; 218 int retval; 219 220 yaffs_trace(YAFFS_TRACE_MTD, "checking block %d bad", block_no); 221 222 retval = mtd_block_isbad(mtd, (loff_t) blocksize * block_no); 223 return (retval) ? YAFFS_FAIL : YAFFS_OK; 224} 225 226static int yaffs_mtd_initialise(struct yaffs_dev *dev) 227{ 228 return YAFFS_OK; 229} 230 231static int yaffs_mtd_deinitialise(struct yaffs_dev *dev) 232{ 233 return YAFFS_OK; 234} 235 236 237void yaffs_mtd_drv_install(struct yaffs_dev *dev) 238{ 239 struct yaffs_driver *drv = &dev->drv; 240 241 drv->drv_write_chunk_fn = yaffs_mtd_write; 242 drv->drv_read_chunk_fn = yaffs_mtd_read; 243 drv->drv_erase_fn = yaffs_mtd_erase; 244 drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; 245 drv->drv_check_bad_fn = yaffs_mtd_check_bad; 246 drv->drv_initialise_fn = yaffs_mtd_initialise; 247 drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; 248} 249 250 251struct mtd_info * yaffs_get_mtd_device(dev_t sdev) 252{ 253 struct mtd_info *mtd; 254 255 mtd = yaffs_get_mtd_device(sdev); 256 257 /* Check it's an mtd device..... */ 258 if (MAJOR(sdev) != MTD_BLOCK_MAJOR) 259 return NULL; /* This isn't an mtd device */ 260 261 /* Check it's NAND */ 262 if (mtd->type != MTD_NANDFLASH) { 263 yaffs_trace(YAFFS_TRACE_ALWAYS, 264 "yaffs: MTD device is not NAND it's type %d", 265 mtd->type); 266 return NULL; 267 } 268 269 yaffs_trace(YAFFS_TRACE_OS, " %s %d", WRITE_SIZE_STR, WRITE_SIZE(mtd)); 270 yaffs_trace(YAFFS_TRACE_OS, " oobsize %d", mtd->oobsize); 271 yaffs_trace(YAFFS_TRACE_OS, " erasesize %d", mtd->erasesize); 272#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) 273 yaffs_trace(YAFFS_TRACE_OS, " size %u", mtd->size); 274#else 275 yaffs_trace(YAFFS_TRACE_OS, " size %lld", mtd->size); 276#endif 277 278 return mtd; 279} 280 281int yaffs_verify_mtd(struct mtd_info *mtd, int yaffs_version, int inband_tags) 282{ 283 if (yaffs_version == 2) { 284 if ((WRITE_SIZE(mtd) < YAFFS_MIN_YAFFS2_CHUNK_SIZE || 285 mtd->oobsize < YAFFS_MIN_YAFFS2_SPARE_SIZE) && 286 !inband_tags) { 287 yaffs_trace(YAFFS_TRACE_ALWAYS, 288 "MTD device does not have the right page sizes" 289 ); 290 return -1; 291 } 292 } else { 293 if (WRITE_SIZE(mtd) < YAFFS_BYTES_PER_CHUNK || 294 mtd->oobsize != YAFFS_BYTES_PER_SPARE) { 295 yaffs_trace(YAFFS_TRACE_ALWAYS, 296 "MTD device does not support have the right page sizes" 297 ); 298 return -1; 299 } 300 } 301 302 return 0; 303} 304 305 306void yaffs_put_mtd_device(struct mtd_info *mtd) 307{ 308 if(mtd) 309 put_mtd_device(mtd); 310} 311