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 <linux/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 /*======================================================================
145 Scan_header() checks to see if a memory region contains an FTL
146 partition. build_maps() reads all the erase unit headers, builds
147 the erase unit map, and then builds the virtual page map.
149 ======================================================================*/
151 static int scan_header(partition_t
*part
)
153 erase_unit_header_t header
;
154 loff_t offset
, max_offset
;
157 part
->header
.FormattedSize
= 0;
158 max_offset
= (0x100000<part
->mbd
.mtd
->size
)?0x100000:part
->mbd
.mtd
->size
;
159 /* Search first megabyte for a valid FTL header */
161 (offset
+ sizeof(header
)) < max_offset
;
162 offset
+= part
->mbd
.mtd
->erasesize
? : 0x2000) {
164 err
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(header
), &ret
,
165 (unsigned char *)&header
);
170 if (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0) break;
173 if (offset
== max_offset
) {
174 printk(KERN_NOTICE
"ftl_cs: FTL header not found.\n");
177 if (header
.BlockSize
!= 9 ||
178 (header
.EraseUnitSize
< 10) || (header
.EraseUnitSize
> 31) ||
179 (header
.NumTransferUnits
>= le16_to_cpu(header
.NumEraseUnits
))) {
180 printk(KERN_NOTICE
"ftl_cs: FTL header corrupt!\n");
183 if ((1 << header
.EraseUnitSize
) != part
->mbd
.mtd
->erasesize
) {
184 printk(KERN_NOTICE
"ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
185 1 << header
.EraseUnitSize
,part
->mbd
.mtd
->erasesize
);
188 part
->header
= header
;
192 static int build_maps(partition_t
*part
)
194 erase_unit_header_t header
;
195 uint16_t xvalid
, xtrans
, i
;
197 int hdr_ok
, ret
= -1;
201 /* Set up erase unit maps */
202 part
->DataUnits
= le16_to_cpu(part
->header
.NumEraseUnits
) -
203 part
->header
.NumTransferUnits
;
204 part
->EUNInfo
= kmalloc_array(part
->DataUnits
, sizeof(struct eun_info_t
),
208 for (i
= 0; i
< part
->DataUnits
; i
++)
209 part
->EUNInfo
[i
].Offset
= 0xffffffff;
211 kmalloc_array(part
->header
.NumTransferUnits
,
212 sizeof(struct xfer_info_t
),
218 for (i
= 0; i
< le16_to_cpu(part
->header
.NumEraseUnits
); i
++) {
219 offset
= ((i
+ le16_to_cpu(part
->header
.FirstPhysicalEUN
))
220 << part
->header
.EraseUnitSize
);
221 ret
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(header
), &retval
,
222 (unsigned char *)&header
);
228 /* Is this a transfer partition? */
229 hdr_ok
= (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0);
230 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) < part
->DataUnits
) &&
231 (part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
== 0xffffffff)) {
232 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
= offset
;
233 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].EraseCount
=
234 le32_to_cpu(header
.EraseCount
);
237 if (xtrans
== part
->header
.NumTransferUnits
) {
238 printk(KERN_NOTICE
"ftl_cs: format error: too many "
239 "transfer units!\n");
242 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) == 0xffff)) {
243 part
->XferInfo
[xtrans
].state
= XFER_PREPARED
;
244 part
->XferInfo
[xtrans
].EraseCount
= le32_to_cpu(header
.EraseCount
);
246 part
->XferInfo
[xtrans
].state
= XFER_UNKNOWN
;
247 /* Pick anything reasonable for the erase count */
248 part
->XferInfo
[xtrans
].EraseCount
=
249 le32_to_cpu(part
->header
.EraseCount
);
251 part
->XferInfo
[xtrans
].Offset
= offset
;
255 /* Check for format trouble */
256 header
= part
->header
;
257 if ((xtrans
!= header
.NumTransferUnits
) ||
258 (xvalid
+xtrans
!= le16_to_cpu(header
.NumEraseUnits
))) {
259 printk(KERN_NOTICE
"ftl_cs: format error: erase units "
264 /* Set up virtual page map */
265 blocks
= le32_to_cpu(header
.FormattedSize
) >> header
.BlockSize
;
266 part
->VirtualBlockMap
= vmalloc(array_size(blocks
, sizeof(uint32_t)));
267 if (!part
->VirtualBlockMap
)
270 memset(part
->VirtualBlockMap
, 0xff, blocks
* sizeof(uint32_t));
271 part
->BlocksPerUnit
= (1 << header
.EraseUnitSize
) >> header
.BlockSize
;
273 part
->bam_cache
= kmalloc_array(part
->BlocksPerUnit
, sizeof(uint32_t),
275 if (!part
->bam_cache
)
276 goto out_VirtualBlockMap
;
278 part
->bam_index
= 0xffff;
281 for (i
= 0; i
< part
->DataUnits
; i
++) {
282 part
->EUNInfo
[i
].Free
= 0;
283 part
->EUNInfo
[i
].Deleted
= 0;
284 offset
= part
->EUNInfo
[i
].Offset
+ le32_to_cpu(header
.BAMOffset
);
286 ret
= mtd_read(part
->mbd
.mtd
, offset
,
287 part
->BlocksPerUnit
* sizeof(uint32_t), &retval
,
288 (unsigned char *)part
->bam_cache
);
293 for (j
= 0; j
< part
->BlocksPerUnit
; j
++) {
294 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[j
]))) {
295 part
->EUNInfo
[i
].Free
++;
297 } else if ((BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[j
])) == BLOCK_DATA
) &&
298 (BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
])) < blocks
))
299 part
->VirtualBlockMap
[BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
]))] =
300 (i
<< header
.EraseUnitSize
) + (j
<< header
.BlockSize
);
301 else if (BLOCK_DELETED(le32_to_cpu(part
->bam_cache
[j
])))
302 part
->EUNInfo
[i
].Deleted
++;
310 kfree(part
->bam_cache
);
312 vfree(part
->VirtualBlockMap
);
314 kfree(part
->XferInfo
);
316 kfree(part
->EUNInfo
);
321 /*======================================================================
323 Erase_xfer() schedules an asynchronous erase operation for a
326 ======================================================================*/
328 static int erase_xfer(partition_t
*part
,
332 struct xfer_info_t
*xfer
;
333 struct erase_info
*erase
;
335 xfer
= &part
->XferInfo
[xfernum
];
336 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer
->Offset
);
337 xfer
->state
= XFER_ERASING
;
339 /* Is there a free erase slot? Always in MTD. */
342 erase
=kmalloc(sizeof(struct erase_info
), GFP_KERNEL
);
346 erase
->addr
= xfer
->Offset
;
347 erase
->len
= 1 << part
->header
.EraseUnitSize
;
349 ret
= mtd_erase(part
->mbd
.mtd
, erase
);
351 xfer
->state
= XFER_ERASED
;
354 xfer
->state
= XFER_FAILED
;
355 pr_notice("ftl_cs: erase failed: err = %d\n", ret
);
363 /*======================================================================
365 Prepare_xfer() takes a freshly erased transfer unit and gives
366 it an appropriate header.
368 ======================================================================*/
370 static int prepare_xfer(partition_t
*part
, int i
)
372 erase_unit_header_t header
;
373 struct xfer_info_t
*xfer
;
379 xfer
= &part
->XferInfo
[i
];
380 xfer
->state
= XFER_FAILED
;
382 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer
->Offset
);
384 /* Write the transfer unit header */
385 header
= part
->header
;
386 header
.LogicalEUN
= cpu_to_le16(0xffff);
387 header
.EraseCount
= cpu_to_le32(xfer
->EraseCount
);
389 ret
= mtd_write(part
->mbd
.mtd
, xfer
->Offset
, sizeof(header
), &retlen
,
396 /* Write the BAM stub */
397 nbam
= DIV_ROUND_UP(part
->BlocksPerUnit
* sizeof(uint32_t) +
398 le32_to_cpu(part
->header
.BAMOffset
), SECTOR_SIZE
);
400 offset
= xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
401 ctl
= cpu_to_le32(BLOCK_CONTROL
);
403 for (i
= 0; i
< nbam
; i
++, offset
+= sizeof(uint32_t)) {
405 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
411 xfer
->state
= XFER_PREPARED
;
416 /*======================================================================
418 Copy_erase_unit() takes a full erase block and a transfer unit,
419 copies everything to the transfer unit, then swaps the block
422 All data blocks are copied to the corresponding blocks in the
423 target unit, so the virtual block map does not need to be
426 ======================================================================*/
428 static int copy_erase_unit(partition_t
*part
, uint16_t srcunit
,
431 u_char buf
[SECTOR_SIZE
];
432 struct eun_info_t
*eun
;
433 struct xfer_info_t
*xfer
;
434 uint32_t src
, dest
, free
, i
;
439 uint16_t srcunitswap
= cpu_to_le16(srcunit
);
441 eun
= &part
->EUNInfo
[srcunit
];
442 xfer
= &part
->XferInfo
[xferunit
];
443 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
444 eun
->Offset
, xfer
->Offset
);
447 /* Read current BAM */
448 if (part
->bam_index
!= srcunit
) {
450 offset
= eun
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
452 ret
= mtd_read(part
->mbd
.mtd
, offset
,
453 part
->BlocksPerUnit
* sizeof(uint32_t), &retlen
,
454 (u_char
*)(part
->bam_cache
));
456 /* mark the cache bad, in case we get an error later */
457 part
->bam_index
= 0xffff;
460 printk( KERN_WARNING
"ftl: Failed to read BAM cache in copy_erase_unit()!\n");
465 /* Write the LogicalEUN for the transfer unit */
466 xfer
->state
= XFER_UNKNOWN
;
467 offset
= xfer
->Offset
+ 20; /* Bad! */
468 unit
= cpu_to_le16(0x7fff);
470 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint16_t), &retlen
,
474 printk( KERN_WARNING
"ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
478 /* Copy all data blocks from source unit to transfer unit */
479 src
= eun
->Offset
; dest
= xfer
->Offset
;
483 for (i
= 0; i
< part
->BlocksPerUnit
; i
++) {
484 switch (BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[i
]))) {
486 /* This gets updated later */
489 case BLOCK_REPLACEMENT
:
490 ret
= mtd_read(part
->mbd
.mtd
, src
, SECTOR_SIZE
, &retlen
,
493 printk(KERN_WARNING
"ftl: Error reading old xfer unit in copy_erase_unit\n");
498 ret
= mtd_write(part
->mbd
.mtd
, dest
, SECTOR_SIZE
, &retlen
,
501 printk(KERN_WARNING
"ftl: Error writing new xfer unit in copy_erase_unit\n");
507 /* All other blocks must be free */
508 part
->bam_cache
[i
] = cpu_to_le32(0xffffffff);
516 /* Write the BAM to the transfer unit */
517 ret
= mtd_write(part
->mbd
.mtd
,
518 xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
519 part
->BlocksPerUnit
* sizeof(int32_t),
521 (u_char
*)part
->bam_cache
);
523 printk( KERN_WARNING
"ftl: Error writing BAM in copy_erase_unit\n");
528 /* All clear? Then update the LogicalEUN again */
529 ret
= mtd_write(part
->mbd
.mtd
, xfer
->Offset
+ 20, sizeof(uint16_t),
530 &retlen
, (u_char
*)&srcunitswap
);
533 printk(KERN_WARNING
"ftl: Error writing new LogicalEUN in copy_erase_unit\n");
538 /* Update the maps and usage stats*/
539 swap(xfer
->EraseCount
, eun
->EraseCount
);
540 swap(xfer
->Offset
, eun
->Offset
);
541 part
->FreeTotal
-= eun
->Free
;
542 part
->FreeTotal
+= free
;
546 /* Now, the cache should be valid for the new block */
547 part
->bam_index
= srcunit
;
550 } /* copy_erase_unit */
552 /*======================================================================
554 reclaim_block() picks a full erase unit and a transfer unit and
555 then calls copy_erase_unit() to copy one to the other. Then, it
556 schedules an erase on the expired block.
558 What's a good way to decide which transfer unit and which erase
559 unit to use? Beats me. My way is to always pick the transfer
560 unit with the fewest erases, and usually pick the data unit with
561 the most deleted blocks. But with a small probability, pick the
562 oldest data unit instead. This means that we generally postpone
563 the next reclamation as long as possible, but shuffle static
564 stuff around a bit for wear leveling.
566 ======================================================================*/
568 static int reclaim_block(partition_t
*part
)
570 uint16_t i
, eun
, xfer
;
574 pr_debug("ftl_cs: reclaiming space...\n");
575 pr_debug("NumTransferUnits == %x\n", part
->header
.NumTransferUnits
);
576 /* Pick the least erased transfer unit */
577 best
= 0xffffffff; xfer
= 0xffff;
580 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++) {
582 if (part
->XferInfo
[i
].state
== XFER_UNKNOWN
) {
583 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i
);
587 if (part
->XferInfo
[i
].state
== XFER_ERASING
) {
588 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i
);
592 else if (part
->XferInfo
[i
].state
== XFER_ERASED
) {
593 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i
);
595 prepare_xfer(part
, i
);
597 if (part
->XferInfo
[i
].state
== XFER_PREPARED
) {
598 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i
);
600 if (part
->XferInfo
[i
].EraseCount
<= best
) {
601 best
= part
->XferInfo
[i
].EraseCount
;
606 pr_debug("XferInfo[%d].state == %x\n",i
, part
->XferInfo
[i
].state
);
609 if (xfer
== 0xffff) {
611 pr_debug("ftl_cs: waiting for transfer "
612 "unit to be prepared...\n");
613 mtd_sync(part
->mbd
.mtd
);
617 printk(KERN_NOTICE
"ftl_cs: reclaim failed: no "
618 "suitable transfer units!\n");
620 pr_debug("ftl_cs: reclaim failed: no "
621 "suitable transfer units!\n");
626 } while (xfer
== 0xffff);
629 if ((jiffies
% shuffle_freq
) == 0) {
630 pr_debug("ftl_cs: recycling freshest block...\n");
632 for (i
= 0; i
< part
->DataUnits
; i
++)
633 if (part
->EUNInfo
[i
].EraseCount
<= best
) {
634 best
= part
->EUNInfo
[i
].EraseCount
;
639 for (i
= 0; i
< part
->DataUnits
; i
++)
640 if (part
->EUNInfo
[i
].Deleted
>= best
) {
641 best
= part
->EUNInfo
[i
].Deleted
;
647 printk(KERN_NOTICE
"ftl_cs: reclaim failed: "
648 "no free blocks!\n");
650 pr_debug("ftl_cs: reclaim failed: "
651 "no free blocks!\n");
656 ret
= copy_erase_unit(part
, eun
, xfer
);
658 erase_xfer(part
, xfer
);
660 printk(KERN_NOTICE
"ftl_cs: copy_erase_unit failed!\n");
662 } /* reclaim_block */
664 /*======================================================================
666 Find_free() searches for a free block. If necessary, it updates
667 the BAM cache for the erase unit containing the free block. It
668 returns the block index -- the erase unit is just the currently
669 cached unit. If there are no free blocks, it returns 0 -- this
670 is never a valid data block because it contains the header.
672 ======================================================================*/
675 static void dump_lists(partition_t
*part
)
678 printk(KERN_DEBUG
"ftl_cs: Free total = %d\n", part
->FreeTotal
);
679 for (i
= 0; i
< part
->DataUnits
; i
++)
680 printk(KERN_DEBUG
"ftl_cs: unit %d: %d phys, %d free, "
682 part
->EUNInfo
[i
].Offset
>> part
->header
.EraseUnitSize
,
683 part
->EUNInfo
[i
].Free
, part
->EUNInfo
[i
].Deleted
);
687 static uint32_t find_free(partition_t
*part
)
694 /* Find an erase unit with some free space */
695 stop
= (part
->bam_index
== 0xffff) ? 0 : part
->bam_index
;
698 if (part
->EUNInfo
[eun
].Free
!= 0) break;
699 /* Wrap around at end of table */
700 if (++eun
== part
->DataUnits
) eun
= 0;
701 } while (eun
!= stop
);
703 if (part
->EUNInfo
[eun
].Free
== 0)
706 /* Is this unit's BAM cached? */
707 if (eun
!= part
->bam_index
) {
708 /* Invalidate cache */
709 part
->bam_index
= 0xffff;
711 ret
= mtd_read(part
->mbd
.mtd
,
712 part
->EUNInfo
[eun
].Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
713 part
->BlocksPerUnit
* sizeof(uint32_t),
715 (u_char
*)(part
->bam_cache
));
718 printk(KERN_WARNING
"ftl: Error reading BAM in find_free\n");
721 part
->bam_index
= eun
;
724 /* Find a free block */
725 for (blk
= 0; blk
< part
->BlocksPerUnit
; blk
++)
726 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[blk
]))) break;
727 if (blk
== part
->BlocksPerUnit
) {
733 printk(KERN_NOTICE
"ftl_cs: bad free list!\n");
736 pr_debug("ftl_cs: found free block at %d in %d\n", blk
, eun
);
742 /*======================================================================
744 Read a series of sectors from an FTL partition.
746 ======================================================================*/
748 static int ftl_read(partition_t
*part
, caddr_t buffer
,
749 u_long sector
, u_long nblocks
)
751 uint32_t log_addr
, bsize
;
754 size_t offset
, retlen
;
756 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
757 part
, sector
, nblocks
);
758 if (!(part
->state
& FTL_FORMATTED
)) {
759 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
762 bsize
= 1 << part
->header
.EraseUnitSize
;
764 for (i
= 0; i
< nblocks
; i
++) {
765 if (((sector
+i
) * SECTOR_SIZE
) >= le32_to_cpu(part
->header
.FormattedSize
)) {
766 printk(KERN_NOTICE
"ftl_cs: bad read offset\n");
769 log_addr
= part
->VirtualBlockMap
[sector
+i
];
770 if (log_addr
== 0xffffffff)
771 memset(buffer
, 0, SECTOR_SIZE
);
773 offset
= (part
->EUNInfo
[log_addr
/ bsize
].Offset
774 + (log_addr
% bsize
));
775 ret
= mtd_read(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
,
779 printk(KERN_WARNING
"Error reading MTD device in ftl_read()\n");
783 buffer
+= SECTOR_SIZE
;
788 /*======================================================================
790 Write a series of sectors to an FTL partition
792 ======================================================================*/
794 static int set_bam_entry(partition_t
*part
, uint32_t log_addr
,
797 uint32_t bsize
, blk
, le_virt_addr
;
803 size_t retlen
, offset
;
805 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
806 part
, log_addr
, virt_addr
);
807 bsize
= 1 << part
->header
.EraseUnitSize
;
808 eun
= log_addr
/ bsize
;
809 blk
= (log_addr
% bsize
) / SECTOR_SIZE
;
810 offset
= (part
->EUNInfo
[eun
].Offset
+ blk
* sizeof(uint32_t) +
811 le32_to_cpu(part
->header
.BAMOffset
));
814 ret
= mtd_read(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
815 (u_char
*)&old_addr
);
817 printk(KERN_WARNING
"ftl: Error reading old_addr in set_bam_entry: %d\n",ret
);
820 old_addr
= le32_to_cpu(old_addr
);
822 if (((virt_addr
== 0xfffffffe) && !BLOCK_FREE(old_addr
)) ||
823 ((virt_addr
== 0) && (BLOCK_TYPE(old_addr
) != BLOCK_DATA
)) ||
824 (!BLOCK_DELETED(virt_addr
) && (old_addr
!= 0xfffffffe))) {
827 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() inconsistency!\n");
828 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, old = 0x%x"
829 ", new = 0x%x\n", log_addr
, old_addr
, virt_addr
);
834 le_virt_addr
= cpu_to_le32(virt_addr
);
835 if (part
->bam_index
== eun
) {
837 if (le32_to_cpu(part
->bam_cache
[blk
]) != old_addr
) {
840 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() "
842 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, cache"
844 le32_to_cpu(part
->bam_cache
[blk
]), old_addr
);
849 part
->bam_cache
[blk
] = le_virt_addr
;
851 ret
= mtd_write(part
->mbd
.mtd
, offset
, sizeof(uint32_t), &retlen
,
852 (u_char
*)&le_virt_addr
);
855 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() failed!\n");
856 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, new = 0x%x\n",
857 log_addr
, virt_addr
);
860 } /* set_bam_entry */
862 static int ftl_write(partition_t
*part
, caddr_t buffer
,
863 u_long sector
, u_long nblocks
)
865 uint32_t bsize
, log_addr
, virt_addr
, old_addr
, blk
;
868 size_t retlen
, offset
;
870 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
871 part
, sector
, nblocks
);
872 if (!(part
->state
& FTL_FORMATTED
)) {
873 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
876 /* See if we need to reclaim space, before we start */
877 while (part
->FreeTotal
< nblocks
) {
878 ret
= reclaim_block(part
);
883 bsize
= 1 << part
->header
.EraseUnitSize
;
885 virt_addr
= sector
* SECTOR_SIZE
| BLOCK_DATA
;
886 for (i
= 0; i
< nblocks
; i
++) {
887 if (virt_addr
>= le32_to_cpu(part
->header
.FormattedSize
)) {
888 printk(KERN_NOTICE
"ftl_cs: bad write offset\n");
892 /* Grab a free block */
893 blk
= find_free(part
);
897 printk(KERN_NOTICE
"ftl_cs: internal error: "
898 "no free blocks!\n");
902 /* Tag the BAM entry, and write the new block */
903 log_addr
= part
->bam_index
* bsize
+ blk
* SECTOR_SIZE
;
904 part
->EUNInfo
[part
->bam_index
].Free
--;
906 if (set_bam_entry(part
, log_addr
, 0xfffffffe))
908 part
->EUNInfo
[part
->bam_index
].Deleted
++;
909 offset
= (part
->EUNInfo
[part
->bam_index
].Offset
+
911 ret
= mtd_write(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
, buffer
);
914 printk(KERN_NOTICE
"ftl_cs: block write failed!\n");
915 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, virt_addr"
916 " = 0x%x, Offset = 0x%zx\n", log_addr
, virt_addr
,
921 /* Only delete the old entry when the new entry is ready */
922 old_addr
= part
->VirtualBlockMap
[sector
+i
];
923 if (old_addr
!= 0xffffffff) {
924 part
->VirtualBlockMap
[sector
+i
] = 0xffffffff;
925 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
926 if (set_bam_entry(part
, old_addr
, 0))
930 /* Finally, set up the new pointers */
931 if (set_bam_entry(part
, log_addr
, virt_addr
))
933 part
->VirtualBlockMap
[sector
+i
] = log_addr
;
934 part
->EUNInfo
[part
->bam_index
].Deleted
--;
936 buffer
+= SECTOR_SIZE
;
937 virt_addr
+= SECTOR_SIZE
;
942 static int ftl_getgeo(struct mtd_blktrans_dev
*dev
, struct hd_geometry
*geo
)
944 partition_t
*part
= (void *)dev
;
947 /* Sort of arbitrary: round size down to 4KiB boundary */
948 sect
= le32_to_cpu(part
->header
.FormattedSize
)/SECTOR_SIZE
;
952 geo
->cylinders
= sect
>> 3;
957 static int ftl_readsect(struct mtd_blktrans_dev
*dev
,
958 unsigned long block
, char *buf
)
960 return ftl_read((void *)dev
, buf
, block
, 1);
963 static int ftl_writesect(struct mtd_blktrans_dev
*dev
,
964 unsigned long block
, char *buf
)
966 return ftl_write((void *)dev
, buf
, block
, 1);
969 static int ftl_discardsect(struct mtd_blktrans_dev
*dev
,
970 unsigned long sector
, unsigned nr_sects
)
972 partition_t
*part
= (void *)dev
;
973 uint32_t bsize
= 1 << part
->header
.EraseUnitSize
;
975 pr_debug("FTL erase sector %ld for %d sectors\n",
979 uint32_t old_addr
= part
->VirtualBlockMap
[sector
];
980 if (old_addr
!= 0xffffffff) {
981 part
->VirtualBlockMap
[sector
] = 0xffffffff;
982 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
983 if (set_bam_entry(part
, old_addr
, 0))
992 /*====================================================================*/
994 static void ftl_freepart(partition_t
*part
)
996 vfree(part
->VirtualBlockMap
);
997 part
->VirtualBlockMap
= NULL
;
998 kfree(part
->EUNInfo
);
999 part
->EUNInfo
= NULL
;
1000 kfree(part
->XferInfo
);
1001 part
->XferInfo
= NULL
;
1002 kfree(part
->bam_cache
);
1003 part
->bam_cache
= NULL
;
1004 } /* ftl_freepart */
1006 static void ftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
1008 partition_t
*partition
;
1010 partition
= kzalloc(sizeof(partition_t
), GFP_KERNEL
);
1013 printk(KERN_WARNING
"No memory to scan for FTL on %s\n",
1018 partition
->mbd
.mtd
= mtd
;
1020 if ((scan_header(partition
) == 0) &&
1021 (build_maps(partition
) == 0)) {
1023 partition
->state
= FTL_FORMATTED
;
1025 printk(KERN_INFO
"ftl_cs: opening %d KiB FTL partition\n",
1026 le32_to_cpu(partition
->header
.FormattedSize
) >> 10);
1028 partition
->mbd
.size
= le32_to_cpu(partition
->header
.FormattedSize
) >> 9;
1030 partition
->mbd
.tr
= tr
;
1031 partition
->mbd
.devnum
= -1;
1032 if (!add_mtd_blktrans_dev((void *)partition
))
1039 static void ftl_remove_dev(struct mtd_blktrans_dev
*dev
)
1041 del_mtd_blktrans_dev(dev
);
1042 ftl_freepart((partition_t
*)dev
);
1045 static struct mtd_blktrans_ops ftl_tr
= {
1048 .part_bits
= PART_BITS
,
1049 .blksize
= SECTOR_SIZE
,
1050 .readsect
= ftl_readsect
,
1051 .writesect
= ftl_writesect
,
1052 .discard
= ftl_discardsect
,
1053 .getgeo
= ftl_getgeo
,
1054 .add_mtd
= ftl_add_mtd
,
1055 .remove_dev
= ftl_remove_dev
,
1056 .owner
= THIS_MODULE
,
1059 static int __init
init_ftl(void)
1061 return register_mtd_blktrans(&ftl_tr
);
1064 static void __exit
cleanup_ftl(void)
1066 deregister_mtd_blktrans(&ftl_tr
);
1069 module_init(init_ftl
);
1070 module_exit(cleanup_ftl
);
1073 MODULE_LICENSE("Dual MPL/GPL");
1074 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1075 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");