blob: df6928179d63d77d90b3ffbe2e5587bc3ab3c0a6 [file] [log] [blame]
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -07001/*
2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3 *
4 * Copyright (C) 2002-2010 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/* mtd interface for YAFFS2 */
15
16#include "yportenv.h"
17#include "yaffs_trace.h"
18
19#include "yaffs_mtdif2.h"
20
21#include "linux/mtd/mtd.h"
22#include "linux/types.h"
23#include "linux/time.h"
24
25#include "yaffs_packedtags2.h"
26
27#include "yaffs_linux.h"
28
29/* NB For use with inband tags....
30 * We assume that the data buffer is of size total_bytes_per_chunk so that we can also
31 * use it to load the tags.
32 */
33int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
34 const u8 * data,
35 const struct yaffs_ext_tags *tags)
36{
37 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
38 struct mtd_oob_ops ops;
39 int retval = 0;
40
41 loff_t addr;
42
43 struct yaffs_packed_tags2 pt;
44
45 int packed_tags_size =
46 dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
47 void *packed_tags_ptr =
48 dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
49
50 yaffs_trace(YAFFS_TRACE_MTD,
51 "nandmtd2_write_chunk_tags chunk %d data %p tags %p",
52 nand_chunk, data, tags);
53
54 addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
55
56 /* For yaffs2 writing there must be both data and tags.
57 * If we're using inband tags, then the tags are stuffed into
58 * the end of the data buffer.
59 */
60 if (!data || !tags)
61 BUG();
62 else if (dev->param.inband_tags) {
63 struct yaffs_packed_tags2_tags_only *pt2tp;
64 pt2tp =
65 (struct yaffs_packed_tags2_tags_only *)(data +
66 dev->
67 data_bytes_per_chunk);
68 yaffs_pack_tags2_tags_only(pt2tp, tags);
69 } else {
70 yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
71 }
72
Steve Mucklef132c6c2012-06-06 18:30:57 -070073 ops.mode = MTD_OPS_AUTO_OOB;
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -070074 ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
75 ops.len = dev->param.total_bytes_per_chunk;
76 ops.ooboffs = 0;
77 ops.datbuf = (u8 *) data;
78 ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
Steve Mucklef132c6c2012-06-06 18:30:57 -070079 retval = mtd_write_oob(mtd, addr, &ops);
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -070080
81 if (retval == 0)
82 return YAFFS_OK;
83 else
84 return YAFFS_FAIL;
85}
86
87int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
88 u8 * data, struct yaffs_ext_tags *tags)
89{
90 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
91 struct mtd_oob_ops ops;
92
93 size_t dummy;
94 int retval = 0;
95 int local_data = 0;
96
97 loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
98
99 struct yaffs_packed_tags2 pt;
100
101 int packed_tags_size =
102 dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
103 void *packed_tags_ptr =
104 dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
105
106 yaffs_trace(YAFFS_TRACE_MTD,
107 "nandmtd2_read_chunk_tags chunk %d data %p tags %p",
108 nand_chunk, data, tags);
109
110 if (dev->param.inband_tags) {
111
112 if (!data) {
113 local_data = 1;
114 data = yaffs_get_temp_buffer(dev, __LINE__);
115 }
116
117 }
118
119 if (dev->param.inband_tags || (data && !tags))
Steve Mucklef132c6c2012-06-06 18:30:57 -0700120 retval = mtd_read(mtd, addr, dev->param.total_bytes_per_chunk,
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -0700121 &dummy, data);
122 else if (tags) {
Steve Mucklef132c6c2012-06-06 18:30:57 -0700123 ops.mode = MTD_OPS_AUTO_OOB;
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -0700124 ops.ooblen = packed_tags_size;
125 ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
126 ops.ooboffs = 0;
127 ops.datbuf = data;
128 ops.oobbuf = yaffs_dev_to_lc(dev)->spare_buffer;
Steve Mucklef132c6c2012-06-06 18:30:57 -0700129 retval = mtd_read_oob(mtd, addr, &ops);
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -0700130 }
131
132 if (dev->param.inband_tags) {
133 if (tags) {
134 struct yaffs_packed_tags2_tags_only *pt2tp;
135 pt2tp =
136 (struct yaffs_packed_tags2_tags_only *)&data[dev->
137 data_bytes_per_chunk];
138 yaffs_unpack_tags2_tags_only(tags, pt2tp);
139 }
140 } else {
141 if (tags) {
142 memcpy(packed_tags_ptr,
143 yaffs_dev_to_lc(dev)->spare_buffer,
144 packed_tags_size);
145 yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
146 }
147 }
148
149 if (local_data)
150 yaffs_release_temp_buffer(dev, data, __LINE__);
151
152 if (tags && retval == -EBADMSG
153 && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
154 tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
155 dev->n_ecc_unfixed++;
156 }
157 if (tags && retval == -EUCLEAN
158 && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
159 tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
160 dev->n_ecc_fixed++;
161 }
162 if (retval == 0)
163 return YAFFS_OK;
164 else
165 return YAFFS_FAIL;
166}
167
168int nandmtd2_mark_block_bad(struct yaffs_dev *dev, int block_no)
169{
170 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
171 int retval;
172 yaffs_trace(YAFFS_TRACE_MTD,
173 "nandmtd2_mark_block_bad %d", block_no);
174
175 retval =
Steve Mucklef132c6c2012-06-06 18:30:57 -0700176 mtd_block_markbad(mtd,
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -0700177 block_no * dev->param.chunks_per_block *
178 dev->param.total_bytes_per_chunk);
179
180 if (retval == 0)
181 return YAFFS_OK;
182 else
183 return YAFFS_FAIL;
184
185}
186
187int nandmtd2_query_block(struct yaffs_dev *dev, int block_no,
188 enum yaffs_block_state *state, u32 * seq_number)
189{
190 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
191 int retval;
192
193 yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_query_block %d", block_no);
194 retval =
Steve Mucklef132c6c2012-06-06 18:30:57 -0700195 mtd_block_isbad(mtd,
Arve Hjønnevåg3de69a72010-05-18 20:35:30 -0700196 block_no * dev->param.chunks_per_block *
197 dev->param.total_bytes_per_chunk);
198
199 if (retval) {
200 yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
201
202 *state = YAFFS_BLOCK_STATE_DEAD;
203 *seq_number = 0;
204 } else {
205 struct yaffs_ext_tags t;
206 nandmtd2_read_chunk_tags(dev, block_no *
207 dev->param.chunks_per_block, NULL, &t);
208
209 if (t.chunk_used) {
210 *seq_number = t.seq_number;
211 *state = YAFFS_BLOCK_STATE_NEEDS_SCANNING;
212 } else {
213 *seq_number = 0;
214 *state = YAFFS_BLOCK_STATE_EMPTY;
215 }
216 }
217 yaffs_trace(YAFFS_TRACE_MTD,
218 "block is bad seq %d state %d", *seq_number, *state);
219
220 if (retval == 0)
221 return YAFFS_OK;
222 else
223 return YAFFS_FAIL;
224}
225