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