blob: ef6ad2551d574c5037767b1ff9fdf37300f3490c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* This version ported to the Linux-MTD system by dwmw2@infradead.org
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
4 * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
5 *
6 * Based on:
7 */
8/*======================================================================
9
10 A Flash Translation Layer memory card driver
11
12 This driver implements a disk-like block device driver with an
13 apparent block size of 512 bytes for flash memory cards.
14
15 ftl_cs.c 1.62 2000/02/01 00:59:04
16
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/
21
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.
26
27 The initial developer of the original code is David A. Hinds
28 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
David Woodhousea1452a32010-08-08 20:58:20 +010029 are Copyright © 1999 David A. Hinds. All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
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.
41
42 LEGAL NOTE: The FTL format is patented by M-Systems. They have
43 granted a license for its use with PCMCIA devices:
44
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."
51
52 Use of the FTL format for non-PCMCIA applications may be an
53 infringement of these patents. For additional information,
Justin P. Mattock631dd1a2010-10-18 11:03:14 +020054 contact M-Systems directly. M-Systems since acquired by Sandisk.
Thomas Gleixner97894cd2005-11-07 11:15:26 +000055
Linus Torvalds1da177e2005-04-16 15:20:36 -070056======================================================================*/
57#include <linux/mtd/blktrans.h>
58#include <linux/module.h>
59#include <linux/mtd/mtd.h>
60/*#define PSYCHO_DEBUG */
61
62#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#include <linux/ptrace.h>
64#include <linux/slab.h>
65#include <linux/string.h>
66#include <linux/timer.h>
67#include <linux/major.h>
68#include <linux/fs.h>
69#include <linux/init.h>
70#include <linux/hdreg.h>
71#include <linux/vmalloc.h>
72#include <linux/blkpg.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080073#include <linux/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
75#include <linux/mtd/ftl.h>
76
77/*====================================================================*/
78
79/* Parameters that can be set with 'insmod' */
80static int shuffle_freq = 50;
81module_param(shuffle_freq, int, 0);
82
83/*====================================================================*/
84
85/* Major device # for FTL device */
86#ifndef FTL_MAJOR
87#define FTL_MAJOR 44
88#endif
89
90
91/*====================================================================*/
92
93/* Maximum number of separate memory devices we'll allow */
94#define MAX_DEV 4
95
96/* Maximum number of regions per device */
97#define MAX_REGION 4
98
99/* Maximum number of partitions in an FTL region */
100#define PART_BITS 4
101
102/* Maximum number of outstanding erase requests per socket */
103#define MAX_ERASE 8
104
105/* Sector size -- shouldn't need to change */
106#define SECTOR_SIZE 512
107
108
109/* Each memory region corresponds to a minor device */
110typedef struct partition_t {
111 struct mtd_blktrans_dev mbd;
David Woodhouse3854be72008-12-10 14:06:42 +0000112 uint32_t state;
113 uint32_t *VirtualBlockMap;
David Woodhouse3854be72008-12-10 14:06:42 +0000114 uint32_t FreeTotal;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 struct eun_info_t {
David Woodhouse3854be72008-12-10 14:06:42 +0000116 uint32_t Offset;
117 uint32_t EraseCount;
118 uint32_t Free;
119 uint32_t Deleted;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 } *EUNInfo;
121 struct xfer_info_t {
David Woodhouse3854be72008-12-10 14:06:42 +0000122 uint32_t Offset;
123 uint32_t EraseCount;
124 uint16_t state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 } *XferInfo;
David Woodhouse3854be72008-12-10 14:06:42 +0000126 uint16_t bam_index;
127 uint32_t *bam_cache;
128 uint16_t DataUnits;
129 uint32_t BlocksPerUnit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 erase_unit_header_t header;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131} partition_t;
132
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133/* Partition state flags */
134#define FTL_FORMATTED 0x01
135
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
142
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143/*======================================================================
144
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.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000148
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149======================================================================*/
150
151static int scan_header(partition_t *part)
152{
153 erase_unit_header_t header;
154 loff_t offset, max_offset;
155 size_t ret;
156 int err;
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 */
160 for (offset = 0;
161 (offset + sizeof(header)) < max_offset;
162 offset += part->mbd.mtd->erasesize ? : 0x2000) {
163
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200164 err = mtd_read(part->mbd.mtd, offset, sizeof(header), &ret,
165 (unsigned char *)&header);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000166
167 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 return err;
169
170 if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
171 }
172
173 if (offset == max_offset) {
174 printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
175 return -ENOENT;
176 }
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");
181 return -1;
182 }
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);
186 return -1;
187 }
188 part->header = header;
189 return 0;
190}
191
192static int build_maps(partition_t *part)
193{
194 erase_unit_header_t header;
David Woodhouse3854be72008-12-10 14:06:42 +0000195 uint16_t xvalid, xtrans, i;
196 unsigned blocks, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 int hdr_ok, ret = -1;
198 ssize_t retval;
199 loff_t offset;
200
201 /* Set up erase unit maps */
202 part->DataUnits = le16_to_cpu(part->header.NumEraseUnits) -
203 part->header.NumTransferUnits;
204 part->EUNInfo = kmalloc(part->DataUnits * sizeof(struct eun_info_t),
205 GFP_KERNEL);
206 if (!part->EUNInfo)
207 goto out;
208 for (i = 0; i < part->DataUnits; i++)
209 part->EUNInfo[i].Offset = 0xffffffff;
210 part->XferInfo =
211 kmalloc(part->header.NumTransferUnits * sizeof(struct xfer_info_t),
212 GFP_KERNEL);
213 if (!part->XferInfo)
214 goto out_EUNInfo;
215
216 xvalid = xtrans = 0;
217 for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
218 offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
219 << part->header.EraseUnitSize);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200220 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
221 (unsigned char *)&header);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000222
223 if (ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 goto out_XferInfo;
225
226 ret = -1;
227 /* Is this a transfer partition? */
228 hdr_ok = (strcmp(header.DataOrgTuple+3, "FTL100") == 0);
229 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) < part->DataUnits) &&
230 (part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset == 0xffffffff)) {
231 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].Offset = offset;
232 part->EUNInfo[le16_to_cpu(header.LogicalEUN)].EraseCount =
233 le32_to_cpu(header.EraseCount);
234 xvalid++;
235 } else {
236 if (xtrans == part->header.NumTransferUnits) {
237 printk(KERN_NOTICE "ftl_cs: format error: too many "
238 "transfer units!\n");
239 goto out_XferInfo;
240 }
241 if (hdr_ok && (le16_to_cpu(header.LogicalEUN) == 0xffff)) {
242 part->XferInfo[xtrans].state = XFER_PREPARED;
243 part->XferInfo[xtrans].EraseCount = le32_to_cpu(header.EraseCount);
244 } else {
245 part->XferInfo[xtrans].state = XFER_UNKNOWN;
246 /* Pick anything reasonable for the erase count */
247 part->XferInfo[xtrans].EraseCount =
248 le32_to_cpu(part->header.EraseCount);
249 }
250 part->XferInfo[xtrans].Offset = offset;
251 xtrans++;
252 }
253 }
254 /* Check for format trouble */
255 header = part->header;
256 if ((xtrans != header.NumTransferUnits) ||
257 (xvalid+xtrans != le16_to_cpu(header.NumEraseUnits))) {
258 printk(KERN_NOTICE "ftl_cs: format error: erase units "
259 "don't add up!\n");
260 goto out_XferInfo;
261 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 /* Set up virtual page map */
264 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
David Woodhouse3854be72008-12-10 14:06:42 +0000265 part->VirtualBlockMap = vmalloc(blocks * sizeof(uint32_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 if (!part->VirtualBlockMap)
267 goto out_XferInfo;
268
David Woodhouse3854be72008-12-10 14:06:42 +0000269 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
271
David Woodhouse3854be72008-12-10 14:06:42 +0000272 part->bam_cache = kmalloc(part->BlocksPerUnit * sizeof(uint32_t),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 GFP_KERNEL);
274 if (!part->bam_cache)
275 goto out_VirtualBlockMap;
276
277 part->bam_index = 0xffff;
278 part->FreeTotal = 0;
279
280 for (i = 0; i < part->DataUnits; i++) {
281 part->EUNInfo[i].Free = 0;
282 part->EUNInfo[i].Deleted = 0;
283 offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000284
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200285 ret = mtd_read(part->mbd.mtd, offset,
286 part->BlocksPerUnit * sizeof(uint32_t), &retval,
287 (unsigned char *)part->bam_cache);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000288
289 if (ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 goto out_bam_cache;
291
292 for (j = 0; j < part->BlocksPerUnit; j++) {
293 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
294 part->EUNInfo[i].Free++;
295 part->FreeTotal++;
296 } else if ((BLOCK_TYPE(le32_to_cpu(part->bam_cache[j])) == BLOCK_DATA) &&
297 (BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j])) < blocks))
298 part->VirtualBlockMap[BLOCK_NUMBER(le32_to_cpu(part->bam_cache[j]))] =
299 (i << header.EraseUnitSize) + (j << header.BlockSize);
300 else if (BLOCK_DELETED(le32_to_cpu(part->bam_cache[j])))
301 part->EUNInfo[i].Deleted++;
302 }
303 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 ret = 0;
306 goto out;
307
308out_bam_cache:
309 kfree(part->bam_cache);
310out_VirtualBlockMap:
311 vfree(part->VirtualBlockMap);
312out_XferInfo:
313 kfree(part->XferInfo);
314out_EUNInfo:
315 kfree(part->EUNInfo);
316out:
317 return ret;
318} /* build_maps */
319
320/*======================================================================
321
322 Erase_xfer() schedules an asynchronous erase operation for a
323 transfer unit.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325======================================================================*/
326
327static int erase_xfer(partition_t *part,
David Woodhouse3854be72008-12-10 14:06:42 +0000328 uint16_t xfernum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 int ret;
331 struct xfer_info_t *xfer;
332 struct erase_info *erase;
333
334 xfer = &part->XferInfo[xfernum];
Brian Norris289c0522011-07-19 10:06:09 -0700335 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 xfer->state = XFER_ERASING;
337
338 /* Is there a free erase slot? Always in MTD. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000339
340
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000342 if (!erase)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 return -ENOMEM;
344
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 erase->addr = xfer->Offset;
346 erase->len = 1 << part->header.EraseUnitSize;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000347
Artem Bityutskiy7e1f0dc2011-12-23 15:25:39 +0200348 ret = mtd_erase(part->mbd.mtd, erase);
Boris Brezillon884cfd92018-02-12 22:03:09 +0100349 if (!ret) {
350 xfer->state = XFER_ERASED;
351 xfer->EraseCount++;
352 } else {
353 xfer->state = XFER_FAILED;
354 pr_notice("ftl_cs: erase failed: err = %d\n", ret);
355 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Boris Brezillon884cfd92018-02-12 22:03:09 +0100357 kfree(erase);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358
359 return ret;
360} /* erase_xfer */
361
362/*======================================================================
363
364 Prepare_xfer() takes a freshly erased transfer unit and gives
365 it an appropriate header.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367======================================================================*/
368
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369static int prepare_xfer(partition_t *part, int i)
370{
371 erase_unit_header_t header;
372 struct xfer_info_t *xfer;
373 int nbam, ret;
David Woodhouse3854be72008-12-10 14:06:42 +0000374 uint32_t ctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 ssize_t retlen;
376 loff_t offset;
377
378 xfer = &part->XferInfo[i];
379 xfer->state = XFER_FAILED;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000380
Brian Norris289c0522011-07-19 10:06:09 -0700381 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383 /* Write the transfer unit header */
384 header = part->header;
385 header.LogicalEUN = cpu_to_le16(0xffff);
386 header.EraseCount = cpu_to_le32(xfer->EraseCount);
387
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200388 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
389 (u_char *)&header);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391 if (ret) {
392 return ret;
393 }
394
395 /* Write the BAM stub */
Arushi Singhalb7568162018-03-25 21:07:43 +0530396 nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
397 le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
399 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
400 ctl = cpu_to_le32(BLOCK_CONTROL);
401
David Woodhouse3854be72008-12-10 14:06:42 +0000402 for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200404 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
405 (u_char *)&ctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
407 if (ret)
408 return ret;
409 }
410 xfer->state = XFER_PREPARED;
411 return 0;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413} /* prepare_xfer */
414
415/*======================================================================
416
417 Copy_erase_unit() takes a full erase block and a transfer unit,
418 copies everything to the transfer unit, then swaps the block
419 pointers.
420
421 All data blocks are copied to the corresponding blocks in the
422 target unit, so the virtual block map does not need to be
423 updated.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425======================================================================*/
426
David Woodhouse3854be72008-12-10 14:06:42 +0000427static int copy_erase_unit(partition_t *part, uint16_t srcunit,
428 uint16_t xferunit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
430 u_char buf[SECTOR_SIZE];
431 struct eun_info_t *eun;
432 struct xfer_info_t *xfer;
David Woodhouse3854be72008-12-10 14:06:42 +0000433 uint32_t src, dest, free, i;
434 uint16_t unit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 int ret;
436 ssize_t retlen;
437 loff_t offset;
David Woodhouse3854be72008-12-10 14:06:42 +0000438 uint16_t srcunitswap = cpu_to_le16(srcunit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439
440 eun = &part->EUNInfo[srcunit];
441 xfer = &part->XferInfo[xferunit];
Brian Norris289c0522011-07-19 10:06:09 -0700442 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 eun->Offset, xfer->Offset);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000444
445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 /* Read current BAM */
447 if (part->bam_index != srcunit) {
448
449 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
450
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200451 ret = mtd_read(part->mbd.mtd, offset,
452 part->BlocksPerUnit * sizeof(uint32_t), &retlen,
453 (u_char *)(part->bam_cache));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
455 /* mark the cache bad, in case we get an error later */
456 part->bam_index = 0xffff;
457
458 if (ret) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000459 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 return ret;
461 }
462 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000463
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 /* Write the LogicalEUN for the transfer unit */
465 xfer->state = XFER_UNKNOWN;
466 offset = xfer->Offset + 20; /* Bad! */
467 unit = cpu_to_le16(0x7fff);
468
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200469 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
470 (u_char *)&unit);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 if (ret) {
473 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
474 return ret;
475 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 /* Copy all data blocks from source unit to transfer unit */
478 src = eun->Offset; dest = xfer->Offset;
479
480 free = 0;
481 ret = 0;
482 for (i = 0; i < part->BlocksPerUnit; i++) {
483 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
484 case BLOCK_CONTROL:
485 /* This gets updated later */
486 break;
487 case BLOCK_DATA:
488 case BLOCK_REPLACEMENT:
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200489 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
490 (u_char *)buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 if (ret) {
492 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
493 return ret;
494 }
495
496
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200497 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
498 (u_char *)buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 if (ret) {
500 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
501 return ret;
502 }
503
504 break;
505 default:
506 /* All other blocks must be free */
507 part->bam_cache[i] = cpu_to_le32(0xffffffff);
508 free++;
509 break;
510 }
511 src += SECTOR_SIZE;
512 dest += SECTOR_SIZE;
513 }
514
515 /* Write the BAM to the transfer unit */
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200516 ret = mtd_write(part->mbd.mtd,
517 xfer->Offset + le32_to_cpu(part->header.BAMOffset),
518 part->BlocksPerUnit * sizeof(int32_t),
519 &retlen,
520 (u_char *)part->bam_cache);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 if (ret) {
522 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
523 return ret;
524 }
525
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 /* All clear? Then update the LogicalEUN again */
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200528 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
529 &retlen, (u_char *)&srcunitswap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
531 if (ret) {
532 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
533 return ret;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000534 }
535
536
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 /* Update the maps and usage stats*/
Fabian Frederick6166a762015-06-10 18:31:06 +0200538 swap(xfer->EraseCount, eun->EraseCount);
539 swap(xfer->Offset, eun->Offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 part->FreeTotal -= eun->Free;
541 part->FreeTotal += free;
542 eun->Free = free;
543 eun->Deleted = 0;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000544
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 /* Now, the cache should be valid for the new block */
546 part->bam_index = srcunit;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000547
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 return 0;
549} /* copy_erase_unit */
550
551/*======================================================================
552
553 reclaim_block() picks a full erase unit and a transfer unit and
554 then calls copy_erase_unit() to copy one to the other. Then, it
555 schedules an erase on the expired block.
556
557 What's a good way to decide which transfer unit and which erase
558 unit to use? Beats me. My way is to always pick the transfer
559 unit with the fewest erases, and usually pick the data unit with
560 the most deleted blocks. But with a small probability, pick the
561 oldest data unit instead. This means that we generally postpone
Brian Norris92394b5c2011-07-20 09:53:42 -0700562 the next reclamation as long as possible, but shuffle static
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 stuff around a bit for wear leveling.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565======================================================================*/
566
567static int reclaim_block(partition_t *part)
568{
David Woodhouse3854be72008-12-10 14:06:42 +0000569 uint16_t i, eun, xfer;
570 uint32_t best;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 int queued, ret;
572
Brian Norris289c0522011-07-19 10:06:09 -0700573 pr_debug("ftl_cs: reclaiming space...\n");
574 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 /* Pick the least erased transfer unit */
576 best = 0xffffffff; xfer = 0xffff;
577 do {
578 queued = 0;
579 for (i = 0; i < part->header.NumTransferUnits; i++) {
580 int n=0;
581 if (part->XferInfo[i].state == XFER_UNKNOWN) {
Brian Norris289c0522011-07-19 10:06:09 -0700582 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 n=1;
584 erase_xfer(part, i);
585 }
586 if (part->XferInfo[i].state == XFER_ERASING) {
Brian Norris289c0522011-07-19 10:06:09 -0700587 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 n=1;
589 queued = 1;
590 }
591 else if (part->XferInfo[i].state == XFER_ERASED) {
Brian Norris289c0522011-07-19 10:06:09 -0700592 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 n=1;
594 prepare_xfer(part, i);
595 }
596 if (part->XferInfo[i].state == XFER_PREPARED) {
Brian Norris289c0522011-07-19 10:06:09 -0700597 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 n=1;
599 if (part->XferInfo[i].EraseCount <= best) {
600 best = part->XferInfo[i].EraseCount;
601 xfer = i;
602 }
603 }
604 if (!n)
Brian Norris289c0522011-07-19 10:06:09 -0700605 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607 }
608 if (xfer == 0xffff) {
609 if (queued) {
Brian Norris289c0522011-07-19 10:06:09 -0700610 pr_debug("ftl_cs: waiting for transfer "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 "unit to be prepared...\n");
Artem Bityutskiy327cf292011-12-30 16:35:35 +0200612 mtd_sync(part->mbd.mtd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 } else {
614 static int ne = 0;
615 if (++ne < 5)
616 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
617 "suitable transfer units!\n");
618 else
Brian Norris289c0522011-07-19 10:06:09 -0700619 pr_debug("ftl_cs: reclaim failed: no "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 "suitable transfer units!\n");
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 return -EIO;
623 }
624 }
625 } while (xfer == 0xffff);
626
627 eun = 0;
628 if ((jiffies % shuffle_freq) == 0) {
Brian Norris289c0522011-07-19 10:06:09 -0700629 pr_debug("ftl_cs: recycling freshest block...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 best = 0xffffffff;
631 for (i = 0; i < part->DataUnits; i++)
632 if (part->EUNInfo[i].EraseCount <= best) {
633 best = part->EUNInfo[i].EraseCount;
634 eun = i;
635 }
636 } else {
637 best = 0;
638 for (i = 0; i < part->DataUnits; i++)
639 if (part->EUNInfo[i].Deleted >= best) {
640 best = part->EUNInfo[i].Deleted;
641 eun = i;
642 }
643 if (best == 0) {
644 static int ne = 0;
645 if (++ne < 5)
646 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
647 "no free blocks!\n");
648 else
Brian Norris289c0522011-07-19 10:06:09 -0700649 pr_debug("ftl_cs: reclaim failed: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 "no free blocks!\n");
651
652 return -EIO;
653 }
654 }
655 ret = copy_erase_unit(part, eun, xfer);
656 if (!ret)
657 erase_xfer(part, xfer);
658 else
659 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
660 return ret;
661} /* reclaim_block */
662
663/*======================================================================
664
665 Find_free() searches for a free block. If necessary, it updates
666 the BAM cache for the erase unit containing the free block. It
667 returns the block index -- the erase unit is just the currently
668 cached unit. If there are no free blocks, it returns 0 -- this
669 is never a valid data block because it contains the header.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671======================================================================*/
672
673#ifdef PSYCHO_DEBUG
674static void dump_lists(partition_t *part)
675{
676 int i;
677 printk(KERN_DEBUG "ftl_cs: Free total = %d\n", part->FreeTotal);
678 for (i = 0; i < part->DataUnits; i++)
679 printk(KERN_DEBUG "ftl_cs: unit %d: %d phys, %d free, "
680 "%d deleted\n", i,
681 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
682 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
683}
684#endif
685
David Woodhouse3854be72008-12-10 14:06:42 +0000686static uint32_t find_free(partition_t *part)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
David Woodhouse3854be72008-12-10 14:06:42 +0000688 uint16_t stop, eun;
689 uint32_t blk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 size_t retlen;
691 int ret;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000692
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 /* Find an erase unit with some free space */
694 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
695 eun = stop;
696 do {
697 if (part->EUNInfo[eun].Free != 0) break;
698 /* Wrap around at end of table */
699 if (++eun == part->DataUnits) eun = 0;
700 } while (eun != stop);
701
702 if (part->EUNInfo[eun].Free == 0)
703 return 0;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000704
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 /* Is this unit's BAM cached? */
706 if (eun != part->bam_index) {
707 /* Invalidate cache */
708 part->bam_index = 0xffff;
709
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200710 ret = mtd_read(part->mbd.mtd,
711 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
712 part->BlocksPerUnit * sizeof(uint32_t),
713 &retlen,
714 (u_char *)(part->bam_cache));
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000715
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 if (ret) {
717 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
718 return 0;
719 }
720 part->bam_index = eun;
721 }
722
723 /* Find a free block */
724 for (blk = 0; blk < part->BlocksPerUnit; blk++)
725 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[blk]))) break;
726 if (blk == part->BlocksPerUnit) {
727#ifdef PSYCHO_DEBUG
728 static int ne = 0;
729 if (++ne == 1)
730 dump_lists(part);
731#endif
732 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
733 return 0;
734 }
Brian Norris289c0522011-07-19 10:06:09 -0700735 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 return blk;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000737
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738} /* find_free */
739
740
741/*======================================================================
742
743 Read a series of sectors from an FTL partition.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745======================================================================*/
746
747static int ftl_read(partition_t *part, caddr_t buffer,
748 u_long sector, u_long nblocks)
749{
David Woodhouse3854be72008-12-10 14:06:42 +0000750 uint32_t log_addr, bsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 u_long i;
752 int ret;
753 size_t offset, retlen;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000754
Brian Norris289c0522011-07-19 10:06:09 -0700755 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 part, sector, nblocks);
757 if (!(part->state & FTL_FORMATTED)) {
758 printk(KERN_NOTICE "ftl_cs: bad partition\n");
759 return -EIO;
760 }
761 bsize = 1 << part->header.EraseUnitSize;
762
763 for (i = 0; i < nblocks; i++) {
764 if (((sector+i) * SECTOR_SIZE) >= le32_to_cpu(part->header.FormattedSize)) {
765 printk(KERN_NOTICE "ftl_cs: bad read offset\n");
766 return -EIO;
767 }
768 log_addr = part->VirtualBlockMap[sector+i];
769 if (log_addr == 0xffffffff)
770 memset(buffer, 0, SECTOR_SIZE);
771 else {
772 offset = (part->EUNInfo[log_addr / bsize].Offset
773 + (log_addr % bsize));
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200774 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
775 (u_char *)buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
777 if (ret) {
778 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
779 return ret;
780 }
781 }
782 buffer += SECTOR_SIZE;
783 }
784 return 0;
785} /* ftl_read */
786
787/*======================================================================
788
789 Write a series of sectors to an FTL partition
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791======================================================================*/
792
David Woodhouse3854be72008-12-10 14:06:42 +0000793static int set_bam_entry(partition_t *part, uint32_t log_addr,
794 uint32_t virt_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
David Woodhouse3854be72008-12-10 14:06:42 +0000796 uint32_t bsize, blk, le_virt_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797#ifdef PSYCHO_DEBUG
David Woodhouse3854be72008-12-10 14:06:42 +0000798 uint32_t old_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799#endif
David Woodhouse3854be72008-12-10 14:06:42 +0000800 uint16_t eun;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 int ret;
802 size_t retlen, offset;
803
Brian Norris289c0522011-07-19 10:06:09 -0700804 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 part, log_addr, virt_addr);
806 bsize = 1 << part->header.EraseUnitSize;
807 eun = log_addr / bsize;
808 blk = (log_addr % bsize) / SECTOR_SIZE;
David Woodhouse3854be72008-12-10 14:06:42 +0000809 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 le32_to_cpu(part->header.BAMOffset));
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000811
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812#ifdef PSYCHO_DEBUG
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200813 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
814 (u_char *)&old_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 if (ret) {
816 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
817 return ret;
818 }
819 old_addr = le32_to_cpu(old_addr);
820
821 if (((virt_addr == 0xfffffffe) && !BLOCK_FREE(old_addr)) ||
822 ((virt_addr == 0) && (BLOCK_TYPE(old_addr) != BLOCK_DATA)) ||
823 (!BLOCK_DELETED(virt_addr) && (old_addr != 0xfffffffe))) {
824 static int ne = 0;
825 if (++ne < 5) {
826 printk(KERN_NOTICE "ftl_cs: set_bam_entry() inconsistency!\n");
827 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, old = 0x%x"
828 ", new = 0x%x\n", log_addr, old_addr, virt_addr);
829 }
830 return -EIO;
831 }
832#endif
833 le_virt_addr = cpu_to_le32(virt_addr);
834 if (part->bam_index == eun) {
835#ifdef PSYCHO_DEBUG
836 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
837 static int ne = 0;
838 if (++ne < 5) {
839 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
840 "inconsistency!\n");
841 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
842 " = 0x%x\n",
843 le32_to_cpu(part->bam_cache[blk]), old_addr);
844 }
845 return -EIO;
846 }
847#endif
848 part->bam_cache[blk] = le_virt_addr;
849 }
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200850 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
851 (u_char *)&le_virt_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
853 if (ret) {
854 printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
855 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, new = 0x%x\n",
856 log_addr, virt_addr);
857 }
858 return ret;
859} /* set_bam_entry */
860
861static int ftl_write(partition_t *part, caddr_t buffer,
862 u_long sector, u_long nblocks)
863{
David Woodhouse3854be72008-12-10 14:06:42 +0000864 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 u_long i;
866 int ret;
867 size_t retlen, offset;
868
Brian Norris289c0522011-07-19 10:06:09 -0700869 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 part, sector, nblocks);
871 if (!(part->state & FTL_FORMATTED)) {
872 printk(KERN_NOTICE "ftl_cs: bad partition\n");
873 return -EIO;
874 }
875 /* See if we need to reclaim space, before we start */
876 while (part->FreeTotal < nblocks) {
877 ret = reclaim_block(part);
878 if (ret)
879 return ret;
880 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000881
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 bsize = 1 << part->header.EraseUnitSize;
883
884 virt_addr = sector * SECTOR_SIZE | BLOCK_DATA;
885 for (i = 0; i < nblocks; i++) {
886 if (virt_addr >= le32_to_cpu(part->header.FormattedSize)) {
887 printk(KERN_NOTICE "ftl_cs: bad write offset\n");
888 return -EIO;
889 }
890
891 /* Grab a free block */
892 blk = find_free(part);
893 if (blk == 0) {
894 static int ne = 0;
895 if (++ne < 5)
896 printk(KERN_NOTICE "ftl_cs: internal error: "
897 "no free blocks!\n");
898 return -ENOSPC;
899 }
900
901 /* Tag the BAM entry, and write the new block */
902 log_addr = part->bam_index * bsize + blk * SECTOR_SIZE;
903 part->EUNInfo[part->bam_index].Free--;
904 part->FreeTotal--;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000905 if (set_bam_entry(part, log_addr, 0xfffffffe))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 return -EIO;
907 part->EUNInfo[part->bam_index].Deleted++;
908 offset = (part->EUNInfo[part->bam_index].Offset +
909 blk * SECTOR_SIZE);
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200910 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 if (ret) {
913 printk(KERN_NOTICE "ftl_cs: block write failed!\n");
914 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, virt_addr"
915 " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
916 offset);
917 return -EIO;
918 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000919
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 /* Only delete the old entry when the new entry is ready */
921 old_addr = part->VirtualBlockMap[sector+i];
922 if (old_addr != 0xffffffff) {
923 part->VirtualBlockMap[sector+i] = 0xffffffff;
924 part->EUNInfo[old_addr/bsize].Deleted++;
925 if (set_bam_entry(part, old_addr, 0))
926 return -EIO;
927 }
928
929 /* Finally, set up the new pointers */
930 if (set_bam_entry(part, log_addr, virt_addr))
931 return -EIO;
932 part->VirtualBlockMap[sector+i] = log_addr;
933 part->EUNInfo[part->bam_index].Deleted--;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000934
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 buffer += SECTOR_SIZE;
936 virt_addr += SECTOR_SIZE;
937 }
938 return 0;
939} /* ftl_write */
940
941static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
942{
943 partition_t *part = (void *)dev;
944 u_long sect;
945
946 /* Sort of arbitrary: round size down to 4KiB boundary */
947 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
948
949 geo->heads = 1;
950 geo->sectors = 8;
951 geo->cylinders = sect >> 3;
952
953 return 0;
954}
955
956static int ftl_readsect(struct mtd_blktrans_dev *dev,
957 unsigned long block, char *buf)
958{
959 return ftl_read((void *)dev, buf, block, 1);
960}
961
962static int ftl_writesect(struct mtd_blktrans_dev *dev,
963 unsigned long block, char *buf)
964{
965 return ftl_write((void *)dev, buf, block, 1);
966}
967
David Woodhousefdc53972008-08-05 18:08:56 +0100968static int ftl_discardsect(struct mtd_blktrans_dev *dev,
969 unsigned long sector, unsigned nr_sects)
970{
971 partition_t *part = (void *)dev;
972 uint32_t bsize = 1 << part->header.EraseUnitSize;
973
Brian Norris289c0522011-07-19 10:06:09 -0700974 pr_debug("FTL erase sector %ld for %d sectors\n",
David Woodhousefdc53972008-08-05 18:08:56 +0100975 sector, nr_sects);
976
977 while (nr_sects) {
978 uint32_t old_addr = part->VirtualBlockMap[sector];
979 if (old_addr != 0xffffffff) {
980 part->VirtualBlockMap[sector] = 0xffffffff;
981 part->EUNInfo[old_addr/bsize].Deleted++;
982 if (set_bam_entry(part, old_addr, 0))
983 return -EIO;
984 }
985 nr_sects--;
986 sector++;
987 }
988
989 return 0;
990}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991/*====================================================================*/
992
Adrian Bunk5ce45d52008-04-14 17:20:24 +0300993static void ftl_freepart(partition_t *part)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 vfree(part->VirtualBlockMap);
996 part->VirtualBlockMap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 kfree(part->EUNInfo);
998 part->EUNInfo = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 kfree(part->XferInfo);
1000 part->XferInfo = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 kfree(part->bam_cache);
1002 part->bam_cache = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003} /* ftl_freepart */
1004
1005static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1006{
1007 partition_t *partition;
1008
Burman Yan95b93a02006-11-15 21:10:29 +02001009 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001010
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 if (!partition) {
1012 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1013 mtd->name);
1014 return;
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001015 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 partition->mbd.mtd = mtd;
1018
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001019 if ((scan_header(partition) == 0) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 (build_maps(partition) == 0)) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 partition->state = FTL_FORMATTED;
1023#ifdef PCMCIA_DEBUG
1024 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1025 le32_to_cpu(partition->header.FormattedSize) >> 10);
1026#endif
1027 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
Richard Purdie19187672006-10-27 09:09:33 +01001028
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 partition->mbd.tr = tr;
1030 partition->mbd.devnum = -1;
1031 if (!add_mtd_blktrans_dev((void *)partition))
1032 return;
1033 }
1034
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 kfree(partition);
1036}
1037
1038static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1039{
1040 del_mtd_blktrans_dev(dev);
1041 ftl_freepart((partition_t *)dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042}
1043
Adrian Bunk5ce45d52008-04-14 17:20:24 +03001044static struct mtd_blktrans_ops ftl_tr = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 .name = "ftl",
1046 .major = FTL_MAJOR,
1047 .part_bits = PART_BITS,
Richard Purdie19187672006-10-27 09:09:33 +01001048 .blksize = SECTOR_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 .readsect = ftl_readsect,
1050 .writesect = ftl_writesect,
David Woodhousefdc53972008-08-05 18:08:56 +01001051 .discard = ftl_discardsect,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 .getgeo = ftl_getgeo,
1053 .add_mtd = ftl_add_mtd,
1054 .remove_dev = ftl_remove_dev,
1055 .owner = THIS_MODULE,
1056};
1057
Peter Huewe627df232009-06-11 02:23:33 +02001058static int __init init_ftl(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 return register_mtd_blktrans(&ftl_tr);
1061}
1062
1063static void __exit cleanup_ftl(void)
1064{
1065 deregister_mtd_blktrans(&ftl_tr);
1066}
1067
1068module_init(init_ftl);
1069module_exit(cleanup_ftl);
1070
1071
1072MODULE_LICENSE("Dual MPL/GPL");
1073MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1074MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");