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
;
134 region_info_t region
;
135 memory_handle_t handle
;
139 /* Partition state flags */
140 #define FTL_FORMATTED 0x01
142 /* Transfer unit states */
143 #define XFER_UNKNOWN 0x00
144 #define XFER_ERASING 0x01
145 #define XFER_ERASED 0x02
146 #define XFER_PREPARED 0x03
147 #define XFER_FAILED 0x04
149 /*====================================================================*/
152 static void ftl_erase_callback(struct erase_info
*done
);
155 /*======================================================================
157 Scan_header() checks to see if a memory region contains an FTL
158 partition. build_maps() reads all the erase unit headers, builds
159 the erase unit map, and then builds the virtual page map.
161 ======================================================================*/
163 static int scan_header(partition_t
*part
)
165 erase_unit_header_t header
;
166 loff_t offset
, max_offset
;
169 part
->header
.FormattedSize
= 0;
170 max_offset
= (0x100000<part
->mbd
.mtd
->size
)?0x100000:part
->mbd
.mtd
->size
;
171 /* Search first megabyte for a valid FTL header */
173 (offset
+ sizeof(header
)) < max_offset
;
174 offset
+= part
->mbd
.mtd
->erasesize
? : 0x2000) {
176 err
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(header
), &ret
,
177 (unsigned char *)&header
);
182 if (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0) break;
185 if (offset
== max_offset
) {
186 printk(KERN_NOTICE
"ftl_cs: FTL header not found.\n");
189 if (header
.BlockSize
!= 9 ||
190 (header
.EraseUnitSize
< 10) || (header
.EraseUnitSize
> 31) ||
191 (header
.NumTransferUnits
>= le16_to_cpu(header
.NumEraseUnits
))) {
192 printk(KERN_NOTICE
"ftl_cs: FTL header corrupt!\n");
195 if ((1 << header
.EraseUnitSize
) != part
->mbd
.mtd
->erasesize
) {
196 printk(KERN_NOTICE
"ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
197 1 << header
.EraseUnitSize
,part
->mbd
.mtd
->erasesize
);
200 part
->header
= header
;
204 static int build_maps(partition_t
*part
)
206 erase_unit_header_t header
;
207 u_int16_t xvalid
, xtrans
, i
;
209 int hdr_ok
, ret
= -1;
213 /* Set up erase unit maps */
214 part
->DataUnits
= le16_to_cpu(part
->header
.NumEraseUnits
) -
215 part
->header
.NumTransferUnits
;
216 part
->EUNInfo
= kmalloc(part
->DataUnits
* sizeof(struct eun_info_t
),
220 for (i
= 0; i
< part
->DataUnits
; i
++)
221 part
->EUNInfo
[i
].Offset
= 0xffffffff;
223 kmalloc(part
->header
.NumTransferUnits
* sizeof(struct xfer_info_t
),
229 for (i
= 0; i
< le16_to_cpu(part
->header
.NumEraseUnits
); i
++) {
230 offset
= ((i
+ le16_to_cpu(part
->header
.FirstPhysicalEUN
))
231 << part
->header
.EraseUnitSize
);
232 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(header
), &retval
,
233 (unsigned char *)&header
);
239 /* Is this a transfer partition? */
240 hdr_ok
= (strcmp(header
.DataOrgTuple
+3, "FTL100") == 0);
241 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) < part
->DataUnits
) &&
242 (part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
== 0xffffffff)) {
243 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].Offset
= offset
;
244 part
->EUNInfo
[le16_to_cpu(header
.LogicalEUN
)].EraseCount
=
245 le32_to_cpu(header
.EraseCount
);
248 if (xtrans
== part
->header
.NumTransferUnits
) {
249 printk(KERN_NOTICE
"ftl_cs: format error: too many "
250 "transfer units!\n");
253 if (hdr_ok
&& (le16_to_cpu(header
.LogicalEUN
) == 0xffff)) {
254 part
->XferInfo
[xtrans
].state
= XFER_PREPARED
;
255 part
->XferInfo
[xtrans
].EraseCount
= le32_to_cpu(header
.EraseCount
);
257 part
->XferInfo
[xtrans
].state
= XFER_UNKNOWN
;
258 /* Pick anything reasonable for the erase count */
259 part
->XferInfo
[xtrans
].EraseCount
=
260 le32_to_cpu(part
->header
.EraseCount
);
262 part
->XferInfo
[xtrans
].Offset
= offset
;
266 /* Check for format trouble */
267 header
= part
->header
;
268 if ((xtrans
!= header
.NumTransferUnits
) ||
269 (xvalid
+xtrans
!= le16_to_cpu(header
.NumEraseUnits
))) {
270 printk(KERN_NOTICE
"ftl_cs: format error: erase units "
275 /* Set up virtual page map */
276 blocks
= le32_to_cpu(header
.FormattedSize
) >> header
.BlockSize
;
277 part
->VirtualBlockMap
= vmalloc(blocks
* sizeof(u_int32_t
));
278 if (!part
->VirtualBlockMap
)
281 memset(part
->VirtualBlockMap
, 0xff, blocks
* sizeof(u_int32_t
));
282 part
->BlocksPerUnit
= (1 << header
.EraseUnitSize
) >> header
.BlockSize
;
284 part
->bam_cache
= kmalloc(part
->BlocksPerUnit
* sizeof(u_int32_t
),
286 if (!part
->bam_cache
)
287 goto out_VirtualBlockMap
;
289 part
->bam_index
= 0xffff;
292 for (i
= 0; i
< part
->DataUnits
; i
++) {
293 part
->EUNInfo
[i
].Free
= 0;
294 part
->EUNInfo
[i
].Deleted
= 0;
295 offset
= part
->EUNInfo
[i
].Offset
+ le32_to_cpu(header
.BAMOffset
);
297 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
,
298 part
->BlocksPerUnit
* sizeof(u_int32_t
), &retval
,
299 (unsigned char *)part
->bam_cache
);
304 for (j
= 0; j
< part
->BlocksPerUnit
; j
++) {
305 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[j
]))) {
306 part
->EUNInfo
[i
].Free
++;
308 } else if ((BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[j
])) == BLOCK_DATA
) &&
309 (BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
])) < blocks
))
310 part
->VirtualBlockMap
[BLOCK_NUMBER(le32_to_cpu(part
->bam_cache
[j
]))] =
311 (i
<< header
.EraseUnitSize
) + (j
<< header
.BlockSize
);
312 else if (BLOCK_DELETED(le32_to_cpu(part
->bam_cache
[j
])))
313 part
->EUNInfo
[i
].Deleted
++;
321 kfree(part
->bam_cache
);
323 vfree(part
->VirtualBlockMap
);
325 kfree(part
->XferInfo
);
327 kfree(part
->EUNInfo
);
332 /*======================================================================
334 Erase_xfer() schedules an asynchronous erase operation for a
337 ======================================================================*/
339 static int erase_xfer(partition_t
*part
,
343 struct xfer_info_t
*xfer
;
344 struct erase_info
*erase
;
346 xfer
= &part
->XferInfo
[xfernum
];
347 DEBUG(1, "ftl_cs: erasing xfer unit at 0x%x\n", xfer
->Offset
);
348 xfer
->state
= XFER_ERASING
;
350 /* Is there a free erase slot? Always in MTD. */
353 erase
=kmalloc(sizeof(struct erase_info
), GFP_KERNEL
);
357 erase
->mtd
= part
->mbd
.mtd
;
358 erase
->callback
= ftl_erase_callback
;
359 erase
->addr
= xfer
->Offset
;
360 erase
->len
= 1 << part
->header
.EraseUnitSize
;
361 erase
->priv
= (u_long
)part
;
363 ret
= part
->mbd
.mtd
->erase(part
->mbd
.mtd
, erase
);
373 /*======================================================================
375 Prepare_xfer() takes a freshly erased transfer unit and gives
376 it an appropriate header.
378 ======================================================================*/
380 static void ftl_erase_callback(struct erase_info
*erase
)
383 struct xfer_info_t
*xfer
;
386 /* Look up the transfer unit */
387 part
= (partition_t
*)(erase
->priv
);
389 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++)
390 if (part
->XferInfo
[i
].Offset
== erase
->addr
) break;
392 if (i
== part
->header
.NumTransferUnits
) {
393 printk(KERN_NOTICE
"ftl_cs: internal error: "
394 "erase lookup failed!\n");
398 xfer
= &part
->XferInfo
[i
];
399 if (erase
->state
== MTD_ERASE_DONE
)
400 xfer
->state
= XFER_ERASED
;
402 xfer
->state
= XFER_FAILED
;
403 printk(KERN_NOTICE
"ftl_cs: erase failed: state = %d\n",
409 } /* ftl_erase_callback */
411 static int prepare_xfer(partition_t
*part
, int i
)
413 erase_unit_header_t header
;
414 struct xfer_info_t
*xfer
;
420 xfer
= &part
->XferInfo
[i
];
421 xfer
->state
= XFER_FAILED
;
423 DEBUG(1, "ftl_cs: preparing xfer unit at 0x%x\n", xfer
->Offset
);
425 /* Write the transfer unit header */
426 header
= part
->header
;
427 header
.LogicalEUN
= cpu_to_le16(0xffff);
428 header
.EraseCount
= cpu_to_le32(xfer
->EraseCount
);
430 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
, sizeof(header
),
431 &retlen
, (u_char
*)&header
);
437 /* Write the BAM stub */
438 nbam
= (part
->BlocksPerUnit
* sizeof(u_int32_t
) +
439 le32_to_cpu(part
->header
.BAMOffset
) + SECTOR_SIZE
- 1) / SECTOR_SIZE
;
441 offset
= xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
442 ctl
= cpu_to_le32(BLOCK_CONTROL
);
444 for (i
= 0; i
< nbam
; i
++, offset
+= sizeof(u_int32_t
)) {
446 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
447 &retlen
, (u_char
*)&ctl
);
452 xfer
->state
= XFER_PREPARED
;
457 /*======================================================================
459 Copy_erase_unit() takes a full erase block and a transfer unit,
460 copies everything to the transfer unit, then swaps the block
463 All data blocks are copied to the corresponding blocks in the
464 target unit, so the virtual block map does not need to be
467 ======================================================================*/
469 static int copy_erase_unit(partition_t
*part
, u_int16_t srcunit
,
472 u_char buf
[SECTOR_SIZE
];
473 struct eun_info_t
*eun
;
474 struct xfer_info_t
*xfer
;
475 u_int32_t src
, dest
, free
, i
;
480 u_int16_t srcunitswap
= cpu_to_le16(srcunit
);
482 eun
= &part
->EUNInfo
[srcunit
];
483 xfer
= &part
->XferInfo
[xferunit
];
484 DEBUG(2, "ftl_cs: copying block 0x%x to 0x%x\n",
485 eun
->Offset
, xfer
->Offset
);
488 /* Read current BAM */
489 if (part
->bam_index
!= srcunit
) {
491 offset
= eun
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
);
493 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
,
494 part
->BlocksPerUnit
* sizeof(u_int32_t
),
495 &retlen
, (u_char
*) (part
->bam_cache
));
497 /* mark the cache bad, in case we get an error later */
498 part
->bam_index
= 0xffff;
501 printk( KERN_WARNING
"ftl: Failed to read BAM cache in copy_erase_unit()!\n");
506 /* Write the LogicalEUN for the transfer unit */
507 xfer
->state
= XFER_UNKNOWN
;
508 offset
= xfer
->Offset
+ 20; /* Bad! */
509 unit
= cpu_to_le16(0x7fff);
511 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int16_t
),
512 &retlen
, (u_char
*) &unit
);
515 printk( KERN_WARNING
"ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
519 /* Copy all data blocks from source unit to transfer unit */
520 src
= eun
->Offset
; dest
= xfer
->Offset
;
524 for (i
= 0; i
< part
->BlocksPerUnit
; i
++) {
525 switch (BLOCK_TYPE(le32_to_cpu(part
->bam_cache
[i
]))) {
527 /* This gets updated later */
530 case BLOCK_REPLACEMENT
:
531 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, src
, SECTOR_SIZE
,
532 &retlen
, (u_char
*) buf
);
534 printk(KERN_WARNING
"ftl: Error reading old xfer unit in copy_erase_unit\n");
539 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, dest
, SECTOR_SIZE
,
540 &retlen
, (u_char
*) buf
);
542 printk(KERN_WARNING
"ftl: Error writing new xfer unit in copy_erase_unit\n");
548 /* All other blocks must be free */
549 part
->bam_cache
[i
] = cpu_to_le32(0xffffffff);
557 /* Write the BAM to the transfer unit */
558 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
559 part
->BlocksPerUnit
* sizeof(int32_t), &retlen
,
560 (u_char
*)part
->bam_cache
);
562 printk( KERN_WARNING
"ftl: Error writing BAM in copy_erase_unit\n");
567 /* All clear? Then update the LogicalEUN again */
568 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, xfer
->Offset
+ 20, sizeof(u_int16_t
),
569 &retlen
, (u_char
*)&srcunitswap
);
572 printk(KERN_WARNING
"ftl: Error writing new LogicalEUN in copy_erase_unit\n");
577 /* Update the maps and usage stats*/
578 i
= xfer
->EraseCount
;
579 xfer
->EraseCount
= eun
->EraseCount
;
582 xfer
->Offset
= eun
->Offset
;
584 part
->FreeTotal
-= eun
->Free
;
585 part
->FreeTotal
+= free
;
589 /* Now, the cache should be valid for the new block */
590 part
->bam_index
= srcunit
;
593 } /* copy_erase_unit */
595 /*======================================================================
597 reclaim_block() picks a full erase unit and a transfer unit and
598 then calls copy_erase_unit() to copy one to the other. Then, it
599 schedules an erase on the expired block.
601 What's a good way to decide which transfer unit and which erase
602 unit to use? Beats me. My way is to always pick the transfer
603 unit with the fewest erases, and usually pick the data unit with
604 the most deleted blocks. But with a small probability, pick the
605 oldest data unit instead. This means that we generally postpone
606 the next reclaimation as long as possible, but shuffle static
607 stuff around a bit for wear leveling.
609 ======================================================================*/
611 static int reclaim_block(partition_t
*part
)
613 u_int16_t i
, eun
, xfer
;
617 DEBUG(0, "ftl_cs: reclaiming space...\n");
618 DEBUG(3, "NumTransferUnits == %x\n", part
->header
.NumTransferUnits
);
619 /* Pick the least erased transfer unit */
620 best
= 0xffffffff; xfer
= 0xffff;
623 for (i
= 0; i
< part
->header
.NumTransferUnits
; i
++) {
625 if (part
->XferInfo
[i
].state
== XFER_UNKNOWN
) {
626 DEBUG(3,"XferInfo[%d].state == XFER_UNKNOWN\n",i
);
630 if (part
->XferInfo
[i
].state
== XFER_ERASING
) {
631 DEBUG(3,"XferInfo[%d].state == XFER_ERASING\n",i
);
635 else if (part
->XferInfo
[i
].state
== XFER_ERASED
) {
636 DEBUG(3,"XferInfo[%d].state == XFER_ERASED\n",i
);
638 prepare_xfer(part
, i
);
640 if (part
->XferInfo
[i
].state
== XFER_PREPARED
) {
641 DEBUG(3,"XferInfo[%d].state == XFER_PREPARED\n",i
);
643 if (part
->XferInfo
[i
].EraseCount
<= best
) {
644 best
= part
->XferInfo
[i
].EraseCount
;
649 DEBUG(3,"XferInfo[%d].state == %x\n",i
, part
->XferInfo
[i
].state
);
652 if (xfer
== 0xffff) {
654 DEBUG(1, "ftl_cs: waiting for transfer "
655 "unit to be prepared...\n");
656 if (part
->mbd
.mtd
->sync
)
657 part
->mbd
.mtd
->sync(part
->mbd
.mtd
);
661 printk(KERN_NOTICE
"ftl_cs: reclaim failed: no "
662 "suitable transfer units!\n");
664 DEBUG(1, "ftl_cs: reclaim failed: no "
665 "suitable transfer units!\n");
670 } while (xfer
== 0xffff);
673 if ((jiffies
% shuffle_freq
) == 0) {
674 DEBUG(1, "ftl_cs: recycling freshest block...\n");
676 for (i
= 0; i
< part
->DataUnits
; i
++)
677 if (part
->EUNInfo
[i
].EraseCount
<= best
) {
678 best
= part
->EUNInfo
[i
].EraseCount
;
683 for (i
= 0; i
< part
->DataUnits
; i
++)
684 if (part
->EUNInfo
[i
].Deleted
>= best
) {
685 best
= part
->EUNInfo
[i
].Deleted
;
691 printk(KERN_NOTICE
"ftl_cs: reclaim failed: "
692 "no free blocks!\n");
694 DEBUG(1,"ftl_cs: reclaim failed: "
695 "no free blocks!\n");
700 ret
= copy_erase_unit(part
, eun
, xfer
);
702 erase_xfer(part
, xfer
);
704 printk(KERN_NOTICE
"ftl_cs: copy_erase_unit failed!\n");
706 } /* reclaim_block */
708 /*======================================================================
710 Find_free() searches for a free block. If necessary, it updates
711 the BAM cache for the erase unit containing the free block. It
712 returns the block index -- the erase unit is just the currently
713 cached unit. If there are no free blocks, it returns 0 -- this
714 is never a valid data block because it contains the header.
716 ======================================================================*/
719 static void dump_lists(partition_t
*part
)
722 printk(KERN_DEBUG
"ftl_cs: Free total = %d\n", part
->FreeTotal
);
723 for (i
= 0; i
< part
->DataUnits
; i
++)
724 printk(KERN_DEBUG
"ftl_cs: unit %d: %d phys, %d free, "
726 part
->EUNInfo
[i
].Offset
>> part
->header
.EraseUnitSize
,
727 part
->EUNInfo
[i
].Free
, part
->EUNInfo
[i
].Deleted
);
731 static u_int32_t
find_free(partition_t
*part
)
738 /* Find an erase unit with some free space */
739 stop
= (part
->bam_index
== 0xffff) ? 0 : part
->bam_index
;
742 if (part
->EUNInfo
[eun
].Free
!= 0) break;
743 /* Wrap around at end of table */
744 if (++eun
== part
->DataUnits
) eun
= 0;
745 } while (eun
!= stop
);
747 if (part
->EUNInfo
[eun
].Free
== 0)
750 /* Is this unit's BAM cached? */
751 if (eun
!= part
->bam_index
) {
752 /* Invalidate cache */
753 part
->bam_index
= 0xffff;
755 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
,
756 part
->EUNInfo
[eun
].Offset
+ le32_to_cpu(part
->header
.BAMOffset
),
757 part
->BlocksPerUnit
* sizeof(u_int32_t
),
758 &retlen
, (u_char
*) (part
->bam_cache
));
761 printk(KERN_WARNING
"ftl: Error reading BAM in find_free\n");
764 part
->bam_index
= eun
;
767 /* Find a free block */
768 for (blk
= 0; blk
< part
->BlocksPerUnit
; blk
++)
769 if (BLOCK_FREE(le32_to_cpu(part
->bam_cache
[blk
]))) break;
770 if (blk
== part
->BlocksPerUnit
) {
776 printk(KERN_NOTICE
"ftl_cs: bad free list!\n");
779 DEBUG(2, "ftl_cs: found free block at %d in %d\n", blk
, eun
);
785 /*======================================================================
787 Read a series of sectors from an FTL partition.
789 ======================================================================*/
791 static int ftl_read(partition_t
*part
, caddr_t buffer
,
792 u_long sector
, u_long nblocks
)
794 u_int32_t log_addr
, bsize
;
797 size_t offset
, retlen
;
799 DEBUG(2, "ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
800 part
, sector
, nblocks
);
801 if (!(part
->state
& FTL_FORMATTED
)) {
802 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
805 bsize
= 1 << part
->header
.EraseUnitSize
;
807 for (i
= 0; i
< nblocks
; i
++) {
808 if (((sector
+i
) * SECTOR_SIZE
) >= le32_to_cpu(part
->header
.FormattedSize
)) {
809 printk(KERN_NOTICE
"ftl_cs: bad read offset\n");
812 log_addr
= part
->VirtualBlockMap
[sector
+i
];
813 if (log_addr
== 0xffffffff)
814 memset(buffer
, 0, SECTOR_SIZE
);
816 offset
= (part
->EUNInfo
[log_addr
/ bsize
].Offset
817 + (log_addr
% bsize
));
818 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, SECTOR_SIZE
,
819 &retlen
, (u_char
*) buffer
);
822 printk(KERN_WARNING
"Error reading MTD device in ftl_read()\n");
826 buffer
+= SECTOR_SIZE
;
831 /*======================================================================
833 Write a series of sectors to an FTL partition
835 ======================================================================*/
837 static int set_bam_entry(partition_t
*part
, u_int32_t log_addr
,
840 u_int32_t bsize
, blk
, le_virt_addr
;
846 size_t retlen
, offset
;
848 DEBUG(2, "ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
849 part
, log_addr
, virt_addr
);
850 bsize
= 1 << part
->header
.EraseUnitSize
;
851 eun
= log_addr
/ bsize
;
852 blk
= (log_addr
% bsize
) / SECTOR_SIZE
;
853 offset
= (part
->EUNInfo
[eun
].Offset
+ blk
* sizeof(u_int32_t
) +
854 le32_to_cpu(part
->header
.BAMOffset
));
857 ret
= part
->mbd
.mtd
->read(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
858 &retlen
, (u_char
*)&old_addr
);
860 printk(KERN_WARNING
"ftl: Error reading old_addr in set_bam_entry: %d\n",ret
);
863 old_addr
= le32_to_cpu(old_addr
);
865 if (((virt_addr
== 0xfffffffe) && !BLOCK_FREE(old_addr
)) ||
866 ((virt_addr
== 0) && (BLOCK_TYPE(old_addr
) != BLOCK_DATA
)) ||
867 (!BLOCK_DELETED(virt_addr
) && (old_addr
!= 0xfffffffe))) {
870 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() inconsistency!\n");
871 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, old = 0x%x"
872 ", new = 0x%x\n", log_addr
, old_addr
, virt_addr
);
877 le_virt_addr
= cpu_to_le32(virt_addr
);
878 if (part
->bam_index
== eun
) {
880 if (le32_to_cpu(part
->bam_cache
[blk
]) != old_addr
) {
883 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() "
885 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, cache"
887 le32_to_cpu(part
->bam_cache
[blk
]), old_addr
);
892 part
->bam_cache
[blk
] = le_virt_addr
;
894 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, sizeof(u_int32_t
),
895 &retlen
, (u_char
*)&le_virt_addr
);
898 printk(KERN_NOTICE
"ftl_cs: set_bam_entry() failed!\n");
899 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, new = 0x%x\n",
900 log_addr
, virt_addr
);
903 } /* set_bam_entry */
905 static int ftl_write(partition_t
*part
, caddr_t buffer
,
906 u_long sector
, u_long nblocks
)
908 u_int32_t bsize
, log_addr
, virt_addr
, old_addr
, blk
;
911 size_t retlen
, offset
;
913 DEBUG(2, "ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
914 part
, sector
, nblocks
);
915 if (!(part
->state
& FTL_FORMATTED
)) {
916 printk(KERN_NOTICE
"ftl_cs: bad partition\n");
919 /* See if we need to reclaim space, before we start */
920 while (part
->FreeTotal
< nblocks
) {
921 ret
= reclaim_block(part
);
926 bsize
= 1 << part
->header
.EraseUnitSize
;
928 virt_addr
= sector
* SECTOR_SIZE
| BLOCK_DATA
;
929 for (i
= 0; i
< nblocks
; i
++) {
930 if (virt_addr
>= le32_to_cpu(part
->header
.FormattedSize
)) {
931 printk(KERN_NOTICE
"ftl_cs: bad write offset\n");
935 /* Grab a free block */
936 blk
= find_free(part
);
940 printk(KERN_NOTICE
"ftl_cs: internal error: "
941 "no free blocks!\n");
945 /* Tag the BAM entry, and write the new block */
946 log_addr
= part
->bam_index
* bsize
+ blk
* SECTOR_SIZE
;
947 part
->EUNInfo
[part
->bam_index
].Free
--;
949 if (set_bam_entry(part
, log_addr
, 0xfffffffe))
951 part
->EUNInfo
[part
->bam_index
].Deleted
++;
952 offset
= (part
->EUNInfo
[part
->bam_index
].Offset
+
954 ret
= part
->mbd
.mtd
->write(part
->mbd
.mtd
, offset
, SECTOR_SIZE
, &retlen
,
958 printk(KERN_NOTICE
"ftl_cs: block write failed!\n");
959 printk(KERN_NOTICE
"ftl_cs: log_addr = 0x%x, virt_addr"
960 " = 0x%x, Offset = 0x%zx\n", log_addr
, virt_addr
,
965 /* Only delete the old entry when the new entry is ready */
966 old_addr
= part
->VirtualBlockMap
[sector
+i
];
967 if (old_addr
!= 0xffffffff) {
968 part
->VirtualBlockMap
[sector
+i
] = 0xffffffff;
969 part
->EUNInfo
[old_addr
/bsize
].Deleted
++;
970 if (set_bam_entry(part
, old_addr
, 0))
974 /* Finally, set up the new pointers */
975 if (set_bam_entry(part
, log_addr
, virt_addr
))
977 part
->VirtualBlockMap
[sector
+i
] = log_addr
;
978 part
->EUNInfo
[part
->bam_index
].Deleted
--;
980 buffer
+= SECTOR_SIZE
;
981 virt_addr
+= SECTOR_SIZE
;
986 static int ftl_getgeo(struct mtd_blktrans_dev
*dev
, struct hd_geometry
*geo
)
988 partition_t
*part
= (void *)dev
;
991 /* Sort of arbitrary: round size down to 4KiB boundary */
992 sect
= le32_to_cpu(part
->header
.FormattedSize
)/SECTOR_SIZE
;
996 geo
->cylinders
= sect
>> 3;
1001 static int ftl_readsect(struct mtd_blktrans_dev
*dev
,
1002 unsigned long block
, char *buf
)
1004 return ftl_read((void *)dev
, buf
, block
, 1);
1007 static int ftl_writesect(struct mtd_blktrans_dev
*dev
,
1008 unsigned long block
, char *buf
)
1010 return ftl_write((void *)dev
, buf
, block
, 1);
1013 /*====================================================================*/
1015 static void ftl_freepart(partition_t
*part
)
1017 vfree(part
->VirtualBlockMap
);
1018 part
->VirtualBlockMap
= NULL
;
1019 kfree(part
->VirtualPageMap
);
1020 part
->VirtualPageMap
= NULL
;
1021 kfree(part
->EUNInfo
);
1022 part
->EUNInfo
= NULL
;
1023 kfree(part
->XferInfo
);
1024 part
->XferInfo
= NULL
;
1025 kfree(part
->bam_cache
);
1026 part
->bam_cache
= NULL
;
1027 } /* ftl_freepart */
1029 static void ftl_add_mtd(struct mtd_blktrans_ops
*tr
, struct mtd_info
*mtd
)
1031 partition_t
*partition
;
1033 partition
= kzalloc(sizeof(partition_t
), GFP_KERNEL
);
1036 printk(KERN_WARNING
"No memory to scan for FTL on %s\n",
1041 partition
->mbd
.mtd
= mtd
;
1043 if ((scan_header(partition
) == 0) &&
1044 (build_maps(partition
) == 0)) {
1046 partition
->state
= FTL_FORMATTED
;
1048 printk(KERN_INFO
"ftl_cs: opening %d KiB FTL partition\n",
1049 le32_to_cpu(partition
->header
.FormattedSize
) >> 10);
1051 partition
->mbd
.size
= le32_to_cpu(partition
->header
.FormattedSize
) >> 9;
1053 partition
->mbd
.tr
= tr
;
1054 partition
->mbd
.devnum
= -1;
1055 if (!add_mtd_blktrans_dev((void *)partition
))
1059 ftl_freepart(partition
);
1063 static void ftl_remove_dev(struct mtd_blktrans_dev
*dev
)
1065 del_mtd_blktrans_dev(dev
);
1066 ftl_freepart((partition_t
*)dev
);
1070 static struct mtd_blktrans_ops ftl_tr
= {
1073 .part_bits
= PART_BITS
,
1074 .blksize
= SECTOR_SIZE
,
1075 .readsect
= ftl_readsect
,
1076 .writesect
= ftl_writesect
,
1077 .getgeo
= ftl_getgeo
,
1078 .add_mtd
= ftl_add_mtd
,
1079 .remove_dev
= ftl_remove_dev
,
1080 .owner
= THIS_MODULE
,
1083 static int init_ftl(void)
1085 DEBUG(0, "$Id: ftl.c,v 1.59 2005/11/29 14:48:31 gleixner Exp $\n");
1087 return register_mtd_blktrans(&ftl_tr
);
1090 static void __exit
cleanup_ftl(void)
1092 deregister_mtd_blktrans(&ftl_tr
);
1095 module_init(init_ftl
);
1096 module_exit(cleanup_ftl
);
1099 MODULE_LICENSE("Dual MPL/GPL");
1100 MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1101 MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");