xref: /rk3399_rockchip-uboot/fs/yaffs2/yaffs_mtdif2.c (revision 1221ce459d04a428f8880f58581f671b736c3c27)
10e8cc8bdSWilliam Juul /*
20e8cc8bdSWilliam Juul  * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
30e8cc8bdSWilliam Juul  *
40e8cc8bdSWilliam Juul  * Copyright (C) 2002-2007 Aleph One Ltd.
50e8cc8bdSWilliam Juul  *   for Toby Churchill Ltd and Brightstar Engineering
60e8cc8bdSWilliam Juul  *
70e8cc8bdSWilliam Juul  * Created by Charles Manning <charles@aleph1.co.uk>
80e8cc8bdSWilliam Juul  *
90e8cc8bdSWilliam Juul  * This program is free software; you can redistribute it and/or modify
100e8cc8bdSWilliam Juul  * it under the terms of the GNU General Public License version 2 as
110e8cc8bdSWilliam Juul  * published by the Free Software Foundation.
120e8cc8bdSWilliam Juul  */
130e8cc8bdSWilliam Juul 
140e8cc8bdSWilliam Juul /* mtd interface for YAFFS2 */
150e8cc8bdSWilliam Juul 
1690ef117bSWilliam Juul /* XXX U-BOOT XXX */
1790ef117bSWilliam Juul #include <common.h>
18*1221ce45SMasahiro Yamada #include <linux/errno.h>
1990ef117bSWilliam Juul 
200e8cc8bdSWilliam Juul #include "yportenv.h"
21753ac610SCharles Manning #include "yaffs_trace.h"
220e8cc8bdSWilliam Juul 
230e8cc8bdSWilliam Juul #include "yaffs_mtdif2.h"
240e8cc8bdSWilliam Juul 
25b5bf5cb3SMasahiro Yamada #include <linux/mtd/mtd.h>
26b5bf5cb3SMasahiro Yamada #include <linux/types.h>
27b5bf5cb3SMasahiro Yamada #include <linux/time.h>
280e8cc8bdSWilliam Juul 
29753ac610SCharles Manning #include "yaffs_trace.h"
300e8cc8bdSWilliam Juul #include "yaffs_packedtags2.h"
310e8cc8bdSWilliam Juul 
32753ac610SCharles Manning #define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context))
33753ac610SCharles Manning #define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context))
34753ac610SCharles Manning 
35753ac610SCharles Manning 
36753ac610SCharles Manning /* NB For use with inband tags....
37753ac610SCharles Manning  * We assume that the data buffer is of size total_bytes_per_chunk so
38753ac610SCharles Manning  * that we can also use it to load the tags.
39753ac610SCharles Manning  */
nandmtd2_write_chunk_tags(struct yaffs_dev * dev,int nand_chunk,const u8 * data,const struct yaffs_ext_tags * tags)40753ac610SCharles Manning int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
41753ac610SCharles Manning 			      const u8 *data,
42753ac610SCharles Manning 			      const struct yaffs_ext_tags *tags)
430e8cc8bdSWilliam Juul {
44753ac610SCharles Manning 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
450e8cc8bdSWilliam Juul 	struct mtd_oob_ops ops;
46753ac610SCharles Manning 
470e8cc8bdSWilliam Juul 	int retval = 0;
48753ac610SCharles Manning 	loff_t addr;
490e8cc8bdSWilliam Juul 
50753ac610SCharles Manning 	struct yaffs_packed_tags2 pt;
510e8cc8bdSWilliam Juul 
52753ac610SCharles Manning 	int packed_tags_size =
53753ac610SCharles Manning 	    dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
54753ac610SCharles Manning 	void *packed_tags_ptr =
55753ac610SCharles Manning 	    dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
560e8cc8bdSWilliam Juul 
57753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD,
58753ac610SCharles Manning 		"nandmtd2_write_chunk_tags chunk %d data %p tags %p",
59753ac610SCharles Manning 		nand_chunk, data, tags);
600e8cc8bdSWilliam Juul 
61753ac610SCharles Manning 	addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
620e8cc8bdSWilliam Juul 
63753ac610SCharles Manning 	/* For yaffs2 writing there must be both data and tags.
64753ac610SCharles Manning 	 * If we're using inband tags, then the tags are stuffed into
65753ac610SCharles Manning 	 * the end of the data buffer.
66753ac610SCharles Manning 	 */
67753ac610SCharles Manning 	if (!data || !tags)
68753ac610SCharles Manning 		BUG();
69753ac610SCharles Manning 	else if (dev->param.inband_tags) {
70753ac610SCharles Manning 		struct yaffs_packed_tags2_tags_only *pt2tp;
71753ac610SCharles Manning 		pt2tp =
72753ac610SCharles Manning 		    (struct yaffs_packed_tags2_tags_only *)(data +
73753ac610SCharles Manning 							dev->
74753ac610SCharles Manning 							data_bytes_per_chunk);
75753ac610SCharles Manning 		yaffs_pack_tags2_tags_only(pt2tp, tags);
760e8cc8bdSWilliam Juul 	} else {
77753ac610SCharles Manning 		yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
780e8cc8bdSWilliam Juul 	}
79753ac610SCharles Manning 
80dfe64e2cSSergey Lapin 	ops.mode = MTD_OPS_AUTO_OOB;
81753ac610SCharles Manning 	ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
82753ac610SCharles Manning 	ops.len = dev->param.total_bytes_per_chunk;
83753ac610SCharles Manning 	ops.ooboffs = 0;
84753ac610SCharles Manning 	ops.datbuf = (u8 *) data;
85753ac610SCharles Manning 	ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
86dfe64e2cSSergey Lapin 	retval = mtd_write_oob(mtd, addr, &ops);
870e8cc8bdSWilliam Juul 
880e8cc8bdSWilliam Juul 	if (retval == 0)
890e8cc8bdSWilliam Juul 		return YAFFS_OK;
900e8cc8bdSWilliam Juul 	else
910e8cc8bdSWilliam Juul 		return YAFFS_FAIL;
920e8cc8bdSWilliam Juul }
930e8cc8bdSWilliam Juul 
nandmtd2_read_chunk_tags(struct yaffs_dev * dev,int nand_chunk,u8 * data,struct yaffs_ext_tags * tags)94753ac610SCharles Manning int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
95753ac610SCharles Manning 			     u8 *data, struct yaffs_ext_tags *tags)
960e8cc8bdSWilliam Juul {
97753ac610SCharles Manning 	struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
98753ac610SCharles Manning 	u8 local_spare[128];
990e8cc8bdSWilliam Juul 	struct mtd_oob_ops ops;
1000e8cc8bdSWilliam Juul 	size_t dummy;
1010e8cc8bdSWilliam Juul 	int retval = 0;
102753ac610SCharles Manning 	int local_data = 0;
103753ac610SCharles Manning 	struct yaffs_packed_tags2 pt;
104753ac610SCharles Manning 	loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
105753ac610SCharles Manning 	int packed_tags_size =
106753ac610SCharles Manning 	    dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
107753ac610SCharles Manning 	void *packed_tags_ptr =
108753ac610SCharles Manning 	    dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
1090e8cc8bdSWilliam Juul 
110753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD,
111753ac610SCharles Manning 		"nandmtd2_read_chunk_tags chunk %d data %p tags %p",
112753ac610SCharles Manning 		nand_chunk, data, tags);
1130e8cc8bdSWilliam Juul 
114753ac610SCharles Manning 	if (dev->param.inband_tags) {
1150e8cc8bdSWilliam Juul 
116753ac610SCharles Manning 		if (!data) {
117753ac610SCharles Manning 			local_data = 1;
118753ac610SCharles Manning 			data = yaffs_get_temp_buffer(dev);
119753ac610SCharles Manning 		}
1200e8cc8bdSWilliam Juul 
121753ac610SCharles Manning 	}
122753ac610SCharles Manning 
123753ac610SCharles Manning 	if (dev->param.inband_tags || (data && !tags))
124dfe64e2cSSergey Lapin 		retval = mtd_read(mtd, addr, dev->param.total_bytes_per_chunk,
1250e8cc8bdSWilliam Juul 				   &dummy, data);
1260e8cc8bdSWilliam Juul 	else if (tags) {
127dfe64e2cSSergey Lapin 		ops.mode = MTD_OPS_AUTO_OOB;
128753ac610SCharles Manning 		ops.ooblen = packed_tags_size;
129753ac610SCharles Manning 		ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
1300e8cc8bdSWilliam Juul 		ops.ooboffs = 0;
1310e8cc8bdSWilliam Juul 		ops.datbuf = data;
132753ac610SCharles Manning 		ops.oobbuf = local_spare;
133dfe64e2cSSergey Lapin 		retval = mtd_read_oob(mtd, addr, &ops);
1340e8cc8bdSWilliam Juul 	}
135753ac610SCharles Manning 
136753ac610SCharles Manning 	if (dev->param.inband_tags) {
137753ac610SCharles Manning 		if (tags) {
138753ac610SCharles Manning 			struct yaffs_packed_tags2_tags_only *pt2tp;
139753ac610SCharles Manning 			pt2tp =
140753ac610SCharles Manning 				(struct yaffs_packed_tags2_tags_only *)
141753ac610SCharles Manning 				&data[dev->data_bytes_per_chunk];
142753ac610SCharles Manning 			yaffs_unpack_tags2_tags_only(tags, pt2tp);
1430e8cc8bdSWilliam Juul 		}
1440e8cc8bdSWilliam Juul 	} else {
145753ac610SCharles Manning 		if (tags) {
146753ac610SCharles Manning 			memcpy(packed_tags_ptr,
147753ac610SCharles Manning 			       local_spare,
148753ac610SCharles Manning 			       packed_tags_size);
149753ac610SCharles Manning 			yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
1500e8cc8bdSWilliam Juul 		}
151753ac610SCharles Manning 	}
1520e8cc8bdSWilliam Juul 
153753ac610SCharles Manning 	if (local_data)
154753ac610SCharles Manning 		yaffs_release_temp_buffer(dev, data);
1550e8cc8bdSWilliam Juul 
156753ac610SCharles Manning 	if (tags && retval == -EBADMSG
157753ac610SCharles Manning 	    && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
158753ac610SCharles Manning 		tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
159753ac610SCharles Manning 		dev->n_ecc_unfixed++;
160753ac610SCharles Manning 	}
161753ac610SCharles Manning 	if (tags && retval == -EUCLEAN
162753ac610SCharles Manning 	    && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
163753ac610SCharles Manning 		tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
164753ac610SCharles Manning 		dev->n_ecc_fixed++;
165753ac610SCharles Manning 	}
1660e8cc8bdSWilliam Juul 	if (retval == 0)
1670e8cc8bdSWilliam Juul 		return YAFFS_OK;
1680e8cc8bdSWilliam Juul 	else
1690e8cc8bdSWilliam Juul 		return YAFFS_FAIL;
1700e8cc8bdSWilliam Juul }
1710e8cc8bdSWilliam Juul 
172753ac610SCharles Manning 
nandmtd2_MarkNANDBlockBad(struct yaffs_dev * dev,int blockNo)173753ac610SCharles Manning int nandmtd2_MarkNANDBlockBad(struct yaffs_dev *dev, int blockNo)
1740e8cc8bdSWilliam Juul {
175753ac610SCharles Manning 	struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
1760e8cc8bdSWilliam Juul 	int retval;
177753ac610SCharles Manning 
178753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD,
179753ac610SCharles Manning 		"nandmtd2_MarkNANDBlockBad %d", blockNo);
1800e8cc8bdSWilliam Juul 
1810e8cc8bdSWilliam Juul 	retval =
182dfe64e2cSSergey Lapin 	    mtd_block_markbad(mtd,
183753ac610SCharles Manning 			       blockNo * dev->param.chunks_per_block *
184753ac610SCharles Manning 			       dev->data_bytes_per_chunk);
1850e8cc8bdSWilliam Juul 
1860e8cc8bdSWilliam Juul 	if (retval == 0)
1870e8cc8bdSWilliam Juul 		return YAFFS_OK;
1880e8cc8bdSWilliam Juul 	else
1890e8cc8bdSWilliam Juul 		return YAFFS_FAIL;
1900e8cc8bdSWilliam Juul 
1910e8cc8bdSWilliam Juul }
1920e8cc8bdSWilliam Juul 
nandmtd2_QueryNANDBlock(struct yaffs_dev * dev,int blockNo,enum yaffs_block_state * state,u32 * sequenceNumber)193753ac610SCharles Manning int nandmtd2_QueryNANDBlock(struct yaffs_dev *dev, int blockNo,
194753ac610SCharles Manning 			    enum yaffs_block_state *state, u32 *sequenceNumber)
1950e8cc8bdSWilliam Juul {
196753ac610SCharles Manning 	struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
1970e8cc8bdSWilliam Juul 	int retval;
1980e8cc8bdSWilliam Juul 
199753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_QueryNANDBlock %d", blockNo);
2000e8cc8bdSWilliam Juul 	retval =
201dfe64e2cSSergey Lapin 	    mtd_block_isbad(mtd,
202753ac610SCharles Manning 			     blockNo * dev->param.chunks_per_block *
203753ac610SCharles Manning 			     dev->data_bytes_per_chunk);
2040e8cc8bdSWilliam Juul 
2050e8cc8bdSWilliam Juul 	if (retval) {
206753ac610SCharles Manning 		yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
2070e8cc8bdSWilliam Juul 
2080e8cc8bdSWilliam Juul 		*state = YAFFS_BLOCK_STATE_DEAD;
2090e8cc8bdSWilliam Juul 		*sequenceNumber = 0;
2100e8cc8bdSWilliam Juul 	} else {
211753ac610SCharles Manning 		struct yaffs_ext_tags t;
212753ac610SCharles Manning 		nandmtd2_read_chunk_tags(dev,
2130e8cc8bdSWilliam Juul 					   blockNo *
214753ac610SCharles Manning 					   dev->param.chunks_per_block, NULL,
2150e8cc8bdSWilliam Juul 					   &t);
2160e8cc8bdSWilliam Juul 
217753ac610SCharles Manning 		if (t.chunk_used) {
218753ac610SCharles Manning 			*sequenceNumber = t.seq_number;
219753ac610SCharles Manning 			*state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
2200e8cc8bdSWilliam Juul 		} else {
2210e8cc8bdSWilliam Juul 			*sequenceNumber = 0;
2220e8cc8bdSWilliam Juul 			*state = YAFFS_BLOCK_STATE_EMPTY;
2230e8cc8bdSWilliam Juul 		}
2240e8cc8bdSWilliam Juul 	}
225753ac610SCharles Manning 	yaffs_trace(YAFFS_TRACE_MTD, "block is bad seq %d state %d",
226753ac610SCharles Manning 			*sequenceNumber, *state);
2270e8cc8bdSWilliam Juul 
2280e8cc8bdSWilliam Juul 	if (retval == 0)
2290e8cc8bdSWilliam Juul 		return YAFFS_OK;
2300e8cc8bdSWilliam Juul 	else
2310e8cc8bdSWilliam Juul 		return YAFFS_FAIL;
2320e8cc8bdSWilliam Juul }
233