aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/mtdconcat.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtdconcat.c')
-rw-r--r--drivers/mtd/mtdconcat.c335
1 files changed, 188 insertions, 147 deletions
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 9af840364a74..1fea631b5852 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -19,6 +19,8 @@
19#include <linux/mtd/mtd.h> 19#include <linux/mtd/mtd.h>
20#include <linux/mtd/concat.h> 20#include <linux/mtd/concat.h>
21 21
22#include <asm/div64.h>
23
22/* 24/*
23 * Our storage structure: 25 * Our storage structure:
24 * Subdev points to an array of pointers to struct mtd_info objects 26 * Subdev points to an array of pointers to struct mtd_info objects
@@ -54,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
54 size_t * retlen, u_char * buf) 56 size_t * retlen, u_char * buf)
55{ 57{
56 struct mtd_concat *concat = CONCAT(mtd); 58 struct mtd_concat *concat = CONCAT(mtd);
57 int err = -EINVAL; 59 int ret = 0, err;
58 int i; 60 int i;
59 61
60 *retlen = 0; 62 *retlen = 0;
@@ -78,19 +80,29 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
78 80
79 err = subdev->read(subdev, from, size, &retsize, buf); 81 err = subdev->read(subdev, from, size, &retsize, buf);
80 82
81 if (err) 83 /* Save information about bitflips! */
82 break; 84 if (unlikely(err)) {
85 if (err == -EBADMSG) {
86 mtd->ecc_stats.failed++;
87 ret = err;
88 } else if (err == -EUCLEAN) {
89 mtd->ecc_stats.corrected++;
90 /* Do not overwrite -EBADMSG !! */
91 if (!ret)
92 ret = err;
93 } else
94 return err;
95 }
83 96
84 *retlen += retsize; 97 *retlen += retsize;
85 len -= size; 98 len -= size;
86 if (len == 0) 99 if (len == 0)
87 break; 100 return ret;
88 101
89 err = -EINVAL;
90 buf += size; 102 buf += size;
91 from = 0; 103 from = 0;
92 } 104 }
93 return err; 105 return -EINVAL;
94} 106}
95 107
96static int 108static int
@@ -141,211 +153,185 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
141} 153}
142 154
143static int 155static int
144concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, 156concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
145 size_t * retlen, u_char * buf, u_char * eccbuf, 157 unsigned long count, loff_t to, size_t * retlen)
146 struct nand_oobinfo *oobsel)
147{ 158{
148 struct mtd_concat *concat = CONCAT(mtd); 159 struct mtd_concat *concat = CONCAT(mtd);
149 int err = -EINVAL; 160 struct kvec *vecs_copy;
161 unsigned long entry_low, entry_high;
162 size_t total_len = 0;
150 int i; 163 int i;
164 int err = -EINVAL;
151 165
152 *retlen = 0; 166 if (!(mtd->flags & MTD_WRITEABLE))
153 167 return -EROFS;
154 for (i = 0; i < concat->num_subdev; i++) {
155 struct mtd_info *subdev = concat->subdev[i];
156 size_t size, retsize;
157
158 if (from >= subdev->size) {
159 /* Not destined for this subdev */
160 size = 0;
161 from -= subdev->size;
162 continue;
163 }
164
165 if (from + len > subdev->size)
166 /* First part goes into this subdev */
167 size = subdev->size - from;
168 else
169 /* Entire transaction goes into this subdev */
170 size = len;
171 168
172 if (subdev->read_ecc) 169 *retlen = 0;
173 err = subdev->read_ecc(subdev, from, size,
174 &retsize, buf, eccbuf, oobsel);
175 else
176 err = -EINVAL;
177 170
178 if (err) 171 /* Calculate total length of data */
179 break; 172 for (i = 0; i < count; i++)
173 total_len += vecs[i].iov_len;
180 174
181 *retlen += retsize; 175 /* Do not allow write past end of device */
182 len -= size; 176 if ((to + total_len) > mtd->size)
183 if (len == 0) 177 return -EINVAL;
184 break;
185 178
186 err = -EINVAL; 179 /* Check alignment */
187 buf += size; 180 if (mtd->writesize > 1) {
188 if (eccbuf) { 181 loff_t __to = to;
189 eccbuf += subdev->oobsize; 182 if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
190 /* in nand.c at least, eccbufs are 183 return -EINVAL;
191 tagged with 2 (int)eccstatus'; we
192 must account for these */
193 eccbuf += 2 * (sizeof (int));
194 }
195 from = 0;
196 } 184 }
197 return err;
198}
199 185
200static int 186 /* make a copy of vecs */
201concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, 187 vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
202 size_t * retlen, const u_char * buf, u_char * eccbuf, 188 if (!vecs_copy)
203 struct nand_oobinfo *oobsel) 189 return -ENOMEM;
204{ 190 memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);
205 struct mtd_concat *concat = CONCAT(mtd);
206 int err = -EINVAL;
207 int i;
208
209 if (!(mtd->flags & MTD_WRITEABLE))
210 return -EROFS;
211
212 *retlen = 0;
213 191
192 entry_low = 0;
214 for (i = 0; i < concat->num_subdev; i++) { 193 for (i = 0; i < concat->num_subdev; i++) {
215 struct mtd_info *subdev = concat->subdev[i]; 194 struct mtd_info *subdev = concat->subdev[i];
216 size_t size, retsize; 195 size_t size, wsize, retsize, old_iov_len;
217 196
218 if (to >= subdev->size) { 197 if (to >= subdev->size) {
219 size = 0;
220 to -= subdev->size; 198 to -= subdev->size;
221 continue; 199 continue;
222 } 200 }
223 if (to + len > subdev->size) 201
224 size = subdev->size - to; 202 size = min(total_len, (size_t)(subdev->size - to));
225 else 203 wsize = size; /* store for future use */
226 size = len; 204
205 entry_high = entry_low;
206 while (entry_high < count) {
207 if (size <= vecs_copy[entry_high].iov_len)
208 break;
209 size -= vecs_copy[entry_high++].iov_len;
210 }
211
212 old_iov_len = vecs_copy[entry_high].iov_len;
213 vecs_copy[entry_high].iov_len = size;
227 214
228 if (!(subdev->flags & MTD_WRITEABLE)) 215 if (!(subdev->flags & MTD_WRITEABLE))
229 err = -EROFS; 216 err = -EROFS;
230 else if (subdev->write_ecc)
231 err = subdev->write_ecc(subdev, to, size,
232 &retsize, buf, eccbuf, oobsel);
233 else 217 else
234 err = -EINVAL; 218 err = subdev->writev(subdev, &vecs_copy[entry_low],
219 entry_high - entry_low + 1, to, &retsize);
220
221 vecs_copy[entry_high].iov_len = old_iov_len - size;
222 vecs_copy[entry_high].iov_base += size;
223
224 entry_low = entry_high;
235 225
236 if (err) 226 if (err)
237 break; 227 break;
238 228
239 *retlen += retsize; 229 *retlen += retsize;
240 len -= size; 230 total_len -= wsize;
241 if (len == 0) 231
232 if (total_len == 0)
242 break; 233 break;
243 234
244 err = -EINVAL; 235 err = -EINVAL;
245 buf += size;
246 if (eccbuf)
247 eccbuf += subdev->oobsize;
248 to = 0; 236 to = 0;
249 } 237 }
238
239 kfree(vecs_copy);
250 return err; 240 return err;
251} 241}
252 242
253static int 243static int
254concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, 244concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
255 size_t * retlen, u_char * buf)
256{ 245{
257 struct mtd_concat *concat = CONCAT(mtd); 246 struct mtd_concat *concat = CONCAT(mtd);
258 int err = -EINVAL; 247 struct mtd_oob_ops devops = *ops;
259 int i; 248 int i, err, ret = 0;
260 249
261 *retlen = 0; 250 ops->retlen = 0;
262 251
263 for (i = 0; i < concat->num_subdev; i++) { 252 for (i = 0; i < concat->num_subdev; i++) {
264 struct mtd_info *subdev = concat->subdev[i]; 253 struct mtd_info *subdev = concat->subdev[i];
265 size_t size, retsize;
266 254
267 if (from >= subdev->size) { 255 if (from >= subdev->size) {
268 /* Not destined for this subdev */
269 size = 0;
270 from -= subdev->size; 256 from -= subdev->size;
271 continue; 257 continue;
272 } 258 }
273 if (from + len > subdev->size)
274 /* First part goes into this subdev */
275 size = subdev->size - from;
276 else
277 /* Entire transaction goes into this subdev */
278 size = len;
279 259
280 if (subdev->read_oob) 260 /* partial read ? */
281 err = subdev->read_oob(subdev, from, size, 261 if (from + devops.len > subdev->size)
282 &retsize, buf); 262 devops.len = subdev->size - from;
283 else 263
284 err = -EINVAL; 264 err = subdev->read_oob(subdev, from, &devops);
265 ops->retlen += devops.retlen;
266
267 /* Save information about bitflips! */
268 if (unlikely(err)) {
269 if (err == -EBADMSG) {
270 mtd->ecc_stats.failed++;
271 ret = err;
272 } else if (err == -EUCLEAN) {
273 mtd->ecc_stats.corrected++;
274 /* Do not overwrite -EBADMSG !! */
275 if (!ret)
276 ret = err;
277 } else
278 return err;
279 }
285 280
286 if (err) 281 devops.len = ops->len - ops->retlen;
287 break; 282 if (!devops.len)
283 return ret;
288 284
289 *retlen += retsize; 285 if (devops.datbuf)
290 len -= size; 286 devops.datbuf += devops.retlen;
291 if (len == 0) 287 if (devops.oobbuf)
292 break; 288 devops.oobbuf += devops.ooblen;
293 289
294 err = -EINVAL;
295 buf += size;
296 from = 0; 290 from = 0;
297 } 291 }
298 return err; 292 return -EINVAL;
299} 293}
300 294
301static int 295static int
302concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, 296concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
303 size_t * retlen, const u_char * buf)
304{ 297{
305 struct mtd_concat *concat = CONCAT(mtd); 298 struct mtd_concat *concat = CONCAT(mtd);
306 int err = -EINVAL; 299 struct mtd_oob_ops devops = *ops;
307 int i; 300 int i, err;
308 301
309 if (!(mtd->flags & MTD_WRITEABLE)) 302 if (!(mtd->flags & MTD_WRITEABLE))
310 return -EROFS; 303 return -EROFS;
311 304
312 *retlen = 0; 305 ops->retlen = 0;
313 306
314 for (i = 0; i < concat->num_subdev; i++) { 307 for (i = 0; i < concat->num_subdev; i++) {
315 struct mtd_info *subdev = concat->subdev[i]; 308 struct mtd_info *subdev = concat->subdev[i];
316 size_t size, retsize;
317 309
318 if (to >= subdev->size) { 310 if (to >= subdev->size) {
319 size = 0;
320 to -= subdev->size; 311 to -= subdev->size;
321 continue; 312 continue;
322 } 313 }
323 if (to + len > subdev->size)
324 size = subdev->size - to;
325 else
326 size = len;
327 314
328 if (!(subdev->flags & MTD_WRITEABLE)) 315 /* partial write ? */
329 err = -EROFS; 316 if (to + devops.len > subdev->size)
330 else if (subdev->write_oob) 317 devops.len = subdev->size - to;
331 err = subdev->write_oob(subdev, to, size, &retsize,
332 buf);
333 else
334 err = -EINVAL;
335 318
319 err = subdev->write_oob(subdev, to, &devops);
320 ops->retlen += devops.retlen;
336 if (err) 321 if (err)
337 break; 322 return err;
338 323
339 *retlen += retsize; 324 devops.len = ops->len - ops->retlen;
340 len -= size; 325 if (!devops.len)
341 if (len == 0) 326 return 0;
342 break;
343 327
344 err = -EINVAL; 328 if (devops.datbuf)
345 buf += size; 329 devops.datbuf += devops.retlen;
330 if (devops.oobbuf)
331 devops.oobbuf += devops.ooblen;
346 to = 0; 332 to = 0;
347 } 333 }
348 return err; 334 return -EINVAL;
349} 335}
350 336
351static void concat_erase_callback(struct erase_info *instr) 337static void concat_erase_callback(struct erase_info *instr)
@@ -636,6 +622,60 @@ static void concat_resume(struct mtd_info *mtd)
636 } 622 }
637} 623}
638 624
625static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
626{
627 struct mtd_concat *concat = CONCAT(mtd);
628 int i, res = 0;
629
630 if (!concat->subdev[0]->block_isbad)
631 return res;
632
633 if (ofs > mtd->size)
634 return -EINVAL;
635
636 for (i = 0; i < concat->num_subdev; i++) {
637 struct mtd_info *subdev = concat->subdev[i];
638
639 if (ofs >= subdev->size) {
640 ofs -= subdev->size;
641 continue;
642 }
643
644 res = subdev->block_isbad(subdev, ofs);
645 break;
646 }
647
648 return res;
649}
650
651static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
652{
653 struct mtd_concat *concat = CONCAT(mtd);
654 int i, err = -EINVAL;
655
656 if (!concat->subdev[0]->block_markbad)
657 return 0;
658
659 if (ofs > mtd->size)
660 return -EINVAL;
661
662 for (i = 0; i < concat->num_subdev; i++) {
663 struct mtd_info *subdev = concat->subdev[i];
664
665 if (ofs >= subdev->size) {
666 ofs -= subdev->size;
667 continue;
668 }
669
670 err = subdev->block_markbad(subdev, ofs);
671 if (!err)
672 mtd->ecc_stats.badblocks++;
673 break;
674 }
675
676 return err;
677}
678
639/* 679/*
640 * This function constructs a virtual MTD device by concatenating 680 * This function constructs a virtual MTD device by concatenating
641 * num_devs MTD devices. A pointer to the new device object is 681 * num_devs MTD devices. A pointer to the new device object is
@@ -677,18 +717,22 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
677 concat->mtd.flags = subdev[0]->flags; 717 concat->mtd.flags = subdev[0]->flags;
678 concat->mtd.size = subdev[0]->size; 718 concat->mtd.size = subdev[0]->size;
679 concat->mtd.erasesize = subdev[0]->erasesize; 719 concat->mtd.erasesize = subdev[0]->erasesize;
680 concat->mtd.oobblock = subdev[0]->oobblock; 720 concat->mtd.writesize = subdev[0]->writesize;
681 concat->mtd.oobsize = subdev[0]->oobsize; 721 concat->mtd.oobsize = subdev[0]->oobsize;
682 concat->mtd.ecctype = subdev[0]->ecctype; 722 concat->mtd.ecctype = subdev[0]->ecctype;
683 concat->mtd.eccsize = subdev[0]->eccsize; 723 concat->mtd.eccsize = subdev[0]->eccsize;
684 if (subdev[0]->read_ecc) 724 if (subdev[0]->writev)
685 concat->mtd.read_ecc = concat_read_ecc; 725 concat->mtd.writev = concat_writev;
686 if (subdev[0]->write_ecc)
687 concat->mtd.write_ecc = concat_write_ecc;
688 if (subdev[0]->read_oob) 726 if (subdev[0]->read_oob)
689 concat->mtd.read_oob = concat_read_oob; 727 concat->mtd.read_oob = concat_read_oob;
690 if (subdev[0]->write_oob) 728 if (subdev[0]->write_oob)
691 concat->mtd.write_oob = concat_write_oob; 729 concat->mtd.write_oob = concat_write_oob;
730 if (subdev[0]->block_isbad)
731 concat->mtd.block_isbad = concat_block_isbad;
732 if (subdev[0]->block_markbad)
733 concat->mtd.block_markbad = concat_block_markbad;
734
735 concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
692 736
693 concat->subdev[0] = subdev[0]; 737 concat->subdev[0] = subdev[0];
694 738
@@ -717,12 +761,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
717 subdev[i]->flags & MTD_WRITEABLE; 761 subdev[i]->flags & MTD_WRITEABLE;
718 } 762 }
719 concat->mtd.size += subdev[i]->size; 763 concat->mtd.size += subdev[i]->size;
720 if (concat->mtd.oobblock != subdev[i]->oobblock || 764 concat->mtd.ecc_stats.badblocks +=
765 subdev[i]->ecc_stats.badblocks;
766 if (concat->mtd.writesize != subdev[i]->writesize ||
721 concat->mtd.oobsize != subdev[i]->oobsize || 767 concat->mtd.oobsize != subdev[i]->oobsize ||
722 concat->mtd.ecctype != subdev[i]->ecctype || 768 concat->mtd.ecctype != subdev[i]->ecctype ||
723 concat->mtd.eccsize != subdev[i]->eccsize || 769 concat->mtd.eccsize != subdev[i]->eccsize ||
724 !concat->mtd.read_ecc != !subdev[i]->read_ecc ||
725 !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
726 !concat->mtd.read_oob != !subdev[i]->read_oob || 770 !concat->mtd.read_oob != !subdev[i]->read_oob ||
727 !concat->mtd.write_oob != !subdev[i]->write_oob) { 771 !concat->mtd.write_oob != !subdev[i]->write_oob) {
728 kfree(concat); 772 kfree(concat);
@@ -734,14 +778,11 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
734 778
735 } 779 }
736 780
781 concat->mtd.ecclayout = subdev[0]->ecclayout;
782
737 concat->num_subdev = num_devs; 783 concat->num_subdev = num_devs;
738 concat->mtd.name = name; 784 concat->mtd.name = name;
739 785
740 /*
741 * NOTE: for now, we do not provide any readv()/writev() methods
742 * because they are messy to implement and they are not
743 * used to a great extent anyway.
744 */
745 concat->mtd.erase = concat_erase; 786 concat->mtd.erase = concat_erase;
746 concat->mtd.read = concat_read; 787 concat->mtd.read = concat_read;
747 concat->mtd.write = concat_write; 788 concat->mtd.write = concat_write;