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
;
114 uint32_t *VirtualPageMap
;
130 uint32_t BlocksPerUnit
;
131 erase_unit_header_t header
;
134 /* Partition state flags */
135 #define FTL_FORMATTED 0x01
137 /* Transfer unit states */
138 #define XFER_UNKNOWN 0x00
139 #define XFER_ERASING 0x01
140 #define XFER_ERASED 0x02
141 #define XFER_PREPARED 0x03
142 #define XFER_FAILED 0x04
144 /*====================================================================*/
147 static void ftl_erase_callback(struct erase_info
*done
);
150 /*======================================================================
152 Scan_header() checks to see if a memory region contains an FTL
153 partition. build_maps() reads all the erase unit headers, builds
154 the erase unit map, and then builds the virtual page map.
156 ======================================================================*/
158 static int scan_header(partition_t
*part
)
160 erase_unit_header_t header
;
161 loff_t offset
, max_offset
;
164 part
->header
.FormattedSize
= 0;
165 max_offset
= (0x100000<part
->mbd
.mtd
->size
)?0x100000:part
->mbd
.mtd
->size
;
166 /* Search first megabyte for a valid FTL header */
168 (offset
+ sizeof(header
)) < max_offset
;
169 offset
+= part
->mbd
.mtd
->erasesize
? : 0x2000) {
171 err
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(header
), &ret
,
172 (unsigned char *)&header
);
177 if (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0) break;
180 if (offset
== max_offset
) {
181 printk(KERN_NOTICE
"ftl_cs: FTL header not found.\n");
184 if (header
.BlockSize
!= 9 ||
185 (header
.EraseUnitSize
< 10) || (header
.EraseUnitSize
> 31) ||
186 (header
.NumTransferUnits
>= le16_to_cpu(header
.NumEraseUnits
))) {
187 printk(KERN_NOTICE
"ftl_cs: FTL header corrupt!\n");
190 if ((1 << header
.EraseUnitSize
) != part
->mbd
.mtd
->erasesize
) {
191 printk(KERN_NOTICE
"ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
192 1 << header
.EraseUnitSize
,part
->mbd
.mtd
->erasesize
);
195 part
->header
= header
;
199 static int build_maps(partition_t
*part
)
201 erase_unit_header_t header
;
202 uint16_t xvalid
, xtrans
, i
;
204 int hdr_ok
, ret
= -1;
208 /* Set up erase unit maps */
209 part
->DataUnits
= le16_to_cpu(part
->header
.NumEraseUnits
) -
210 part
->header
.NumTransferUnits
;
211 part
->EUNInfo
= kmalloc(part
->DataUnits
* sizeof(struct eun_info_t
),
215 for (i
= 0; i
< part
->DataUnits
; i
++)
216 part
->EUNInfo
[i
].Offset
= 0xffffffff;
218 kmalloc(part
->header
.NumTransferUnits
* sizeof(struct xfer_info_t
),
224 for (i
= 0; i
< le16_to_cpu(part
->header
.NumEraseUnits
); i
++) {
225 offset
= ((i
+ le16_to_cpu(part
->header
.FirstPhysicalEUN
))
226 << part
->header
.EraseUnitSize
);
227 ret
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(header
), &retval
,
228 (unsigned char *)&header
);
234 /* Is this a transfer partition? */
235 hdr_ok
= (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0);
236 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) < part
->DataUnits
) &&
237 (part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
== 0xffffffff)) {
238 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
= offset
;
239 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].EraseCount
=
240 le32_to_cpu(header
.EraseCount
);
243 if (xtrans
== part
->header
.NumTransferUnits
) {
244 printk(KERN_NOTICE
"ftl_cs: format error: too many "
245 "transfer units!\n");
248 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) == 0xffff)) {
249 part
->XferInfo
[xtrans
].state
= XFER_PREPARED
;
250 part
->XferInfo
[xtrans
].EraseCount
= le32_to_cpu(header
.EraseCount
);
252 part
->XferInfo
[xtrans
].state
= XFER_UNKNOWN
;
253 /* Pick anything reasonable for the erase count */
254 part
->XferInfo
[xtrans
].EraseCount
=
255 le32_to_cpu(part
->header
.EraseCount
);
257 part
->XferInfo
[xtrans
].Offset
= offset
;
261 /* Check for format trouble */
262 header
= part
->header
;
263 if ((xtrans
!= header
.NumTransferUnits
) ||
264 (xvalid
+xtrans
!= le16_to_cpu(header
.NumEraseUnits
))) {
265 printk(KERN_NOTICE
"ftl_cs: format error: erase units "
270 /* Set up virtual page map */
271 blocks
= le32_to_cpu(header
.FormattedSize
) >> header
.BlockSize
;
272 part
->VirtualBlockMap
= vmalloc(blocks
* sizeof(uint32_t));
273 if (!part
->VirtualBlockMap
)
276 memset(part
->VirtualBlockMap
, 0xff, blocks
* sizeof(uint32_t));
277 part
->BlocksPerUnit
= (1 << header
.EraseUnitSize
) >> header
.BlockSize
;
279 part
->bam_cache
= kmalloc(part
->BlocksPerUnit
* sizeof(uint32_t),
281 if (!part
->bam_cache
)
282 goto out_VirtualBlockMap
;
284 part
->bam_index
= 0xffff;
287 for (i
= 0; i
< part
->DataUnits
; i
++) {
288 part
->EUNInfo
[i
].Free
= 0;
289 part
->EUNInfo
[i
].Deleted
= 0;
290 offset
= part
->EUNInfo
[i
].Offset
+ le32_to_cpu(header
.BAMOffset
);
292 ret
= mtd_read(part
->mbd
.mtd
, offset
,
293 part
->BlocksPerUnit
* sizeof(uint32_t), &retval
,
294 (unsigned char *)part
->bam_cache
);
299 for (j
= 0; j
< part
->BlocksPerUnit
; j
++) {
300 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[j
]))) {
301 part
->EUNInfo
[i
].Free
++;
303 } else if ((BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[j
])) == BLOCK_DATA
) &&
304 (BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
])) < blocks
))
305 part
->VirtualBlockMap
[BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
]))] =
306 (i
<< header
.EraseUnitSize
) + (j
<< header
.BlockSize
);
307 else if (BLOCK_DELETED(le32_to_cpu(part
->bam_cache
[j
])))
308 part
->EUNInfo
[i
].Deleted
++;
316 kfree(part
->bam_cache
);
318 vfree(part
->VirtualBlockMap
);
320 kfree(part
->XferInfo
);
322 kfree(part
->EUNInfo
);
327 /*======================================================================
329 Erase_xfer() schedules an asynchronous erase operation for a
332 ======================================================================*/
334 static int erase_xfer(partition_t
*part
,
338 struct xfer_info_t
*xfer
;
339 struct erase_info
*erase
;
341 xfer
= &part
->XferInfo
[xfernum
];
342 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer
->Offset
);
343 xfer
->state
= XFER_ERASING
;
345 /* Is there a free erase slot? Always in MTD. */
348 erase
=kmalloc(sizeof(struct erase_info
), GFP_KERNEL
);
352 erase
->mtd
= part
->mbd
.mtd
;
353 erase
->callback
= ftl_erase_callback
;
354 erase
->addr
= xfer
->Offset
;
355 erase
->len
= 1 << part
->header
.EraseUnitSize
;
356 erase
->priv
= (u_long
)part
;
358 ret
= mtd_erase(part
->mbd
.mtd
, erase
);
368 /*======================================================================
370 Prepare_xfer() takes a freshly erased transfer unit and gives
371 it an appropriate header.
373 ======================================================================*/
375 static void ftl_erase_callback(struct erase_info
*erase
)
378 struct xfer_info_t
*xfer
;
381 /* Look up the transfer unit */
382 part
= (partition_t
*)(erase
->priv
);
384 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++)
385 if (part
->XferInfo
[i
].Offset
== erase
->addr
) break;
387 if (i
== part
->header
.NumTransferUnits
) {
388 printk(KERN_NOTICE
"ftl_cs: internal error: "
389 "erase lookup failed!\n");
393 xfer
= &part
->XferInfo
[i
];
394 if (erase
->state
== MTD_ERASE_DONE
)
395 xfer
->state
= XFER_ERASED
;
397 xfer
->state
= XFER_FAILED
;
398 printk(KERN_NOTICE
"ftl_cs: erase failed: state = %d\n",
404 } /* ftl_erase_callback */
406 static int prepare_xfer(partition_t
*part
, int i
)
408 erase_unit_header_t header
;
409 struct xfer_info_t
*xfer
;
415 xfer
= &part
->XferInfo
[i
];
416 xfer
->state
= XFER_FAILED
;
418 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer
->Offset
);
420 /* Write the transfer unit header */
421 header
= part
->header
;
422 header
.LogicalEUN
= cpu_to_le16(0xffff);
423 header
.EraseCount
= cpu_to_le32(xfer
->EraseCount
);
425 ret
= mtd_write(part
->mbd
.mtd
, xfer
->Offset
, sizeof(header
), &retlen
,
432 /* Write the BAM stub */
433 nbam
= (part
->BlocksPerUnit
* sizeof(uint32_t) +
434 le32_to_cpu(part
->header
.BAMOffset
) + SECTOR_SIZE
- 1) / SECTOR_SIZE
;
436 offset
= xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
437 ctl
= cpu_to_le32(BLOCK_CONTROL
);
439 for (i
= 0; i
< nbam
; i
++, offset
+= sizeof(uint32_t)) {
441 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
447 xfer
->state
= XFER_PREPARED
;
452 /*======================================================================
454 Copy_erase_unit() takes a full erase block and a transfer unit,
455 copies everything to the transfer unit, then swaps the block
458 All data blocks are copied to the corresponding blocks in the
459 target unit, so the virtual block map does not need to be
462 ======================================================================*/
464 static int copy_erase_unit(partition_t
*part
, uint16_t srcunit
,
467 u_char buf
[SECTOR_SIZE
];
468 struct eun_info_t
*eun
;
469 struct xfer_info_t
*xfer
;
470 uint32_t src
, dest
, free
, i
;
475 uint16_t srcunitswap
= cpu_to_le16(srcunit
);
477 eun
= &part
->EUNInfo
[srcunit
];
478 xfer
= &part
->XferInfo
[xferunit
];
479 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
480 eun
->Offset
, xfer
->Offset
);
483 /* Read current BAM */
484 if (part
->bam_index
!= srcunit
) {
486 offset
= eun
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
488 ret
= mtd_read(part
->mbd
.mtd
, offset
,
489 part
->BlocksPerUnit
* sizeof(uint32_t), &retlen
,
490 (u_char
*)(part
->bam_cache
));
492 /* mark the cache bad, in case we get an error later */
493 part
->bam_index
= 0xffff;
496 printk( KERN_WARNING
"ftl: Failed to read BAM cache in copy_erase_unit()!\n");
501 /* Write the LogicalEUN for the transfer unit */
502 xfer
->state
= XFER_UNKNOWN
;
503 offset
= xfer
->Offset
+ 20; /* Bad! */
504 unit
= cpu_to_le16(0x7fff);
506 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint16_t), &retlen
,
510 printk( KERN_WARNING
"ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
514 /* Copy all data blocks from source unit to transfer unit */
515 src
= eun
->Offset
; dest
= xfer
->Offset
;
519 for (i
= 0; i
< part
->BlocksPerUnit
; i
++) {
520 switch (BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[i
]))) {
522 /* This gets updated later */
525 case BLOCK_REPLACEMENT
:
526 ret
= mtd_read(part
->mbd
.mtd
, src
, SECTOR_SIZE
, &retlen
,
529 printk(KERN_WARNING
"ftl: Error reading old xfer unit in copy_erase_unit\n");
534 ret
= mtd_write(part
->mbd
.mtd
, dest
, SECTOR_SIZE
, &retlen
,
537 printk(KERN_WARNING
"ftl: Error writing new xfer unit in copy_erase_unit\n");
543 /* All other blocks must be free */
544 part
->bam_cache
[i
] = cpu_to_le32(0xffffffff);
552 /* Write the BAM to the transfer unit */
553 ret
= mtd_write(part
->mbd
.mtd
,
554 xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
555 part
->BlocksPerUnit
* sizeof(int32_t),
557 (u_char
*)part
->bam_cache
);
559 printk( KERN_WARNING
"ftl: Error writing BAM in copy_erase_unit\n");
564 /* All clear? Then update the LogicalEUN again */
565 ret
= mtd_write(part
->mbd
.mtd
, xfer
->Offset
+ 20, sizeof(uint16_t),
566 &retlen
, (u_char
*)&srcunitswap
);
569 printk(KERN_WARNING
"ftl: Error writing new LogicalEUN in copy_erase_unit\n");
574 /* Update the maps and usage stats*/
575 i
= xfer
->EraseCount
;
576 xfer
->EraseCount
= eun
->EraseCount
;
579 xfer
->Offset
= eun
->Offset
;
581 part
->FreeTotal
-= eun
->Free
;
582 part
->FreeTotal
+= free
;
586 /* Now, the cache should be valid for the new block */
587 part
->bam_index
= srcunit
;
590 } /* copy_erase_unit */
592 /*======================================================================
594 reclaim_block() picks a full erase unit and a transfer unit and
595 then calls copy_erase_unit() to copy one to the other. Then, it
596 schedules an erase on the expired block.
598 What's a good way to decide which transfer unit and which erase
599 unit to use? Beats me. My way is to always pick the transfer
600 unit with the fewest erases, and usually pick the data unit with
601 the most deleted blocks. But with a small probability, pick the
602 oldest data unit instead. This means that we generally postpone
603 the next reclamation as long as possible, but shuffle static
604 stuff around a bit for wear leveling.
606 ======================================================================*/
608 static int reclaim_block(partition_t
*part
)
610 uint16_t i
, eun
, xfer
;
614 pr_debug("ftl_cs: reclaiming space...\n");
615 pr_debug("NumTransferUnits == %x\n", part
->header
.NumTransferUnits
);
616 /* Pick the least erased transfer unit */
617 best
= 0xffffffff; xfer
= 0xffff;
620 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++) {
622 if (part
->XferInfo
[i
].state
== XFER_UNKNOWN
) {
623 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i
);
627 if (part
->XferInfo
[i
].state
== XFER_ERASING
) {
628 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i
);
632 else if (part
->XferInfo
[i
].state
== XFER_ERASED
) {
633 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i
);
635 prepare_xfer(part
, i
);
637 if (part
->XferInfo
[i
].state
== XFER_PREPARED
) {
638 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i
);
640 if (part
->XferInfo
[i
].EraseCount
<= best
) {
641 best
= part
->XferInfo
[i
].EraseCount
;
646 pr_debug("XferInfo[%d].state == %x\n",i
, part
->XferInfo
[i
].state
);
649 if (xfer
== 0xffff) {
651 pr_debug("ftl_cs: waiting for transfer "
652 "unit to be prepared...\n");
653 mtd_sync(part
->mbd
.mtd
);
657 printk(KERN_NOTICE
"ftl_cs: reclaim failed: no "
658 "suitable transfer units!\n");
660 pr_debug("ftl_cs: reclaim failed: no "
661 "suitable transfer units!\n");
666 } while (xfer
== 0xffff);
669 if ((jiffies
% shuffle_freq
) == 0) {
670 pr_debug("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 pr_debug("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 uint32_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
= mtd_read(part
->mbd
.mtd
,
752 part
->EUNInfo
[eun
].Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
753 part
->BlocksPerUnit
* sizeof(uint32_t),
755 (u_char
*)(part
->bam_cache
));
758 printk(KERN_WARNING
"ftl: Error reading BAM in find_free\n");
761 part
->bam_index
= eun
;
764 /* Find a free block */
765 for (blk
= 0; blk
< part
->BlocksPerUnit
; blk
++)
766 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[blk
]))) break;
767 if (blk
== part
->BlocksPerUnit
) {
773 printk(KERN_NOTICE
"ftl_cs: bad free list!\n");
776 pr_debug("ftl_cs: found free block at %d in %d\n", blk
, eun
);
782 /*======================================================================
784 Read a series of sectors from an FTL partition.
786 ======================================================================*/
788 static int ftl_read(partition_t
*part
, caddr_t buffer
,
789 u_long sector
, u_long nblocks
)
791 uint32_t log_addr
, bsize
;
794 size_t offset
, retlen
;
796 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
797 part
, sector
, nblocks
);
798 if (!(part
->state
& FTL_FORMATTED
)) {
799 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
802 bsize
= 1 << part
->header
.EraseUnitSize
;
804 for (i
= 0; i
< nblocks
; i
++) {
805 if (((sector
+i
) * SECTOR_SIZE
) >= le32_to_cpu(part
->header
.FormattedSize
)) {
806 printk(KERN_NOTICE
"ftl_cs: bad read offset\n");
809 log_addr
= part
->VirtualBlockMap
[sector
+i
];
810 if (log_addr
== 0xffffffff)
811 memset(buffer
, 0, SECTOR_SIZE
);
813 offset
= (part
->EUNInfo
[log_addr
/ bsize
].Offset
814 + (log_addr
% bsize
));
815 ret
= mtd_read(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
,
819 printk(KERN_WARNING
"Error reading MTD device in ftl_read()\n");
823 buffer
+= SECTOR_SIZE
;
828 /*======================================================================
830 Write a series of sectors to an FTL partition
832 ======================================================================*/
834 static int set_bam_entry(partition_t
*part
, uint32_t log_addr
,
837 uint32_t bsize
, blk
, le_virt_addr
;
843 size_t retlen
, offset
;
845 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
846 part
, log_addr
, virt_addr
);
847 bsize
= 1 << part
->header
.EraseUnitSize
;
848 eun
= log_addr
/ bsize
;
849 blk
= (log_addr
% bsize
) / SECTOR_SIZE
;
850 offset
= (part
->EUNInfo
[eun
].Offset
+ blk
* sizeof(uint32_t) +
851 le32_to_cpu(part
->header
.BAMOffset
));
854 ret
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
855 (u_char
*)&old_addr
);
857 printk(KERN_WARNING
"ftl: Error reading old_addr in set_bam_entry: %d\n",ret
);
860 old_addr
= le32_to_cpu(old_addr
);
862 if (((virt_addr
== 0xfffffffe) && !BLOCK_FREE(old_addr
)) ||
863 ((virt_addr
== 0) && (BLOCK_TYPE(old_addr
) != BLOCK_DATA
)) ||
864 (!BLOCK_DELETED(virt_addr
) && (old_addr
!= 0xfffffffe))) {
867 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() inconsistency!\n");
868 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, old = 0x%x"
869 ", new = 0x%x\n", log_addr
, old_addr
, virt_addr
);
874 le_virt_addr
= cpu_to_le32(virt_addr
);
875 if (part
->bam_index
== eun
) {
877 if (le32_to_cpu(part
->bam_cache
[blk
]) != old_addr
) {
880 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() "
882 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, cache"
884 le32_to_cpu(part
->bam_cache
[blk
]), old_addr
);
889 part
->bam_cache
[blk
] = le_virt_addr
;
891 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
892 (u_char
*)&le_virt_addr
);
895 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() failed!\n");
896 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, new = 0x%x\n",
897 log_addr
, virt_addr
);
900 } /* set_bam_entry */
902 static int ftl_write(partition_t
*part
, caddr_t buffer
,
903 u_long sector
, u_long nblocks
)
905 uint32_t bsize
, log_addr
, virt_addr
, old_addr
, blk
;
908 size_t retlen
, offset
;
910 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
911 part
, sector
, nblocks
);
912 if (!(part
->state
& FTL_FORMATTED
)) {
913 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
916 /* See if we need to reclaim space, before we start */
917 while (part
->FreeTotal
< nblocks
) {
918 ret
= reclaim_block(part
);
923 bsize
= 1 << part
->header
.EraseUnitSize
;
925 virt_addr
= sector
* SECTOR_SIZE
| BLOCK_DATA
;
926 for (i
= 0; i
< nblocks
; i
++) {
927 if (virt_addr
>= le32_to_cpu(part
->header
.FormattedSize
)) {
928 printk(KERN_NOTICE
"ftl_cs: bad write offset\n");
932 /* Grab a free block */
933 blk
= find_free(part
);
937 printk(KERN_NOTICE
"ftl_cs: internal error: "
938 "no free blocks!\n");
942 /* Tag the BAM entry, and write the new block */
943 log_addr
= part
->bam_index
* bsize
+ blk
* SECTOR_SIZE
;
944 part
->EUNInfo
[part
->bam_index
].Free
--;
946 if (set_bam_entry(part
, log_addr
, 0xfffffffe))
948 part
->EUNInfo
[part
->bam_index
].Deleted
++;
949 offset
= (part
->EUNInfo
[part
->bam_index
].Offset
+
951 ret
= mtd_write(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
, buffer
);
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 static int ftl_discardsect(struct mtd_blktrans_dev
*dev
,
1010 unsigned long sector
, unsigned nr_sects
)
1012 partition_t
*part
= (void *)dev
;
1013 uint32_t bsize
= 1 << part
->header
.EraseUnitSize
;
1015 pr_debug("FTL erase sector %ld for %d sectors\n",
1019 uint32_t old_addr
= part
->VirtualBlockMap
[sector
];
1020 if (old_addr
!= 0xffffffff) {
1021 part
->VirtualBlockMap
[sector
] = 0xffffffff;
1022 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
1023 if (set_bam_entry(part
, old_addr
, 0))
1032 /*====================================================================*/
1034 static void ftl_freepart(partition_t
*part
)
1036 vfree(part
->VirtualBlockMap
);
1037 part
->VirtualBlockMap
= NULL
;
1038 kfree(part
->VirtualPageMap
);
1039 part
->VirtualPageMap
= NULL
;
1040 kfree(part
->EUNInfo
);
1041 part
->EUNInfo
= NULL
;
1042 kfree(part
->XferInfo
);
1043 part
->XferInfo
= NULL
;
1044 kfree(part
->bam_cache
);
1045 part
->bam_cache
= NULL
;
1046 } /* ftl_freepart */
1048 static void ftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
1050 partition_t
*partition
;
1052 partition
= kzalloc(sizeof(partition_t
), GFP_KERNEL
);
1055 printk(KERN_WARNING
"No memory to scan for FTL on %s\n",
1060 partition
->mbd
.mtd
= mtd
;
1062 if ((scan_header(partition
) == 0) &&
1063 (build_maps(partition
) == 0)) {
1065 partition
->state
= FTL_FORMATTED
;
1067 printk(KERN_INFO
"ftl_cs: opening %d KiB FTL partition\n",
1068 le32_to_cpu(partition
->header
.FormattedSize
) >> 10);
1070 partition
->mbd
.size
= le32_to_cpu(partition
->header
.FormattedSize
) >> 9;
1072 partition
->mbd
.tr
= tr
;
1073 partition
->mbd
.devnum
= -1;
1074 if (!add_mtd_blktrans_dev((void *)partition
))
1078 ftl_freepart(partition
);
1082 static void ftl_remove_dev(struct mtd_blktrans_dev
*dev
)
1084 del_mtd_blktrans_dev(dev
);
1085 ftl_freepart((partition_t
*)dev
);
1088 static struct mtd_blktrans_ops ftl_tr
= {
1091 .part_bits
= PART_BITS
,
1092 .blksize
= SECTOR_SIZE
,
1093 .readsect
= ftl_readsect
,
1094 .writesect
= ftl_writesect
,
1095 .discard
= ftl_discardsect
,
1096 .getgeo
= ftl_getgeo
,
1097 .add_mtd
= ftl_add_mtd
,
1098 .remove_dev
= ftl_remove_dev
,
1099 .owner
= THIS_MODULE
,
1102 static int __init
init_ftl(void)
1104 return register_mtd_blktrans(&ftl_tr
);
1107 static void __exit
cleanup_ftl(void)
1109 deregister_mtd_blktrans(&ftl_tr
);
1112 module_init(init_ftl
);
1113 module_exit(cleanup_ftl
);
1116 MODULE_LICENSE("Dual MPL/GPL");
1117 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1118 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");