1 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
3 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
8 /*======================================================================
10 A Flash Translation Layer memory card driver
12 This driver implements a disk-like block device driver with an
13 apparent block size of 512 bytes for flash memory cards.
15 ftl_cs.c 1.62 2000/02/01 00:59:04
17 The contents of this file are subject to the Mozilla Public
18 License Version 1.1 (the "License"); you may not use this file
19 except in compliance with the License. You may obtain a copy of
20 the License at http://www.mozilla.org/MPL/
22 Software distributed under the License is distributed on an "AS
23 IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
24 implied. See the License for the specific language governing
25 rights and limitations under the License.
27 The initial developer of the original code is David A. Hinds
28 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
29 are Copyright © 1999 David A. Hinds. All Rights Reserved.
31 Alternatively, the contents of this file may be used under the
32 terms of the GNU General Public License version 2 (the "GPL"), in
33 which case the provisions of the GPL are applicable instead of the
34 above. If you wish to allow the use of your version of this file
35 only under the terms of the GPL and not to allow others to use
36 your version of this file under the MPL, indicate your decision
37 by deleting the provisions above and replace them with the notice
38 and other provisions required by the GPL. If you do not delete
39 the provisions above, a recipient may use your version of this
40 file under either the MPL or the GPL.
42 LEGAL NOTE: The FTL format is patented by M-Systems. They have
43 granted a license for its use with PCMCIA devices:
45 "M-Systems grants a royalty-free, non-exclusive license under
46 any presently existing M-Systems intellectual property rights
47 necessary for the design and development of FTL-compatible
48 drivers, file systems and utilities using the data formats with
49 PCMCIA PC Cards as described in the PCMCIA Flash Translation
50 Layer (FTL) Specification."
52 Use of the FTL format for non-PCMCIA applications may be an
53 infringement of these patents. For additional information,
54 contact M-Systems directly. M-Systems since acquired by Sandisk.
56 ======================================================================*/
57 #include <linux/mtd/blktrans.h>
58 #include <linux/module.h>
59 #include <linux/mtd/mtd.h>
60 /*#define PSYCHO_DEBUG */
62 #include <linux/kernel.h>
63 #include <linux/ptrace.h>
64 #include <linux/slab.h>
65 #include <linux/string.h>
66 #include <linux/timer.h>
67 #include <linux/major.h>
69 #include <linux/init.h>
70 #include <linux/hdreg.h>
71 #include <linux/vmalloc.h>
72 #include <linux/blkpg.h>
73 #include <asm/uaccess.h>
75 #include <linux/mtd/ftl.h>
77 /*====================================================================*/
79 /* Parameters that can be set with 'insmod' */
80 static int shuffle_freq
= 50;
81 module_param(shuffle_freq
, int, 0);
83 /*====================================================================*/
85 /* Major device # for FTL device */
91 /*====================================================================*/
93 /* Maximum number of separate memory devices we'll allow */
96 /* Maximum number of regions per device */
99 /* Maximum number of partitions in an FTL region */
102 /* Maximum number of outstanding erase requests per socket */
105 /* Sector size -- shouldn't need to change */
106 #define SECTOR_SIZE 512
109 /* Each memory region corresponds to a minor device */
110 typedef struct partition_t
{
111 struct mtd_blktrans_dev mbd
;
113 uint32_t *VirtualBlockMap
;
129 uint32_t BlocksPerUnit
;
130 erase_unit_header_t header
;
133 /* Partition state flags */
134 #define FTL_FORMATTED 0x01
136 /* Transfer unit states */
137 #define XFER_UNKNOWN 0x00
138 #define XFER_ERASING 0x01
139 #define XFER_ERASED 0x02
140 #define XFER_PREPARED 0x03
141 #define XFER_FAILED 0x04
143 /*====================================================================*/
146 static void ftl_erase_callback(struct erase_info
*done
);
149 /*======================================================================
151 Scan_header() checks to see if a memory region contains an FTL
152 partition. build_maps() reads all the erase unit headers, builds
153 the erase unit map, and then builds the virtual page map.
155 ======================================================================*/
157 static int scan_header(partition_t
*part
)
159 erase_unit_header_t header
;
160 loff_t offset
, max_offset
;
163 part
->header
.FormattedSize
= 0;
164 max_offset
= (0x100000<part
->mbd
.mtd
->size
)?0x100000:part
->mbd
.mtd
->size
;
165 /* Search first megabyte for a valid FTL header */
167 (offset
+ sizeof(header
)) < max_offset
;
168 offset
+= part
->mbd
.mtd
->erasesize
? : 0x2000) {
170 err
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(header
), &ret
,
171 (unsigned char *)&header
);
176 if (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0) break;
179 if (offset
== max_offset
) {
180 printk(KERN_NOTICE
"ftl_cs: FTL header not found.\n");
183 if (header
.BlockSize
!= 9 ||
184 (header
.EraseUnitSize
< 10) || (header
.EraseUnitSize
> 31) ||
185 (header
.NumTransferUnits
>= le16_to_cpu(header
.NumEraseUnits
))) {
186 printk(KERN_NOTICE
"ftl_cs: FTL header corrupt!\n");
189 if ((1 << header
.EraseUnitSize
) != part
->mbd
.mtd
->erasesize
) {
190 printk(KERN_NOTICE
"ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
191 1 << header
.EraseUnitSize
,part
->mbd
.mtd
->erasesize
);
194 part
->header
= header
;
198 static int build_maps(partition_t
*part
)
200 erase_unit_header_t header
;
201 uint16_t xvalid
, xtrans
, i
;
203 int hdr_ok
, ret
= -1;
207 /* Set up erase unit maps */
208 part
->DataUnits
= le16_to_cpu(part
->header
.NumEraseUnits
) -
209 part
->header
.NumTransferUnits
;
210 part
->EUNInfo
= kmalloc(part
->DataUnits
* sizeof(struct eun_info_t
),
214 for (i
= 0; i
< part
->DataUnits
; i
++)
215 part
->EUNInfo
[i
].Offset
= 0xffffffff;
217 kmalloc(part
->header
.NumTransferUnits
* sizeof(struct xfer_info_t
),
223 for (i
= 0; i
< le16_to_cpu(part
->header
.NumEraseUnits
); i
++) {
224 offset
= ((i
+ le16_to_cpu(part
->header
.FirstPhysicalEUN
))
225 << part
->header
.EraseUnitSize
);
226 ret
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(header
), &retval
,
227 (unsigned char *)&header
);
233 /* Is this a transfer partition? */
234 hdr_ok
= (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0);
235 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) < part
->DataUnits
) &&
236 (part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
== 0xffffffff)) {
237 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
= offset
;
238 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].EraseCount
=
239 le32_to_cpu(header
.EraseCount
);
242 if (xtrans
== part
->header
.NumTransferUnits
) {
243 printk(KERN_NOTICE
"ftl_cs: format error: too many "
244 "transfer units!\n");
247 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) == 0xffff)) {
248 part
->XferInfo
[xtrans
].state
= XFER_PREPARED
;
249 part
->XferInfo
[xtrans
].EraseCount
= le32_to_cpu(header
.EraseCount
);
251 part
->XferInfo
[xtrans
].state
= XFER_UNKNOWN
;
252 /* Pick anything reasonable for the erase count */
253 part
->XferInfo
[xtrans
].EraseCount
=
254 le32_to_cpu(part
->header
.EraseCount
);
256 part
->XferInfo
[xtrans
].Offset
= offset
;
260 /* Check for format trouble */
261 header
= part
->header
;
262 if ((xtrans
!= header
.NumTransferUnits
) ||
263 (xvalid
+xtrans
!= le16_to_cpu(header
.NumEraseUnits
))) {
264 printk(KERN_NOTICE
"ftl_cs: format error: erase units "
269 /* Set up virtual page map */
270 blocks
= le32_to_cpu(header
.FormattedSize
) >> header
.BlockSize
;
271 part
->VirtualBlockMap
= vmalloc(blocks
* sizeof(uint32_t));
272 if (!part
->VirtualBlockMap
)
275 memset(part
->VirtualBlockMap
, 0xff, blocks
* sizeof(uint32_t));
276 part
->BlocksPerUnit
= (1 << header
.EraseUnitSize
) >> header
.BlockSize
;
278 part
->bam_cache
= kmalloc(part
->BlocksPerUnit
* sizeof(uint32_t),
280 if (!part
->bam_cache
)
281 goto out_VirtualBlockMap
;
283 part
->bam_index
= 0xffff;
286 for (i
= 0; i
< part
->DataUnits
; i
++) {
287 part
->EUNInfo
[i
].Free
= 0;
288 part
->EUNInfo
[i
].Deleted
= 0;
289 offset
= part
->EUNInfo
[i
].Offset
+ le32_to_cpu(header
.BAMOffset
);
291 ret
= mtd_read(part
->mbd
.mtd
, offset
,
292 part
->BlocksPerUnit
* sizeof(uint32_t), &retval
,
293 (unsigned char *)part
->bam_cache
);
298 for (j
= 0; j
< part
->BlocksPerUnit
; j
++) {
299 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[j
]))) {
300 part
->EUNInfo
[i
].Free
++;
302 } else if ((BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[j
])) == BLOCK_DATA
) &&
303 (BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
])) < blocks
))
304 part
->VirtualBlockMap
[BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
]))] =
305 (i
<< header
.EraseUnitSize
) + (j
<< header
.BlockSize
);
306 else if (BLOCK_DELETED(le32_to_cpu(part
->bam_cache
[j
])))
307 part
->EUNInfo
[i
].Deleted
++;
315 kfree(part
->bam_cache
);
317 vfree(part
->VirtualBlockMap
);
319 kfree(part
->XferInfo
);
321 kfree(part
->EUNInfo
);
326 /*======================================================================
328 Erase_xfer() schedules an asynchronous erase operation for a
331 ======================================================================*/
333 static int erase_xfer(partition_t
*part
,
337 struct xfer_info_t
*xfer
;
338 struct erase_info
*erase
;
340 xfer
= &part
->XferInfo
[xfernum
];
341 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer
->Offset
);
342 xfer
->state
= XFER_ERASING
;
344 /* Is there a free erase slot? Always in MTD. */
347 erase
=kmalloc(sizeof(struct erase_info
), GFP_KERNEL
);
351 erase
->mtd
= part
->mbd
.mtd
;
352 erase
->callback
= ftl_erase_callback
;
353 erase
->addr
= xfer
->Offset
;
354 erase
->len
= 1 << part
->header
.EraseUnitSize
;
355 erase
->priv
= (u_long
)part
;
357 ret
= mtd_erase(part
->mbd
.mtd
, erase
);
367 /*======================================================================
369 Prepare_xfer() takes a freshly erased transfer unit and gives
370 it an appropriate header.
372 ======================================================================*/
374 static void ftl_erase_callback(struct erase_info
*erase
)
377 struct xfer_info_t
*xfer
;
380 /* Look up the transfer unit */
381 part
= (partition_t
*)(erase
->priv
);
383 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++)
384 if (part
->XferInfo
[i
].Offset
== erase
->addr
) break;
386 if (i
== part
->header
.NumTransferUnits
) {
387 printk(KERN_NOTICE
"ftl_cs: internal error: "
388 "erase lookup failed!\n");
392 xfer
= &part
->XferInfo
[i
];
393 if (erase
->state
== MTD_ERASE_DONE
)
394 xfer
->state
= XFER_ERASED
;
396 xfer
->state
= XFER_FAILED
;
397 printk(KERN_NOTICE
"ftl_cs: erase failed: state = %d\n",
403 } /* ftl_erase_callback */
405 static int prepare_xfer(partition_t
*part
, int i
)
407 erase_unit_header_t header
;
408 struct xfer_info_t
*xfer
;
414 xfer
= &part
->XferInfo
[i
];
415 xfer
->state
= XFER_FAILED
;
417 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer
->Offset
);
419 /* Write the transfer unit header */
420 header
= part
->header
;
421 header
.LogicalEUN
= cpu_to_le16(0xffff);
422 header
.EraseCount
= cpu_to_le32(xfer
->EraseCount
);
424 ret
= mtd_write(part
->mbd
.mtd
, xfer
->Offset
, sizeof(header
), &retlen
,
431 /* Write the BAM stub */
432 nbam
= (part
->BlocksPerUnit
* sizeof(uint32_t) +
433 le32_to_cpu(part
->header
.BAMOffset
) + SECTOR_SIZE
- 1) / SECTOR_SIZE
;
435 offset
= xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
436 ctl
= cpu_to_le32(BLOCK_CONTROL
);
438 for (i
= 0; i
< nbam
; i
++, offset
+= sizeof(uint32_t)) {
440 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
446 xfer
->state
= XFER_PREPARED
;
451 /*======================================================================
453 Copy_erase_unit() takes a full erase block and a transfer unit,
454 copies everything to the transfer unit, then swaps the block
457 All data blocks are copied to the corresponding blocks in the
458 target unit, so the virtual block map does not need to be
461 ======================================================================*/
463 static int copy_erase_unit(partition_t
*part
, uint16_t srcunit
,
466 u_char buf
[SECTOR_SIZE
];
467 struct eun_info_t
*eun
;
468 struct xfer_info_t
*xfer
;
469 uint32_t src
, dest
, free
, i
;
474 uint16_t srcunitswap
= cpu_to_le16(srcunit
);
476 eun
= &part
->EUNInfo
[srcunit
];
477 xfer
= &part
->XferInfo
[xferunit
];
478 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
479 eun
->Offset
, xfer
->Offset
);
482 /* Read current BAM */
483 if (part
->bam_index
!= srcunit
) {
485 offset
= eun
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
487 ret
= mtd_read(part
->mbd
.mtd
, offset
,
488 part
->BlocksPerUnit
* sizeof(uint32_t), &retlen
,
489 (u_char
*)(part
->bam_cache
));
491 /* mark the cache bad, in case we get an error later */
492 part
->bam_index
= 0xffff;
495 printk( KERN_WARNING
"ftl: Failed to read BAM cache in copy_erase_unit()!\n");
500 /* Write the LogicalEUN for the transfer unit */
501 xfer
->state
= XFER_UNKNOWN
;
502 offset
= xfer
->Offset
+ 20; /* Bad! */
503 unit
= cpu_to_le16(0x7fff);
505 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint16_t), &retlen
,
509 printk( KERN_WARNING
"ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
513 /* Copy all data blocks from source unit to transfer unit */
514 src
= eun
->Offset
; dest
= xfer
->Offset
;
518 for (i
= 0; i
< part
->BlocksPerUnit
; i
++) {
519 switch (BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[i
]))) {
521 /* This gets updated later */
524 case BLOCK_REPLACEMENT
:
525 ret
= mtd_read(part
->mbd
.mtd
, src
, SECTOR_SIZE
, &retlen
,
528 printk(KERN_WARNING
"ftl: Error reading old xfer unit in copy_erase_unit\n");
533 ret
= mtd_write(part
->mbd
.mtd
, dest
, SECTOR_SIZE
, &retlen
,
536 printk(KERN_WARNING
"ftl: Error writing new xfer unit in copy_erase_unit\n");
542 /* All other blocks must be free */
543 part
->bam_cache
[i
] = cpu_to_le32(0xffffffff);
551 /* Write the BAM to the transfer unit */
552 ret
= mtd_write(part
->mbd
.mtd
,
553 xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
554 part
->BlocksPerUnit
* sizeof(int32_t),
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
= mtd_write(part
->mbd
.mtd
, xfer
->Offset
+ 20, sizeof(uint16_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 reclamation 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 uint16_t i
, eun
, xfer
;
613 pr_debug("ftl_cs: reclaiming space...\n");
614 pr_debug("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 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i
);
626 if (part
->XferInfo
[i
].state
== XFER_ERASING
) {
627 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i
);
631 else if (part
->XferInfo
[i
].state
== XFER_ERASED
) {
632 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i
);
634 prepare_xfer(part
, i
);
636 if (part
->XferInfo
[i
].state
== XFER_PREPARED
) {
637 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i
);
639 if (part
->XferInfo
[i
].EraseCount
<= best
) {
640 best
= part
->XferInfo
[i
].EraseCount
;
645 pr_debug("XferInfo[%d].state == %x\n",i
, part
->XferInfo
[i
].state
);
648 if (xfer
== 0xffff) {
650 pr_debug("ftl_cs: waiting for transfer "
651 "unit to be prepared...\n");
652 mtd_sync(part
->mbd
.mtd
);
656 printk(KERN_NOTICE
"ftl_cs: reclaim failed: no "
657 "suitable transfer units!\n");
659 pr_debug("ftl_cs: reclaim failed: no "
660 "suitable transfer units!\n");
665 } while (xfer
== 0xffff);
668 if ((jiffies
% shuffle_freq
) == 0) {
669 pr_debug("ftl_cs: recycling freshest block...\n");
671 for (i
= 0; i
< part
->DataUnits
; i
++)
672 if (part
->EUNInfo
[i
].EraseCount
<= best
) {
673 best
= part
->EUNInfo
[i
].EraseCount
;
678 for (i
= 0; i
< part
->DataUnits
; i
++)
679 if (part
->EUNInfo
[i
].Deleted
>= best
) {
680 best
= part
->EUNInfo
[i
].Deleted
;
686 printk(KERN_NOTICE
"ftl_cs: reclaim failed: "
687 "no free blocks!\n");
689 pr_debug("ftl_cs: reclaim failed: "
690 "no free blocks!\n");
695 ret
= copy_erase_unit(part
, eun
, xfer
);
697 erase_xfer(part
, xfer
);
699 printk(KERN_NOTICE
"ftl_cs: copy_erase_unit failed!\n");
701 } /* reclaim_block */
703 /*======================================================================
705 Find_free() searches for a free block. If necessary, it updates
706 the BAM cache for the erase unit containing the free block. It
707 returns the block index -- the erase unit is just the currently
708 cached unit. If there are no free blocks, it returns 0 -- this
709 is never a valid data block because it contains the header.
711 ======================================================================*/
714 static void dump_lists(partition_t
*part
)
717 printk(KERN_DEBUG
"ftl_cs: Free total = %d\n", part
->FreeTotal
);
718 for (i
= 0; i
< part
->DataUnits
; i
++)
719 printk(KERN_DEBUG
"ftl_cs: unit %d: %d phys, %d free, "
721 part
->EUNInfo
[i
].Offset
>> part
->header
.EraseUnitSize
,
722 part
->EUNInfo
[i
].Free
, part
->EUNInfo
[i
].Deleted
);
726 static uint32_t find_free(partition_t
*part
)
733 /* Find an erase unit with some free space */
734 stop
= (part
->bam_index
== 0xffff) ? 0 : part
->bam_index
;
737 if (part
->EUNInfo
[eun
].Free
!= 0) break;
738 /* Wrap around at end of table */
739 if (++eun
== part
->DataUnits
) eun
= 0;
740 } while (eun
!= stop
);
742 if (part
->EUNInfo
[eun
].Free
== 0)
745 /* Is this unit's BAM cached? */
746 if (eun
!= part
->bam_index
) {
747 /* Invalidate cache */
748 part
->bam_index
= 0xffff;
750 ret
= mtd_read(part
->mbd
.mtd
,
751 part
->EUNInfo
[eun
].Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
752 part
->BlocksPerUnit
* sizeof(uint32_t),
754 (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 pr_debug("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 uint32_t log_addr
, bsize
;
793 size_t offset
, retlen
;
795 pr_debug("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
= mtd_read(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
,
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
, uint32_t log_addr
,
836 uint32_t bsize
, blk
, le_virt_addr
;
842 size_t retlen
, offset
;
844 pr_debug("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(uint32_t) +
850 le32_to_cpu(part
->header
.BAMOffset
));
853 ret
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
854 (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
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
891 (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 uint32_t bsize
, log_addr
, virt_addr
, old_addr
, blk
;
907 size_t retlen
, offset
;
909 pr_debug("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
= mtd_write(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
, buffer
);
953 printk(KERN_NOTICE
"ftl_cs: block write failed!\n");
954 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, virt_addr"
955 " = 0x%x, Offset = 0x%zx\n", log_addr
, virt_addr
,
960 /* Only delete the old entry when the new entry is ready */
961 old_addr
= part
->VirtualBlockMap
[sector
+i
];
962 if (old_addr
!= 0xffffffff) {
963 part
->VirtualBlockMap
[sector
+i
] = 0xffffffff;
964 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
965 if (set_bam_entry(part
, old_addr
, 0))
969 /* Finally, set up the new pointers */
970 if (set_bam_entry(part
, log_addr
, virt_addr
))
972 part
->VirtualBlockMap
[sector
+i
] = log_addr
;
973 part
->EUNInfo
[part
->bam_index
].Deleted
--;
975 buffer
+= SECTOR_SIZE
;
976 virt_addr
+= SECTOR_SIZE
;
981 static int ftl_getgeo(struct mtd_blktrans_dev
*dev
, struct hd_geometry
*geo
)
983 partition_t
*part
= (void *)dev
;
986 /* Sort of arbitrary: round size down to 4KiB boundary */
987 sect
= le32_to_cpu(part
->header
.FormattedSize
)/SECTOR_SIZE
;
991 geo
->cylinders
= sect
>> 3;
996 static int ftl_readsect(struct mtd_blktrans_dev
*dev
,
997 unsigned long block
, char *buf
)
999 return ftl_read((void *)dev
, buf
, block
, 1);
1002 static int ftl_writesect(struct mtd_blktrans_dev
*dev
,
1003 unsigned long block
, char *buf
)
1005 return ftl_write((void *)dev
, buf
, block
, 1);
1008 static int ftl_discardsect(struct mtd_blktrans_dev
*dev
,
1009 unsigned long sector
, unsigned nr_sects
)
1011 partition_t
*part
= (void *)dev
;
1012 uint32_t bsize
= 1 << part
->header
.EraseUnitSize
;
1014 pr_debug("FTL erase sector %ld for %d sectors\n",
1018 uint32_t old_addr
= part
->VirtualBlockMap
[sector
];
1019 if (old_addr
!= 0xffffffff) {
1020 part
->VirtualBlockMap
[sector
] = 0xffffffff;
1021 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
1022 if (set_bam_entry(part
, old_addr
, 0))
1031 /*====================================================================*/
1033 static void ftl_freepart(partition_t
*part
)
1035 vfree(part
->VirtualBlockMap
);
1036 part
->VirtualBlockMap
= NULL
;
1037 kfree(part
->EUNInfo
);
1038 part
->EUNInfo
= NULL
;
1039 kfree(part
->XferInfo
);
1040 part
->XferInfo
= NULL
;
1041 kfree(part
->bam_cache
);
1042 part
->bam_cache
= NULL
;
1043 } /* ftl_freepart */
1045 static void ftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
1047 partition_t
*partition
;
1049 partition
= kzalloc(sizeof(partition_t
), GFP_KERNEL
);
1052 printk(KERN_WARNING
"No memory to scan for FTL on %s\n",
1057 partition
->mbd
.mtd
= mtd
;
1059 if ((scan_header(partition
) == 0) &&
1060 (build_maps(partition
) == 0)) {
1062 partition
->state
= FTL_FORMATTED
;
1064 printk(KERN_INFO
"ftl_cs: opening %d KiB FTL partition\n",
1065 le32_to_cpu(partition
->header
.FormattedSize
) >> 10);
1067 partition
->mbd
.size
= le32_to_cpu(partition
->header
.FormattedSize
) >> 9;
1069 partition
->mbd
.tr
= tr
;
1070 partition
->mbd
.devnum
= -1;
1071 if (!add_mtd_blktrans_dev((void *)partition
))
1078 static void ftl_remove_dev(struct mtd_blktrans_dev
*dev
)
1080 del_mtd_blktrans_dev(dev
);
1081 ftl_freepart((partition_t
*)dev
);
1084 static struct mtd_blktrans_ops ftl_tr
= {
1087 .part_bits
= PART_BITS
,
1088 .blksize
= SECTOR_SIZE
,
1089 .readsect
= ftl_readsect
,
1090 .writesect
= ftl_writesect
,
1091 .discard
= ftl_discardsect
,
1092 .getgeo
= ftl_getgeo
,
1093 .add_mtd
= ftl_add_mtd
,
1094 .remove_dev
= ftl_remove_dev
,
1095 .owner
= THIS_MODULE
,
1098 static int __init
init_ftl(void)
1100 return register_mtd_blktrans(&ftl_tr
);
1103 static void __exit
cleanup_ftl(void)
1105 deregister_mtd_blktrans(&ftl_tr
);
1108 module_init(init_ftl
);
1109 module_exit(cleanup_ftl
);
1112 MODULE_LICENSE("Dual MPL/GPL");
1113 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1114 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");