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