1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
2 * $Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $
4 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
5 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
9 /*======================================================================
11 A Flash Translation Layer memory card driver
13 This driver implements a disk-like block device driver with an
14 apparent block size of 512 bytes for flash memory cards.
16 ftl_cs.c 1.62 2000/02/01 00:59:04
18 The contents of this file are subject to the Mozilla Public
19 License Version 1.1 (the "License"); you may not use this file
20 except in compliance with the License. You may obtain a copy of
21 the License at http://www.mozilla.org/MPL/
23 Software distributed under the License is distributed on an "AS
24 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
25 implied. See the License for the specific language governing
26 rights and limitations under the License.
28 The initial developer of the original code is David A. Hinds
29 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
30 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
32 Alternatively, the contents of this file may be used under the
33 terms of the GNU General Public License version 2 (the "GPL"), in
34 which case the provisions of the GPL are applicable instead of the
35 above. If you wish to allow the use of your version of this file
36 only under the terms of the GPL and not to allow others to use
37 your version of this file under the MPL, indicate your decision
38 by deleting the provisions above and replace them with the notice
39 and other provisions required by the GPL. If you do not delete
40 the provisions above, a recipient may use your version of this
41 file under either the MPL or the GPL.
43 LEGAL NOTE: The FTL format is patented by M-Systems. They have
44 granted a license for its use with PCMCIA devices:
46 "M-Systems grants a royalty-free, non-exclusive license under
47 any presently existing M-Systems intellectual property rights
48 necessary for the design and development of FTL-compatible
49 drivers, file systems and utilities using the data formats with
50 PCMCIA PC Cards as described in the PCMCIA Flash Translation
51 Layer (FTL) Specification."
53 Use of the FTL format for non-PCMCIA applications may be an
54 infringement of these patents. For additional information,
55 contact M-Systems (http://www.m-sys.com) directly.
57 ======================================================================*/
58 #include <linux/mtd/blktrans.h>
59 #include <linux/module.h>
60 #include <linux/mtd/mtd.h>
61 /*#define PSYCHO_DEBUG */
63 #include <linux/kernel.h>
64 #include <linux/ptrace.h>
65 #include <linux/slab.h>
66 #include <linux/string.h>
67 #include <linux/timer.h>
68 #include <linux/major.h>
70 #include <linux/init.h>
71 #include <linux/hdreg.h>
72 #include <linux/vmalloc.h>
73 #include <linux/blkpg.h>
74 #include <asm/uaccess.h>
76 #include <linux/mtd/ftl.h>
78 /*====================================================================*/
80 /* Parameters that can be set with 'insmod' */
81 static int shuffle_freq
= 50;
82 module_param(shuffle_freq
, int, 0);
84 /*====================================================================*/
86 /* Major device # for FTL device */
92 /*====================================================================*/
94 /* Maximum number of separate memory devices we'll allow */
97 /* Maximum number of regions per device */
100 /* Maximum number of partitions in an FTL region */
103 /* Maximum number of outstanding erase requests per socket */
106 /* Sector size -- shouldn't need to change */
107 #define SECTOR_SIZE 512
110 /* Each memory region corresponds to a minor device */
111 typedef struct partition_t
{
112 struct mtd_blktrans_dev mbd
;
114 u_int32_t
*VirtualBlockMap
;
115 u_int32_t
*VirtualPageMap
;
119 u_int32_t EraseCount
;
125 u_int32_t EraseCount
;
129 u_int32_t
*bam_cache
;
131 u_int32_t BlocksPerUnit
;
132 erase_unit_header_t header
;
135 /* Partition state flags */
136 #define FTL_FORMATTED 0x01
138 /* Transfer unit states */
139 #define XFER_UNKNOWN 0x00
140 #define XFER_ERASING 0x01
141 #define XFER_ERASED 0x02
142 #define XFER_PREPARED 0x03
143 #define XFER_FAILED 0x04
145 /*====================================================================*/
148 static void ftl_erase_callback(struct erase_info
*done
);
151 /*======================================================================
153 Scan_header() checks to see if a memory region contains an FTL
154 partition. build_maps() reads all the erase unit headers, builds
155 the erase unit map, and then builds the virtual page map.
157 ======================================================================*/
159 static int scan_header(partition_t
*part
)
161 erase_unit_header_t header
;
162 loff_t offset
, max_offset
;
165 part
->header
.FormattedSize
= 0;
166 max_offset
= (0x100000<part
->mbd
.mtd
->size
)?0x100000:part
->mbd
.mtd
->size
;
167 /* Search first megabyte for a valid FTL header */
169 (offset
+ sizeof(header
)) < max_offset
;
170 offset
+= part
->mbd
.mtd
->erasesize
? : 0x2000) {
172 err
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(header
), &ret
,
173 (unsigned char *)&header
);
178 if (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0) break;
181 if (offset
== max_offset
) {
182 printk(KERN_NOTICE
"ftl_cs: FTL header not found.\n");
185 if (header
.BlockSize
!= 9 ||
186 (header
.EraseUnitSize
< 10) || (header
.EraseUnitSize
> 31) ||
187 (header
.NumTransferUnits
>= le16_to_cpu(header
.NumEraseUnits
))) {
188 printk(KERN_NOTICE
"ftl_cs: FTL header corrupt!\n");
191 if ((1 << header
.EraseUnitSize
) != part
->mbd
.mtd
->erasesize
) {
192 printk(KERN_NOTICE
"ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
193 1 << header
.EraseUnitSize
,part
->mbd
.mtd
->erasesize
);
196 part
->header
= header
;
200 static int build_maps(partition_t
*part
)
202 erase_unit_header_t header
;
203 u_int16_t xvalid
, xtrans
, i
;
205 int hdr_ok
, ret
= -1;
209 /* Set up erase unit maps */
210 part
->DataUnits
= le16_to_cpu(part
->header
.NumEraseUnits
) -
211 part
->header
.NumTransferUnits
;
212 part
->EUNInfo
= kmalloc(part
->DataUnits
* sizeof(struct eun_info_t
),
216 for (i
= 0; i
< part
->DataUnits
; i
++)
217 part
->EUNInfo
[i
].Offset
= 0xffffffff;
219 kmalloc(part
->header
.NumTransferUnits
* sizeof(struct xfer_info_t
),
225 for (i
= 0; i
< le16_to_cpu(part
->header
.NumEraseUnits
); i
++) {
226 offset
= ((i
+ le16_to_cpu(part
->header
.FirstPhysicalEUN
))
227 << part
->header
.EraseUnitSize
);
228 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(header
), &retval
,
229 (unsigned char *)&header
);
235 /* Is this a transfer partition? */
236 hdr_ok
= (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0);
237 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) < part
->DataUnits
) &&
238 (part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
== 0xffffffff)) {
239 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
= offset
;
240 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].EraseCount
=
241 le32_to_cpu(header
.EraseCount
);
244 if (xtrans
== part
->header
.NumTransferUnits
) {
245 printk(KERN_NOTICE
"ftl_cs: format error: too many "
246 "transfer units!\n");
249 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) == 0xffff)) {
250 part
->XferInfo
[xtrans
].state
= XFER_PREPARED
;
251 part
->XferInfo
[xtrans
].EraseCount
= le32_to_cpu(header
.EraseCount
);
253 part
->XferInfo
[xtrans
].state
= XFER_UNKNOWN
;
254 /* Pick anything reasonable for the erase count */
255 part
->XferInfo
[xtrans
].EraseCount
=
256 le32_to_cpu(part
->header
.EraseCount
);
258 part
->XferInfo
[xtrans
].Offset
= offset
;
262 /* Check for format trouble */
263 header
= part
->header
;
264 if ((xtrans
!= header
.NumTransferUnits
) ||
265 (xvalid
+xtrans
!= le16_to_cpu(header
.NumEraseUnits
))) {
266 printk(KERN_NOTICE
"ftl_cs: format error: erase units "
271 /* Set up virtual page map */
272 blocks
= le32_to_cpu(header
.FormattedSize
) >> header
.BlockSize
;
273 part
->VirtualBlockMap
= vmalloc(blocks
* sizeof(u_int32_t
));
274 if (!part
->VirtualBlockMap
)
277 memset(part
->VirtualBlockMap
, 0xff, blocks
* sizeof(u_int32_t
));
278 part
->BlocksPerUnit
= (1 << header
.EraseUnitSize
) >> header
.BlockSize
;
280 part
->bam_cache
= kmalloc(part
->BlocksPerUnit
* sizeof(u_int32_t
),
282 if (!part
->bam_cache
)
283 goto out_VirtualBlockMap
;
285 part
->bam_index
= 0xffff;
288 for (i
= 0; i
< part
->DataUnits
; i
++) {
289 part
->EUNInfo
[i
].Free
= 0;
290 part
->EUNInfo
[i
].Deleted
= 0;
291 offset
= part
->EUNInfo
[i
].Offset
+ le32_to_cpu(header
.BAMOffset
);
293 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
,
294 part
->BlocksPerUnit
* sizeof(u_int32_t
), &retval
,
295 (unsigned char *)part
->bam_cache
);
300 for (j
= 0; j
< part
->BlocksPerUnit
; j
++) {
301 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[j
]))) {
302 part
->EUNInfo
[i
].Free
++;
304 } else if ((BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[j
])) == BLOCK_DATA
) &&
305 (BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
])) < blocks
))
306 part
->VirtualBlockMap
[BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
]))] =
307 (i
<< header
.EraseUnitSize
) + (j
<< header
.BlockSize
);
308 else if (BLOCK_DELETED(le32_to_cpu(part
->bam_cache
[j
])))
309 part
->EUNInfo
[i
].Deleted
++;
317 kfree(part
->bam_cache
);
319 vfree(part
->VirtualBlockMap
);
321 kfree(part
->XferInfo
);
323 kfree(part
->EUNInfo
);
328 /*======================================================================
330 Erase_xfer() schedules an asynchronous erase operation for a
333 ======================================================================*/
335 static int erase_xfer(partition_t
*part
,
339 struct xfer_info_t
*xfer
;
340 struct erase_info
*erase
;
342 xfer
= &part
->XferInfo
[xfernum
];
343 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer
->Offset
);
344 xfer
->state
= XFER_ERASING
;
346 /* Is there a free erase slot? Always in MTD. */
349 erase
=kmalloc(sizeof(struct erase_info
), GFP_KERNEL
);
353 erase
->mtd
= part
->mbd
.mtd
;
354 erase
->callback
= ftl_erase_callback
;
355 erase
->addr
= xfer
->Offset
;
356 erase
->len
= 1 << part
->header
.EraseUnitSize
;
357 erase
->priv
= (u_long
)part
;
359 ret
= part
->mbd
.mtd
->erase(part
->mbd
.mtd
, erase
);
369 /*======================================================================
371 Prepare_xfer() takes a freshly erased transfer unit and gives
372 it an appropriate header.
374 ======================================================================*/
376 static void ftl_erase_callback(struct erase_info
*erase
)
379 struct xfer_info_t
*xfer
;
382 /* Look up the transfer unit */
383 part
= (partition_t
*)(erase
->priv
);
385 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++)
386 if (part
->XferInfo
[i
].Offset
== erase
->addr
) break;
388 if (i
== part
->header
.NumTransferUnits
) {
389 printk(KERN_NOTICE
"ftl_cs: internal error: "
390 "erase lookup failed!\n");
394 xfer
= &part
->XferInfo
[i
];
395 if (erase
->state
== MTD_ERASE_DONE
)
396 xfer
->state
= XFER_ERASED
;
398 xfer
->state
= XFER_FAILED
;
399 printk(KERN_NOTICE
"ftl_cs: erase failed: state = %d\n",
405 } /* ftl_erase_callback */
407 static int prepare_xfer(partition_t
*part
, int i
)
409 erase_unit_header_t header
;
410 struct xfer_info_t
*xfer
;
416 xfer
= &part
->XferInfo
[i
];
417 xfer
->state
= XFER_FAILED
;
419 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer
->Offset
);
421 /* Write the transfer unit header */
422 header
= part
->header
;
423 header
.LogicalEUN
= cpu_to_le16(0xffff);
424 header
.EraseCount
= cpu_to_le32(xfer
->EraseCount
);
426 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
, sizeof(header
),
427 &retlen
, (u_char
*)&header
);
433 /* Write the BAM stub */
434 nbam
= (part
->BlocksPerUnit
* sizeof(u_int32_t
) +
435 le32_to_cpu(part
->header
.BAMOffset
) + SECTOR_SIZE
- 1) / SECTOR_SIZE
;
437 offset
= xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
438 ctl
= cpu_to_le32(BLOCK_CONTROL
);
440 for (i
= 0; i
< nbam
; i
++, offset
+= sizeof(u_int32_t
)) {
442 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
443 &retlen
, (u_char
*)&ctl
);
448 xfer
->state
= XFER_PREPARED
;
453 /*======================================================================
455 Copy_erase_unit() takes a full erase block and a transfer unit,
456 copies everything to the transfer unit, then swaps the block
459 All data blocks are copied to the corresponding blocks in the
460 target unit, so the virtual block map does not need to be
463 ======================================================================*/
465 static int copy_erase_unit(partition_t
*part
, u_int16_t srcunit
,
468 u_char buf
[SECTOR_SIZE
];
469 struct eun_info_t
*eun
;
470 struct xfer_info_t
*xfer
;
471 u_int32_t src
, dest
, free
, i
;
476 u_int16_t srcunitswap
= cpu_to_le16(srcunit
);
478 eun
= &part
->EUNInfo
[srcunit
];
479 xfer
= &part
->XferInfo
[xferunit
];
480 DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
481 eun
->Offset
, xfer
->Offset
);
484 /* Read current BAM */
485 if (part
->bam_index
!= srcunit
) {
487 offset
= eun
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
489 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
,
490 part
->BlocksPerUnit
* sizeof(u_int32_t
),
491 &retlen
, (u_char
*) (part
->bam_cache
));
493 /* mark the cache bad, in case we get an error later */
494 part
->bam_index
= 0xffff;
497 printk( KERN_WARNING
"ftl: Failed to read BAM cache in copy_erase_unit()!\n");
502 /* Write the LogicalEUN for the transfer unit */
503 xfer
->state
= XFER_UNKNOWN
;
504 offset
= xfer
->Offset
+ 20; /* Bad! */
505 unit
= cpu_to_le16(0x7fff);
507 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int16_t
),
508 &retlen
, (u_char
*) &unit
);
511 printk( KERN_WARNING
"ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
515 /* Copy all data blocks from source unit to transfer unit */
516 src
= eun
->Offset
; dest
= xfer
->Offset
;
520 for (i
= 0; i
< part
->BlocksPerUnit
; i
++) {
521 switch (BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[i
]))) {
523 /* This gets updated later */
526 case BLOCK_REPLACEMENT
:
527 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, src
, SECTOR_SIZE
,
528 &retlen
, (u_char
*) buf
);
530 printk(KERN_WARNING
"ftl: Error reading old xfer unit in copy_erase_unit\n");
535 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, dest
, SECTOR_SIZE
,
536 &retlen
, (u_char
*) buf
);
538 printk(KERN_WARNING
"ftl: Error writing new xfer unit in copy_erase_unit\n");
544 /* All other blocks must be free */
545 part
->bam_cache
[i
] = cpu_to_le32(0xffffffff);
553 /* Write the BAM to the transfer unit */
554 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
555 part
->BlocksPerUnit
* sizeof(int32_t), &retlen
,
556 (u_char
*)part
->bam_cache
);
558 printk( KERN_WARNING
"ftl: Error writing BAM in copy_erase_unit\n");
563 /* All clear? Then update the LogicalEUN again */
564 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
+ 20, sizeof(u_int16_t
),
565 &retlen
, (u_char
*)&srcunitswap
);
568 printk(KERN_WARNING
"ftl: Error writing new LogicalEUN in copy_erase_unit\n");
573 /* Update the maps and usage stats*/
574 i
= xfer
->EraseCount
;
575 xfer
->EraseCount
= eun
->EraseCount
;
578 xfer
->Offset
= eun
->Offset
;
580 part
->FreeTotal
-= eun
->Free
;
581 part
->FreeTotal
+= free
;
585 /* Now, the cache should be valid for the new block */
586 part
->bam_index
= srcunit
;
589 } /* copy_erase_unit */
591 /*======================================================================
593 reclaim_block() picks a full erase unit and a transfer unit and
594 then calls copy_erase_unit() to copy one to the other. Then, it
595 schedules an erase on the expired block.
597 What's a good way to decide which transfer unit and which erase
598 unit to use? Beats me. My way is to always pick the transfer
599 unit with the fewest erases, and usually pick the data unit with
600 the most deleted blocks. But with a small probability, pick the
601 oldest data unit instead. This means that we generally postpone
602 the next reclaimation as long as possible, but shuffle static
603 stuff around a bit for wear leveling.
605 ======================================================================*/
607 static int reclaim_block(partition_t
*part
)
609 u_int16_t i
, eun
, xfer
;
613 DEBUG(0, "ftl_cs: reclaiming space...\n");
614 DEBUG(3, "NumTransferUnits == %x\n", part
->header
.NumTransferUnits
);
615 /* Pick the least erased transfer unit */
616 best
= 0xffffffff; xfer
= 0xffff;
619 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++) {
621 if (part
->XferInfo
[i
].state
== XFER_UNKNOWN
) {
622 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i
);
626 if (part
->XferInfo
[i
].state
== XFER_ERASING
) {
627 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i
);
631 else if (part
->XferInfo
[i
].state
== XFER_ERASED
) {
632 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i
);
634 prepare_xfer(part
, i
);
636 if (part
->XferInfo
[i
].state
== XFER_PREPARED
) {
637 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i
);
639 if (part
->XferInfo
[i
].EraseCount
<= best
) {
640 best
= part
->XferInfo
[i
].EraseCount
;
645 DEBUG(3,"XferInfo[%d].state == %x\n",i
, part
->XferInfo
[i
].state
);
648 if (xfer
== 0xffff) {
650 DEBUG(1, "ftl_cs: waiting for transfer "
651 "unit to be prepared...\n");
652 if (part
->mbd
.mtd
->sync
)
653 part
->mbd
.mtd
->sync(part
->mbd
.mtd
);
657 printk(KERN_NOTICE
"ftl_cs: reclaim failed: no "
658 "suitable transfer units!\n");
660 DEBUG(1, "ftl_cs: reclaim failed: no "
661 "suitable transfer units!\n");
666 } while (xfer
== 0xffff);
669 if ((jiffies
% shuffle_freq
) == 0) {
670 DEBUG(1, "ftl_cs: recycling freshest block...\n");
672 for (i
= 0; i
< part
->DataUnits
; i
++)
673 if (part
->EUNInfo
[i
].EraseCount
<= best
) {
674 best
= part
->EUNInfo
[i
].EraseCount
;
679 for (i
= 0; i
< part
->DataUnits
; i
++)
680 if (part
->EUNInfo
[i
].Deleted
>= best
) {
681 best
= part
->EUNInfo
[i
].Deleted
;
687 printk(KERN_NOTICE
"ftl_cs: reclaim failed: "
688 "no free blocks!\n");
690 DEBUG(1,"ftl_cs: reclaim failed: "
691 "no free blocks!\n");
696 ret
= copy_erase_unit(part
, eun
, xfer
);
698 erase_xfer(part
, xfer
);
700 printk(KERN_NOTICE
"ftl_cs: copy_erase_unit failed!\n");
702 } /* reclaim_block */
704 /*======================================================================
706 Find_free() searches for a free block. If necessary, it updates
707 the BAM cache for the erase unit containing the free block. It
708 returns the block index -- the erase unit is just the currently
709 cached unit. If there are no free blocks, it returns 0 -- this
710 is never a valid data block because it contains the header.
712 ======================================================================*/
715 static void dump_lists(partition_t
*part
)
718 printk(KERN_DEBUG
"ftl_cs: Free total = %d\n", part
->FreeTotal
);
719 for (i
= 0; i
< part
->DataUnits
; i
++)
720 printk(KERN_DEBUG
"ftl_cs: unit %d: %d phys, %d free, "
722 part
->EUNInfo
[i
].Offset
>> part
->header
.EraseUnitSize
,
723 part
->EUNInfo
[i
].Free
, part
->EUNInfo
[i
].Deleted
);
727 static u_int32_t
find_free(partition_t
*part
)
734 /* Find an erase unit with some free space */
735 stop
= (part
->bam_index
== 0xffff) ? 0 : part
->bam_index
;
738 if (part
->EUNInfo
[eun
].Free
!= 0) break;
739 /* Wrap around at end of table */
740 if (++eun
== part
->DataUnits
) eun
= 0;
741 } while (eun
!= stop
);
743 if (part
->EUNInfo
[eun
].Free
== 0)
746 /* Is this unit's BAM cached? */
747 if (eun
!= part
->bam_index
) {
748 /* Invalidate cache */
749 part
->bam_index
= 0xffff;
751 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
,
752 part
->EUNInfo
[eun
].Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
753 part
->BlocksPerUnit
* sizeof(u_int32_t
),
754 &retlen
, (u_char
*) (part
->bam_cache
));
757 printk(KERN_WARNING
"ftl: Error reading BAM in find_free\n");
760 part
->bam_index
= eun
;
763 /* Find a free block */
764 for (blk
= 0; blk
< part
->BlocksPerUnit
; blk
++)
765 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[blk
]))) break;
766 if (blk
== part
->BlocksPerUnit
) {
772 printk(KERN_NOTICE
"ftl_cs: bad free list!\n");
775 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk
, eun
);
781 /*======================================================================
783 Read a series of sectors from an FTL partition.
785 ======================================================================*/
787 static int ftl_read(partition_t
*part
, caddr_t buffer
,
788 u_long sector
, u_long nblocks
)
790 u_int32_t log_addr
, bsize
;
793 size_t offset
, retlen
;
795 DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
796 part
, sector
, nblocks
);
797 if (!(part
->state
& FTL_FORMATTED
)) {
798 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
801 bsize
= 1 << part
->header
.EraseUnitSize
;
803 for (i
= 0; i
< nblocks
; i
++) {
804 if (((sector
+i
) * SECTOR_SIZE
) >= le32_to_cpu(part
->header
.FormattedSize
)) {
805 printk(KERN_NOTICE
"ftl_cs: bad read offset\n");
808 log_addr
= part
->VirtualBlockMap
[sector
+i
];
809 if (log_addr
== 0xffffffff)
810 memset(buffer
, 0, SECTOR_SIZE
);
812 offset
= (part
->EUNInfo
[log_addr
/ bsize
].Offset
813 + (log_addr
% bsize
));
814 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, SECTOR_SIZE
,
815 &retlen
, (u_char
*) buffer
);
818 printk(KERN_WARNING
"Error reading MTD device in ftl_read()\n");
822 buffer
+= SECTOR_SIZE
;
827 /*======================================================================
829 Write a series of sectors to an FTL partition
831 ======================================================================*/
833 static int set_bam_entry(partition_t
*part
, u_int32_t log_addr
,
836 u_int32_t bsize
, blk
, le_virt_addr
;
842 size_t retlen
, offset
;
844 DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
845 part
, log_addr
, virt_addr
);
846 bsize
= 1 << part
->header
.EraseUnitSize
;
847 eun
= log_addr
/ bsize
;
848 blk
= (log_addr
% bsize
) / SECTOR_SIZE
;
849 offset
= (part
->EUNInfo
[eun
].Offset
+ blk
* sizeof(u_int32_t
) +
850 le32_to_cpu(part
->header
.BAMOffset
));
853 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
854 &retlen
, (u_char
*)&old_addr
);
856 printk(KERN_WARNING
"ftl: Error reading old_addr in set_bam_entry: %d\n",ret
);
859 old_addr
= le32_to_cpu(old_addr
);
861 if (((virt_addr
== 0xfffffffe) && !BLOCK_FREE(old_addr
)) ||
862 ((virt_addr
== 0) && (BLOCK_TYPE(old_addr
) != BLOCK_DATA
)) ||
863 (!BLOCK_DELETED(virt_addr
) && (old_addr
!= 0xfffffffe))) {
866 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() inconsistency!\n");
867 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, old = 0x%x"
868 ", new = 0x%x\n", log_addr
, old_addr
, virt_addr
);
873 le_virt_addr
= cpu_to_le32(virt_addr
);
874 if (part
->bam_index
== eun
) {
876 if (le32_to_cpu(part
->bam_cache
[blk
]) != old_addr
) {
879 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() "
881 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, cache"
883 le32_to_cpu(part
->bam_cache
[blk
]), old_addr
);
888 part
->bam_cache
[blk
] = le_virt_addr
;
890 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
891 &retlen
, (u_char
*)&le_virt_addr
);
894 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() failed!\n");
895 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, new = 0x%x\n",
896 log_addr
, virt_addr
);
899 } /* set_bam_entry */
901 static int ftl_write(partition_t
*part
, caddr_t buffer
,
902 u_long sector
, u_long nblocks
)
904 u_int32_t bsize
, log_addr
, virt_addr
, old_addr
, blk
;
907 size_t retlen
, offset
;
909 DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
910 part
, sector
, nblocks
);
911 if (!(part
->state
& FTL_FORMATTED
)) {
912 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
915 /* See if we need to reclaim space, before we start */
916 while (part
->FreeTotal
< nblocks
) {
917 ret
= reclaim_block(part
);
922 bsize
= 1 << part
->header
.EraseUnitSize
;
924 virt_addr
= sector
* SECTOR_SIZE
| BLOCK_DATA
;
925 for (i
= 0; i
< nblocks
; i
++) {
926 if (virt_addr
>= le32_to_cpu(part
->header
.FormattedSize
)) {
927 printk(KERN_NOTICE
"ftl_cs: bad write offset\n");
931 /* Grab a free block */
932 blk
= find_free(part
);
936 printk(KERN_NOTICE
"ftl_cs: internal error: "
937 "no free blocks!\n");
941 /* Tag the BAM entry, and write the new block */
942 log_addr
= part
->bam_index
* bsize
+ blk
* SECTOR_SIZE
;
943 part
->EUNInfo
[part
->bam_index
].Free
--;
945 if (set_bam_entry(part
, log_addr
, 0xfffffffe))
947 part
->EUNInfo
[part
->bam_index
].Deleted
++;
948 offset
= (part
->EUNInfo
[part
->bam_index
].Offset
+
950 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
,
954 printk(KERN_NOTICE
"ftl_cs: block write failed!\n");
955 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, virt_addr"
956 " = 0x%x, Offset = 0x%zx\n", log_addr
, virt_addr
,
961 /* Only delete the old entry when the new entry is ready */
962 old_addr
= part
->VirtualBlockMap
[sector
+i
];
963 if (old_addr
!= 0xffffffff) {
964 part
->VirtualBlockMap
[sector
+i
] = 0xffffffff;
965 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
966 if (set_bam_entry(part
, old_addr
, 0))
970 /* Finally, set up the new pointers */
971 if (set_bam_entry(part
, log_addr
, virt_addr
))
973 part
->VirtualBlockMap
[sector
+i
] = log_addr
;
974 part
->EUNInfo
[part
->bam_index
].Deleted
--;
976 buffer
+= SECTOR_SIZE
;
977 virt_addr
+= SECTOR_SIZE
;
982 static int ftl_getgeo(struct mtd_blktrans_dev
*dev
, struct hd_geometry
*geo
)
984 partition_t
*part
= (void *)dev
;
987 /* Sort of arbitrary: round size down to 4KiB boundary */
988 sect
= le32_to_cpu(part
->header
.FormattedSize
)/SECTOR_SIZE
;
992 geo
->cylinders
= sect
>> 3;
997 static int ftl_readsect(struct mtd_blktrans_dev
*dev
,
998 unsigned long block
, char *buf
)
1000 return ftl_read((void *)dev
, buf
, block
, 1);
1003 static int ftl_writesect(struct mtd_blktrans_dev
*dev
,
1004 unsigned long block
, char *buf
)
1006 return ftl_write((void *)dev
, buf
, block
, 1);
1009 /*====================================================================*/
1011 static void ftl_freepart(partition_t
*part
)
1013 vfree(part
->VirtualBlockMap
);
1014 part
->VirtualBlockMap
= NULL
;
1015 kfree(part
->VirtualPageMap
);
1016 part
->VirtualPageMap
= NULL
;
1017 kfree(part
->EUNInfo
);
1018 part
->EUNInfo
= NULL
;
1019 kfree(part
->XferInfo
);
1020 part
->XferInfo
= NULL
;
1021 kfree(part
->bam_cache
);
1022 part
->bam_cache
= NULL
;
1023 } /* ftl_freepart */
1025 static void ftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
1027 partition_t
*partition
;
1029 partition
= kzalloc(sizeof(partition_t
), GFP_KERNEL
);
1032 printk(KERN_WARNING
"No memory to scan for FTL on %s\n",
1037 partition
->mbd
.mtd
= mtd
;
1039 if ((scan_header(partition
) == 0) &&
1040 (build_maps(partition
) == 0)) {
1042 partition
->state
= FTL_FORMATTED
;
1044 printk(KERN_INFO
"ftl_cs: opening %d KiB FTL partition\n",
1045 le32_to_cpu(partition
->header
.FormattedSize
) >> 10);
1047 partition
->mbd
.size
= le32_to_cpu(partition
->header
.FormattedSize
) >> 9;
1049 partition
->mbd
.tr
= tr
;
1050 partition
->mbd
.devnum
= -1;
1051 if (!add_mtd_blktrans_dev((void *)partition
))
1055 ftl_freepart(partition
);
1059 static void ftl_remove_dev(struct mtd_blktrans_dev
*dev
)
1061 del_mtd_blktrans_dev(dev
);
1062 ftl_freepart((partition_t
*)dev
);
1066 static struct mtd_blktrans_ops ftl_tr
= {
1069 .part_bits
= PART_BITS
,
1070 .blksize
= SECTOR_SIZE
,
1071 .readsect
= ftl_readsect
,
1072 .writesect
= ftl_writesect
,
1073 .getgeo
= ftl_getgeo
,
1074 .add_mtd
= ftl_add_mtd
,
1075 .remove_dev
= ftl_remove_dev
,
1076 .owner
= THIS_MODULE
,
1079 static int init_ftl(void)
1081 DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n");
1083 return register_mtd_blktrans(&ftl_tr
);
1086 static void __exit
cleanup_ftl(void)
1088 deregister_mtd_blktrans(&ftl_tr
);
1091 module_init(init_ftl
);
1092 module_exit(cleanup_ftl
);
1095 MODULE_LICENSE("Dual MPL/GPL");
1096 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1097 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");