1 /* 2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. 3 * 4 * Copyright (C) 2002-2007 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 const char *yaffs_mtdif_c_version = 15 "$Id: yaffs_mtdif.c,v 1.19 2007/02/14 01:09:06 wookey Exp $"; 16 17 #include "yportenv.h" 18 19 20 #include "yaffs_mtdif.h" 21 22 #include "linux/mtd/mtd.h" 23 #include "linux/types.h" 24 #include "linux/time.h" 25 #include "linux/mtd/nand.h" 26 27 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)) 28 static struct nand_oobinfo yaffs_oobinfo = { 29 .useecc = 1, 30 .eccbytes = 6, 31 .eccpos = {8, 9, 10, 13, 14, 15} 32 }; 33 34 static struct nand_oobinfo yaffs_noeccinfo = { 35 .useecc = 0, 36 }; 37 #endif 38 39 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17)) 40 static inline void translate_spare2oob(const yaffs_Spare *spare, __u8 *oob) 41 { 42 oob[0] = spare->tagByte0; 43 oob[1] = spare->tagByte1; 44 oob[2] = spare->tagByte2; 45 oob[3] = spare->tagByte3; 46 oob[4] = spare->tagByte4; 47 oob[5] = spare->tagByte5 & 0x3f; 48 oob[5] |= spare->blockStatus == 'Y' ? 0: 0x80; 49 oob[5] |= spare->pageStatus == 0 ? 0: 0x40; 50 oob[6] = spare->tagByte6; 51 oob[7] = spare->tagByte7; 52 } 53 54 static inline void translate_oob2spare(yaffs_Spare *spare, __u8 *oob) 55 { 56 struct yaffs_NANDSpare *nspare = (struct yaffs_NANDSpare *)spare; 57 spare->tagByte0 = oob[0]; 58 spare->tagByte1 = oob[1]; 59 spare->tagByte2 = oob[2]; 60 spare->tagByte3 = oob[3]; 61 spare->tagByte4 = oob[4]; 62 spare->tagByte5 = oob[5] == 0xff ? 0xff : oob[5] & 0x3f; 63 spare->blockStatus = oob[5] & 0x80 ? 0xff : 'Y'; 64 spare->pageStatus = oob[5] & 0x40 ? 0xff : 0; 65 spare->ecc1[0] = spare->ecc1[1] = spare->ecc1[2] = 0xff; 66 spare->tagByte6 = oob[6]; 67 spare->tagByte7 = oob[7]; 68 spare->ecc2[0] = spare->ecc2[1] = spare->ecc2[2] = 0xff; 69 70 nspare->eccres1 = nspare->eccres2 = 0; /* FIXME */ 71 } 72 #endif 73 74 int 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 138 int 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 208 int 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 237 int nandmtd_InitialiseNAND(yaffs_Device * dev) 238 { 239 return YAFFS_OK; 240 } 241 242