2f2949187a0093e1834ccf56bc03ed1aef1526af
[yaffs2.git] / direct / u-boot / fs / yaffs2 / yaffs_mtdif2.c
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 /* mtd interface for YAFFS2 */
15
16 /* XXX U-BOOT XXX */
17 #include <common.h>
18 #include "asm/errno.h"
19
20 #include "yportenv.h"
21 #include "yaffs_trace.h"
22
23 #include "yaffs_mtdif2.h"
24
25 #include "linux/mtd/mtd.h"
26 #include "linux/types.h"
27 #include "linux/time.h"
28
29 #include "yaffs_trace.h"
30
31 #include "yaffs_packedtags2.h"
32 #include "string.h"
33
34
35 int nandmtd2_WriteChunkWithTagsToNAND(struct yaffs_dev* dev, int chunkInNAND,
36                                       const u8 * data,
37                                       const struct yaffs_ext_tags * tags)
38 {
39         struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
40 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
41         struct mtd_oob_ops ops;
42 #else
43         size_t dummy;
44 #endif
45         int retval = 0;
46
47         loff_t addr = ((loff_t) chunkInNAND) * dev->data_bytes_per_chunk;
48
49         struct yaffs_packed_tags2 pt;
50
51         yaffs_trace(YAFFS_TRACE_MTD,
52                 "nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p",
53                 chunkInNAND, data, tags);
54
55 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
56         if (tags)
57                 yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
58         else
59                 BUG(); /* both tags and data should always be present */
60
61         if (data) {
62                 ops.mode = MTD_OOB_AUTO;
63                 ops.ooblen = sizeof(pt);
64                 ops.len = dev->data_bytes_per_chunk;
65                 ops.ooboffs = 0;
66                 ops.datbuf = (u8 *)data;
67                 ops.oobbuf = (void *)&pt;
68                 retval = mtd->write_oob(mtd, addr, &ops);
69         } else
70                 BUG(); /* both tags and data should always be present */
71 #else
72         if (tags) {
73                 yaffs_pack_tags2(&pt, tags);
74         }
75
76         if (data && tags) {
77                 if (dev->param.use_nand_ecc)
78                         retval =
79                             mtd->write_ecc(mtd, addr, dev->data_bytes_per_chunk,
80                                            &dummy, data, (u8 *) & pt, NULL);
81                 else
82                         retval =
83                             mtd->write_ecc(mtd, addr, dev->data_bytes_per_chunk,
84                                            &dummy, data, (u8 *) & pt, NULL);
85         } else {
86                 if (data)
87                         retval =
88                             mtd->write(mtd, addr, dev->data_bytes_per_chunk, &dummy,
89                                        data);
90                 if (tags)
91                         retval =
92                             mtd->write_oob(mtd, addr, mtd->oobsize, &dummy,
93                                            (u8 *) & pt);
94
95         }
96 #endif
97
98         if (retval == 0)
99                 return YAFFS_OK;
100         else
101                 return YAFFS_FAIL;
102 }
103
104 int nandmtd2_ReadChunkWithTagsFromNAND(struct yaffs_dev * dev, int chunkInNAND,
105                                        u8 * data, struct yaffs_ext_tags * tags)
106 {
107         static u8 *spare_buffer = NULL;
108
109         struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
110 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
111         struct mtd_oob_ops ops;
112 #endif
113         size_t dummy;
114         int retval = 0;
115
116         loff_t addr = ((loff_t) chunkInNAND) * dev->data_bytes_per_chunk;
117
118         struct yaffs_packed_tags2 pt;
119         
120         if(!spare_buffer)
121                 spare_buffer = kmalloc(mtd->oobsize, GFP_NOFS);
122
123         yaffs_trace(YAFFS_TRACE_MTD,
124                 "nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p",
125                 chunkInNAND, data, tags);
126
127 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
128         if (data && !tags)
129                 retval = mtd->read(mtd, addr, dev->data_bytes_per_chunk,
130                                 &dummy, data);
131         else if (tags) {
132                 ops.mode = MTD_OOB_AUTO;
133                 ops.ooblen = sizeof(pt);
134                 ops.len = data ? dev->data_bytes_per_chunk : sizeof(pt);
135                 ops.ooboffs = 0;
136                 ops.datbuf = data;
137                 ops.oobbuf = spare_buffer;
138                 retval = mtd->read_oob(mtd, addr, &ops);
139         }
140 #else
141         if (data && tags) {
142                 if (dev->useNANDECC) {
143                         retval =
144                             mtd->read_ecc(mtd, addr, dev->data_bytes_per_chunk,
145                                           &dummy, data, dev->spare_buffer,
146                                           NULL);
147                 } else {
148                         retval =
149                             mtd->read_ecc(mtd, addr, dev->data_bytes_per_chunk,
150                                           &dummy, data, dev->spare_buffer,
151                                           NULL);
152                 }
153         } else {
154                 if (data)
155                         retval =
156                             mtd->read(mtd, addr, dev->data_bytes_per_chunk, &dummy,
157                                       data);
158                 if (tags)
159                         retval =
160                             mtd->read_oob(mtd, addr, mtd->oobsize, &dummy,
161                                           dev->spare_buffer);
162         }
163 #endif
164
165         memcpy(&pt, spare_buffer, sizeof(pt));
166
167         if (tags)
168                 yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
169
170         if(tags && retval == -EBADMSG && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR)
171                 tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
172
173         if (retval == 0)
174                 return YAFFS_OK;
175         else
176                 return YAFFS_FAIL;
177 }
178
179 int nandmtd2_MarkNANDBlockBad(struct yaffs_dev *dev, int blockNo)
180 {
181         struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
182         int retval;
183
184         yaffs_trace(YAFFS_TRACE_MTD,
185                 "nandmtd2_MarkNANDBlockBad %d", blockNo);
186
187         retval =
188             mtd->block_markbad(mtd,
189                                blockNo * dev->param.chunks_per_block *
190                                dev->data_bytes_per_chunk);
191
192         if (retval == 0)
193                 return YAFFS_OK;
194         else
195                 return YAFFS_FAIL;
196
197 }
198
199 int nandmtd2_QueryNANDBlock(struct yaffs_dev *dev, int blockNo,
200                             enum yaffs_block_state * state, int *sequenceNumber)
201 {
202         struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
203         int retval;
204
205         yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_QueryNANDBlock %d", blockNo);
206         retval =
207             mtd->block_isbad(mtd,
208                              blockNo * dev->param.chunks_per_block *
209                              dev->data_bytes_per_chunk);
210
211         if (retval) {
212                 yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
213
214                 *state = YAFFS_BLOCK_STATE_DEAD;
215                 *sequenceNumber = 0;
216         } else {
217                 struct yaffs_ext_tags t;
218                 nandmtd2_ReadChunkWithTagsFromNAND(dev,
219                                                    blockNo *
220                                                    dev->param.chunks_per_block, NULL,
221                                                    &t);
222
223                 if (t.chunk_used) {
224                         *sequenceNumber = t.seq_number;
225                         *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
226                 } else {
227                         *sequenceNumber = 0;
228                         *state = YAFFS_BLOCK_STATE_EMPTY;
229                 }
230         }
231         yaffs_trace(YAFFS_TRACE_MTD, "block is bad seq %d state %d", *sequenceNumber, *state);
232
233         if (retval == 0)
234                 return YAFFS_OK;
235         else
236                 return YAFFS_FAIL;
237 }