blob: f21f15bfbd83678ac8400d55f37ff24ee70c3d32 [file] [log] [blame]
William Juulc051bbe2007-11-15 11:13:05 +01001/*
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
William Juule24ebad2007-11-15 12:23:57 +010016/* XXX U-BOOT XXX */
Simon Glassc06c1be2020-05-10 11:40:08 -060017#include <linux/bug.h>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090018#include <linux/errno.h>
William Juule24ebad2007-11-15 12:23:57 +010019
William Juulc051bbe2007-11-15 11:13:05 +010020#include "yportenv.h"
Charles Manning3796e1f2012-05-09 16:55:17 +000021#include "yaffs_trace.h"
William Juulc051bbe2007-11-15 11:13:05 +010022
23#include "yaffs_mtdif2.h"
24
Masahiro Yamadaadae2ec2016-09-21 11:28:53 +090025#include <linux/mtd/mtd.h>
26#include <linux/types.h>
27#include <linux/time.h>
William Juulc051bbe2007-11-15 11:13:05 +010028
Charles Manning3796e1f2012-05-09 16:55:17 +000029#include "yaffs_trace.h"
William Juulc051bbe2007-11-15 11:13:05 +010030#include "yaffs_packedtags2.h"
Charles Manning3796e1f2012-05-09 16:55:17 +000031
32#define yaffs_dev_to_mtd(dev) ((struct mtd_info *)((dev)->driver_context))
33#define yaffs_dev_to_lc(dev) ((struct yaffs_linux_context *)((dev)->os_context))
William Juulc051bbe2007-11-15 11:13:05 +010034
Charles Manning3796e1f2012-05-09 16:55:17 +000035/* NB For use with inband tags....
36 * We assume that the data buffer is of size total_bytes_per_chunk so
37 * that we can also use it to load the tags.
38 */
39int nandmtd2_write_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
40 const u8 *data,
41 const struct yaffs_ext_tags *tags)
William Juulc051bbe2007-11-15 11:13:05 +010042{
Charles Manning3796e1f2012-05-09 16:55:17 +000043 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
William Juulc051bbe2007-11-15 11:13:05 +010044 struct mtd_oob_ops ops;
Charles Manning3796e1f2012-05-09 16:55:17 +000045
William Juulc051bbe2007-11-15 11:13:05 +010046 int retval = 0;
Charles Manning3796e1f2012-05-09 16:55:17 +000047 loff_t addr;
William Juulc051bbe2007-11-15 11:13:05 +010048
Charles Manning3796e1f2012-05-09 16:55:17 +000049 struct yaffs_packed_tags2 pt;
William Juulc051bbe2007-11-15 11:13:05 +010050
Charles Manning3796e1f2012-05-09 16:55:17 +000051 int packed_tags_size =
52 dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
53 void *packed_tags_ptr =
54 dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
William Juulc051bbe2007-11-15 11:13:05 +010055
Charles Manning3796e1f2012-05-09 16:55:17 +000056 yaffs_trace(YAFFS_TRACE_MTD,
57 "nandmtd2_write_chunk_tags chunk %d data %p tags %p",
58 nand_chunk, data, tags);
William Juulc051bbe2007-11-15 11:13:05 +010059
Charles Manning3796e1f2012-05-09 16:55:17 +000060 addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
William Juulc051bbe2007-11-15 11:13:05 +010061
Charles Manning3796e1f2012-05-09 16:55:17 +000062 /* For yaffs2 writing there must be both data and tags.
63 * If we're using inband tags, then the tags are stuffed into
64 * the end of the data buffer.
65 */
66 if (!data || !tags)
67 BUG();
68 else if (dev->param.inband_tags) {
69 struct yaffs_packed_tags2_tags_only *pt2tp;
70 pt2tp =
71 (struct yaffs_packed_tags2_tags_only *)(data +
72 dev->
73 data_bytes_per_chunk);
74 yaffs_pack_tags2_tags_only(pt2tp, tags);
William Juulc051bbe2007-11-15 11:13:05 +010075 } else {
Charles Manning3796e1f2012-05-09 16:55:17 +000076 yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc);
William Juulc051bbe2007-11-15 11:13:05 +010077 }
Charles Manning3796e1f2012-05-09 16:55:17 +000078
Sergey Lapin3a38a552013-01-14 03:46:50 +000079 ops.mode = MTD_OPS_AUTO_OOB;
Charles Manning3796e1f2012-05-09 16:55:17 +000080 ops.ooblen = (dev->param.inband_tags) ? 0 : packed_tags_size;
81 ops.len = dev->param.total_bytes_per_chunk;
82 ops.ooboffs = 0;
83 ops.datbuf = (u8 *) data;
84 ops.oobbuf = (dev->param.inband_tags) ? NULL : packed_tags_ptr;
Sergey Lapin3a38a552013-01-14 03:46:50 +000085 retval = mtd_write_oob(mtd, addr, &ops);
William Juulc051bbe2007-11-15 11:13:05 +010086
87 if (retval == 0)
88 return YAFFS_OK;
89 else
90 return YAFFS_FAIL;
91}
92
Charles Manning3796e1f2012-05-09 16:55:17 +000093int nandmtd2_read_chunk_tags(struct yaffs_dev *dev, int nand_chunk,
94 u8 *data, struct yaffs_ext_tags *tags)
William Juulc051bbe2007-11-15 11:13:05 +010095{
Charles Manning3796e1f2012-05-09 16:55:17 +000096 struct mtd_info *mtd = yaffs_dev_to_mtd(dev);
97 u8 local_spare[128];
William Juulc051bbe2007-11-15 11:13:05 +010098 struct mtd_oob_ops ops;
William Juulc051bbe2007-11-15 11:13:05 +010099 size_t dummy;
100 int retval = 0;
Charles Manning3796e1f2012-05-09 16:55:17 +0000101 int local_data = 0;
102 struct yaffs_packed_tags2 pt;
103 loff_t addr = ((loff_t) nand_chunk) * dev->param.total_bytes_per_chunk;
104 int packed_tags_size =
105 dev->param.no_tags_ecc ? sizeof(pt.t) : sizeof(pt);
106 void *packed_tags_ptr =
107 dev->param.no_tags_ecc ? (void *)&pt.t : (void *)&pt;
William Juulc051bbe2007-11-15 11:13:05 +0100108
Charles Manning3796e1f2012-05-09 16:55:17 +0000109 yaffs_trace(YAFFS_TRACE_MTD,
110 "nandmtd2_read_chunk_tags chunk %d data %p tags %p",
111 nand_chunk, data, tags);
William Juulc051bbe2007-11-15 11:13:05 +0100112
Charles Manning3796e1f2012-05-09 16:55:17 +0000113 if (dev->param.inband_tags) {
William Juulc051bbe2007-11-15 11:13:05 +0100114
Charles Manning3796e1f2012-05-09 16:55:17 +0000115 if (!data) {
116 local_data = 1;
117 data = yaffs_get_temp_buffer(dev);
118 }
119
120 }
William Juulc051bbe2007-11-15 11:13:05 +0100121
Charles Manning3796e1f2012-05-09 16:55:17 +0000122 if (dev->param.inband_tags || (data && !tags))
Sergey Lapin3a38a552013-01-14 03:46:50 +0000123 retval = mtd_read(mtd, addr, dev->param.total_bytes_per_chunk,
Charles Manning3796e1f2012-05-09 16:55:17 +0000124 &dummy, data);
William Juulc051bbe2007-11-15 11:13:05 +0100125 else if (tags) {
Sergey Lapin3a38a552013-01-14 03:46:50 +0000126 ops.mode = MTD_OPS_AUTO_OOB;
Charles Manning3796e1f2012-05-09 16:55:17 +0000127 ops.ooblen = packed_tags_size;
128 ops.len = data ? dev->data_bytes_per_chunk : packed_tags_size;
William Juulc051bbe2007-11-15 11:13:05 +0100129 ops.ooboffs = 0;
130 ops.datbuf = data;
Charles Manning3796e1f2012-05-09 16:55:17 +0000131 ops.oobbuf = local_spare;
Sergey Lapin3a38a552013-01-14 03:46:50 +0000132 retval = mtd_read_oob(mtd, addr, &ops);
William Juulc051bbe2007-11-15 11:13:05 +0100133 }
Charles Manning3796e1f2012-05-09 16:55:17 +0000134
135 if (dev->param.inband_tags) {
136 if (tags) {
137 struct yaffs_packed_tags2_tags_only *pt2tp;
138 pt2tp =
139 (struct yaffs_packed_tags2_tags_only *)
140 &data[dev->data_bytes_per_chunk];
141 yaffs_unpack_tags2_tags_only(tags, pt2tp);
William Juulc051bbe2007-11-15 11:13:05 +0100142 }
143 } else {
Charles Manning3796e1f2012-05-09 16:55:17 +0000144 if (tags) {
145 memcpy(packed_tags_ptr,
146 local_spare,
147 packed_tags_size);
148 yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc);
149 }
William Juulc051bbe2007-11-15 11:13:05 +0100150 }
William Juulc051bbe2007-11-15 11:13:05 +0100151
Charles Manning3796e1f2012-05-09 16:55:17 +0000152 if (local_data)
153 yaffs_release_temp_buffer(dev, data);
William Juulc051bbe2007-11-15 11:13:05 +0100154
Charles Manning3796e1f2012-05-09 16:55:17 +0000155 if (tags && retval == -EBADMSG
156 && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
157 tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED;
158 dev->n_ecc_unfixed++;
159 }
160 if (tags && retval == -EUCLEAN
161 && tags->ecc_result == YAFFS_ECC_RESULT_NO_ERROR) {
162 tags->ecc_result = YAFFS_ECC_RESULT_FIXED;
163 dev->n_ecc_fixed++;
164 }
William Juulc051bbe2007-11-15 11:13:05 +0100165 if (retval == 0)
166 return YAFFS_OK;
167 else
168 return YAFFS_FAIL;
169}
170
Charles Manning3796e1f2012-05-09 16:55:17 +0000171int nandmtd2_MarkNANDBlockBad(struct yaffs_dev *dev, int blockNo)
William Juulc051bbe2007-11-15 11:13:05 +0100172{
Charles Manning3796e1f2012-05-09 16:55:17 +0000173 struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
William Juulc051bbe2007-11-15 11:13:05 +0100174 int retval;
Charles Manning3796e1f2012-05-09 16:55:17 +0000175
176 yaffs_trace(YAFFS_TRACE_MTD,
177 "nandmtd2_MarkNANDBlockBad %d", blockNo);
William Juulc051bbe2007-11-15 11:13:05 +0100178
179 retval =
Sergey Lapin3a38a552013-01-14 03:46:50 +0000180 mtd_block_markbad(mtd,
Charles Manning3796e1f2012-05-09 16:55:17 +0000181 blockNo * dev->param.chunks_per_block *
182 dev->data_bytes_per_chunk);
William Juulc051bbe2007-11-15 11:13:05 +0100183
184 if (retval == 0)
185 return YAFFS_OK;
186 else
187 return YAFFS_FAIL;
188
189}
190
Charles Manning3796e1f2012-05-09 16:55:17 +0000191int nandmtd2_QueryNANDBlock(struct yaffs_dev *dev, int blockNo,
192 enum yaffs_block_state *state, u32 *sequenceNumber)
William Juulc051bbe2007-11-15 11:13:05 +0100193{
Charles Manning3796e1f2012-05-09 16:55:17 +0000194 struct mtd_info *mtd = (struct mtd_info *)(dev->driver_context);
William Juulc051bbe2007-11-15 11:13:05 +0100195 int retval;
196
Charles Manning3796e1f2012-05-09 16:55:17 +0000197 yaffs_trace(YAFFS_TRACE_MTD, "nandmtd2_QueryNANDBlock %d", blockNo);
William Juulc051bbe2007-11-15 11:13:05 +0100198 retval =
Sergey Lapin3a38a552013-01-14 03:46:50 +0000199 mtd_block_isbad(mtd,
Charles Manning3796e1f2012-05-09 16:55:17 +0000200 blockNo * dev->param.chunks_per_block *
201 dev->data_bytes_per_chunk);
William Juulc051bbe2007-11-15 11:13:05 +0100202
203 if (retval) {
Charles Manning3796e1f2012-05-09 16:55:17 +0000204 yaffs_trace(YAFFS_TRACE_MTD, "block is bad");
William Juulc051bbe2007-11-15 11:13:05 +0100205
206 *state = YAFFS_BLOCK_STATE_DEAD;
207 *sequenceNumber = 0;
208 } else {
Charles Manning3796e1f2012-05-09 16:55:17 +0000209 struct yaffs_ext_tags t;
210 nandmtd2_read_chunk_tags(dev,
211 blockNo *
212 dev->param.chunks_per_block, NULL,
213 &t);
William Juulc051bbe2007-11-15 11:13:05 +0100214
Charles Manning3796e1f2012-05-09 16:55:17 +0000215 if (t.chunk_used) {
216 *sequenceNumber = t.seq_number;
217 *state = YAFFS_BLOCK_STATE_NEEDS_SCAN;
William Juulc051bbe2007-11-15 11:13:05 +0100218 } else {
219 *sequenceNumber = 0;
220 *state = YAFFS_BLOCK_STATE_EMPTY;
221 }
222 }
Charles Manning3796e1f2012-05-09 16:55:17 +0000223 yaffs_trace(YAFFS_TRACE_MTD, "block is bad seq %d state %d",
224 *sequenceNumber, *state);
William Juulc051bbe2007-11-15 11:13:05 +0100225
226 if (retval == 0)
227 return YAFFS_OK;
228 else
229 return YAFFS_FAIL;
230}