1/* 2 * YAFFS: Yet another FFS. A NAND-flash specific file system. 3 * yaffs_mtdif.c NAND mtd wrapper functions. 4 * 5 * Copyright (C) 2002 Aleph One Ltd. 6 * for Toby Churchill Ltd and Brightstar Engineering 7 * 8 * Created by Charles Manning <charles@aleph1.co.uk> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 */ 15 16const char *yaffs_mtdif_c_version = 17 "$Id: yaffs_mtdif.c,v 1.15 2006/10/03 10:13:03 charles Exp $"; 18 19#include "yportenv.h" 20 21 22#include "yaffs_mtdif.h" 23 24#include "linux/mtd/mtd.h" 25#include "linux/types.h" 26#include "linux/time.h" 27#include "linux/mtd/nand.h" 28 29#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) 30static struct nand_oobinfo yaffs_oobinfo = { 31 .useecc = 1, 32 .eccbytes = 6, 33 .eccpos = {8, 9, 10, 13, 14, 15} 34}; 35 36static struct nand_oobinfo yaffs_noeccinfo = { 37 .useecc = 0, 38}; 39#endif 40 41#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 42static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob) 43{ 44 oob[0] = spare->tagByte0; 45 oob[1] = spare->tagByte1; 46 oob[2] = spare->tagByte2; 47 oob[3] = spare->tagByte3; 48 oob[4] = spare->tagByte4; 49 oob[5] = spare->tagByte5 & 0x3f; 50 oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80; 51 oob[5] |= spare->pageStatus == 0 ? 0: 0x40; 52 oob[6] = spare->tagByte6; 53 oob[7] = spare->tagByte7; 54} 55 56static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob) 57{ 58 struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare; 59 spare->tagByte0 = oob[0]; 60 spare->tagByte1 = oob[1]; 61 spare->tagByte2 = oob[2]; 62 spare->tagByte3 = oob[3]; 63 spare->tagByte4 = oob[4]; 64 spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f; 65 spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y'; 66 spare->pageStatus = oob[5] & 0x40 ? 0xff : 0; 67 spare->tagByte6 = oob[6]; 68 spare->tagByte7 = oob[7]; 69 70 nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */ 71} 72#endif 73 74int nandmtd_WriteChunkToNAND(yaffs_Device * dev, int chunkInNAND, 75 const __u8 * data, const yaffs_Spare * spare) 76{ 77 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 78#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 79 struct mtd_oob_ops ops; 80#endif 81 size_t dummy; 82 int retval = 0; 83 84 loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; 85#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 86 __u8 spareAsBytes[8]; /* OOB */ 87 88 if (data && !spare) 89 retval = mtd->write(mtd, addr, dev->nDataBytesPerChunk, 90 &dummy, data); 91 else if (spare) { 92 if (dev->useNANDECC) { 93 translate_spare2oob(spare, spareAsBytes); 94 ops.mode = MTD_OOB_AUTO; 95 ops.ooblen = 8; /* temp hack */ 96 } else { 97 ops.mode = MTD_OOB_RAW; 98 ops.ooblen = YAFFS_BYTES_PER_SPARE; 99 } 100 ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; 101 ops.datbuf = (u8 *)data; 102 ops.ooboffs = 0; 103 ops.oobbuf = spareAsBytes; 104 retval = mtd->write_oob(mtd, addr, &ops); 105 } 106#else 107 __u8 *spareAsBytes = (__u8 *) spare; 108 109 if (data && spare) { 110 if (dev->useNANDECC) 111 retval = 112 mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, 113 &dummy, data, spareAsBytes, 114 &yaffs_oobinfo); 115 else 116 retval = 117 mtd->write_ecc(mtd, addr, dev->nDataBytesPerChunk, 118 &dummy, data, spareAsBytes, 119 &yaffs_noeccinfo); 120 } else { 121 if (data) 122 retval = 123 mtd->write(mtd, addr, dev->nDataBytesPerChunk, &dummy, 124 data); 125 if (spare) 126 retval = 127 mtd->write_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, 128 &dummy, spareAsBytes); 129 } 130#endif 131 132 if (retval == 0) 133 return YAFFS_OK; 134 else 135 return YAFFS_FAIL; 136} 137 138int nandmtd_ReadChunkFromNAND(yaffs_Device * dev, int chunkInNAND, __u8 * data, 139 yaffs_Spare * spare) 140{ 141 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 142#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 143 struct mtd_oob_ops ops; 144#endif 145 size_t dummy; 146 int retval = 0; 147 148 loff_t addr = ((loff_t) chunkInNAND) * dev->nDataBytesPerChunk; 149#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 150 __u8 spareAsBytes[8]; /* OOB */ 151 152 if (data && !spare) 153 retval = mtd->read(mtd, addr, dev->nDataBytesPerChunk, 154 &dummy, data); 155 else if (spare) { 156 if (dev->useNANDECC) { 157 ops.mode = MTD_OOB_AUTO; 158 ops.ooblen = 8; /* temp hack */ 159 } else { 160 ops.mode = MTD_OOB_RAW; 161 ops.ooblen = YAFFS_BYTES_PER_SPARE; 162 } 163 ops.len = data ? dev->nDataBytesPerChunk : ops.ooblen; 164 ops.datbuf = data; 165 ops.ooboffs = 0; 166 ops.oobbuf = spareAsBytes; 167 retval = mtd->read_oob(mtd, addr, &ops); 168 if (dev->useNANDECC) 169 translate_oob2spare(spare, spareAsBytes); 170 } 171#else 172 __u8 *spareAsBytes = (__u8 *) spare; 173 174 if (data && spare) { 175 if (dev->useNANDECC) { 176 /* Careful, this call adds 2 ints */ 177 /* to the end of the spare data. Calling function */ 178 /* should allocate enough memory for spare, */ 179 /* i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)]. */ 180 retval = 181 mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, 182 &dummy, data, spareAsBytes, 183 &yaffs_oobinfo); 184 } else { 185 retval = 186 mtd->read_ecc(mtd, addr, dev->nDataBytesPerChunk, 187 &dummy, data, spareAsBytes, 188 &yaffs_noeccinfo); 189 } 190 } else { 191 if (data) 192 retval = 193 mtd->read(mtd, addr, dev->nDataBytesPerChunk, &dummy, 194 data); 195 if (spare) 196 retval = 197 mtd->read_oob(mtd, addr, YAFFS_BYTES_PER_SPARE, 198 &dummy, spareAsBytes); 199 } 200#endif 201 202 if (retval == 0) 203 return YAFFS_OK; 204 else 205 return YAFFS_FAIL; 206} 207 208int nandmtd_EraseBlockInNAND(yaffs_Device * dev, int blockNumber) 209{ 210 struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice); 211 __u32 addr = 212 ((loff_t) blockNumber) * dev->nDataBytesPerChunk 213 * dev->nChunksPerBlock; 214 struct erase_info ei; 215 int retval = 0; 216 217 ei.mtd = mtd; 218 ei.addr = addr; 219 ei.len = dev->nDataBytesPerChunk * dev->nChunksPerBlock; 220 ei.time = 1000; 221 ei.retries = 2; 222 ei.callback = NULL; 223 ei.priv = (u_long) dev; 224 225 /* Todo finish off the ei if required */ 226 227 sema_init(&dev->sem, 0); 228 229 retval = mtd->erase(mtd, &ei); 230 231 if (retval == 0) 232 return YAFFS_OK; 233 else 234 return YAFFS_FAIL; 235} 236 237int nandmtd_InitialiseNAND(yaffs_Device * dev) 238{ 239 return YAFFS_OK; 240} 241 242