blob: 2578f27914ef50cff03b05a49a4ddcae7cbefa58 [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;
Kees Cook6da2ec52018-06-12 13:55:00 -0700204 part->EUNInfo = kmalloc_array(part->DataUnits, sizeof(struct eun_info_t),
205 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 if (!part->EUNInfo)
207 goto out;
208 for (i = 0; i < part->DataUnits; i++)
209 part->EUNInfo[i].Offset = 0xffffffff;
210 part->XferInfo =
Kees Cook6da2ec52018-06-12 13:55:00 -0700211 kmalloc_array(part->header.NumTransferUnits,
212 sizeof(struct xfer_info_t),
213 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 if (!part->XferInfo)
215 goto out_EUNInfo;
216
217 xvalid = xtrans = 0;
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);
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200221 ret = mtd_read(part->mbd.mtd, offset, sizeof(header), &retval,
222 (unsigned char *)&header);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000223
224 if (ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 goto out_XferInfo;
226
227 ret = -1;
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);
235 xvalid++;
236 } else {
237 if (xtrans == part->header.NumTransferUnits) {
238 printk(KERN_NOTICE "ftl_cs: format error: too many "
239 "transfer units!\n");
240 goto out_XferInfo;
241 }
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);
245 } else {
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);
250 }
251 part->XferInfo[xtrans].Offset = offset;
252 xtrans++;
253 }
254 }
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 "
260 "don't add up!\n");
261 goto out_XferInfo;
262 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 /* Set up virtual page map */
265 blocks = le32_to_cpu(header.FormattedSize) >> header.BlockSize;
Kees Cook42bc47b2018-06-12 14:27:11 -0700266 part->VirtualBlockMap = vmalloc(array_size(blocks, sizeof(uint32_t)));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 if (!part->VirtualBlockMap)
268 goto out_XferInfo;
269
David Woodhouse3854be72008-12-10 14:06:42 +0000270 memset(part->VirtualBlockMap, 0xff, blocks * sizeof(uint32_t));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 part->BlocksPerUnit = (1 << header.EraseUnitSize) >> header.BlockSize;
272
Kees Cook6da2ec52018-06-12 13:55:00 -0700273 part->bam_cache = kmalloc_array(part->BlocksPerUnit, sizeof(uint32_t),
274 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 if (!part->bam_cache)
276 goto out_VirtualBlockMap;
277
278 part->bam_index = 0xffff;
279 part->FreeTotal = 0;
280
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);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000285
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200286 ret = mtd_read(part->mbd.mtd, offset,
287 part->BlocksPerUnit * sizeof(uint32_t), &retval,
288 (unsigned char *)part->bam_cache);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000289
290 if (ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 goto out_bam_cache;
292
293 for (j = 0; j < part->BlocksPerUnit; j++) {
294 if (BLOCK_FREE(le32_to_cpu(part->bam_cache[j]))) {
295 part->EUNInfo[i].Free++;
296 part->FreeTotal++;
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++;
303 }
304 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000305
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 ret = 0;
307 goto out;
308
309out_bam_cache:
310 kfree(part->bam_cache);
311out_VirtualBlockMap:
312 vfree(part->VirtualBlockMap);
313out_XferInfo:
314 kfree(part->XferInfo);
315out_EUNInfo:
316 kfree(part->EUNInfo);
317out:
318 return ret;
319} /* build_maps */
320
321/*======================================================================
322
323 Erase_xfer() schedules an asynchronous erase operation for a
324 transfer unit.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000325
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326======================================================================*/
327
328static int erase_xfer(partition_t *part,
David Woodhouse3854be72008-12-10 14:06:42 +0000329 uint16_t xfernum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330{
331 int ret;
332 struct xfer_info_t *xfer;
333 struct erase_info *erase;
334
335 xfer = &part->XferInfo[xfernum];
Brian Norris289c0522011-07-19 10:06:09 -0700336 pr_debug("ftl_cs: erasing xfer unit at 0x%x\n", xfer->Offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 xfer->state = XFER_ERASING;
338
339 /* Is there a free erase slot? Always in MTD. */
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000340
341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000343 if (!erase)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 return -ENOMEM;
345
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 erase->addr = xfer->Offset;
347 erase->len = 1 << part->header.EraseUnitSize;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000348
Artem Bityutskiy7e1f0dc2011-12-23 15:25:39 +0200349 ret = mtd_erase(part->mbd.mtd, erase);
Boris Brezillon884cfd92018-02-12 22:03:09 +0100350 if (!ret) {
351 xfer->state = XFER_ERASED;
352 xfer->EraseCount++;
353 } else {
354 xfer->state = XFER_FAILED;
355 pr_notice("ftl_cs: erase failed: err = %d\n", ret);
356 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Boris Brezillon884cfd92018-02-12 22:03:09 +0100358 kfree(erase);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
360 return ret;
361} /* erase_xfer */
362
363/*======================================================================
364
365 Prepare_xfer() takes a freshly erased transfer unit and gives
366 it an appropriate header.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368======================================================================*/
369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370static int prepare_xfer(partition_t *part, int i)
371{
372 erase_unit_header_t header;
373 struct xfer_info_t *xfer;
374 int nbam, ret;
David Woodhouse3854be72008-12-10 14:06:42 +0000375 uint32_t ctl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 ssize_t retlen;
377 loff_t offset;
378
379 xfer = &part->XferInfo[i];
380 xfer->state = XFER_FAILED;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000381
Brian Norris289c0522011-07-19 10:06:09 -0700382 pr_debug("ftl_cs: preparing xfer unit at 0x%x\n", xfer->Offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383
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);
388
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200389 ret = mtd_write(part->mbd.mtd, xfer->Offset, sizeof(header), &retlen,
390 (u_char *)&header);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
392 if (ret) {
393 return ret;
394 }
395
396 /* Write the BAM stub */
Arushi Singhalb7568162018-03-25 21:07:43 +0530397 nbam = DIV_ROUND_UP(part->BlocksPerUnit * sizeof(uint32_t) +
398 le32_to_cpu(part->header.BAMOffset), SECTOR_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399
400 offset = xfer->Offset + le32_to_cpu(part->header.BAMOffset);
401 ctl = cpu_to_le32(BLOCK_CONTROL);
402
David Woodhouse3854be72008-12-10 14:06:42 +0000403 for (i = 0; i < nbam; i++, offset += sizeof(uint32_t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200405 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
406 (u_char *)&ctl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 if (ret)
409 return ret;
410 }
411 xfer->state = XFER_PREPARED;
412 return 0;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000413
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414} /* prepare_xfer */
415
416/*======================================================================
417
418 Copy_erase_unit() takes a full erase block and a transfer unit,
419 copies everything to the transfer unit, then swaps the block
420 pointers.
421
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
424 updated.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000425
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426======================================================================*/
427
David Woodhouse3854be72008-12-10 14:06:42 +0000428static int copy_erase_unit(partition_t *part, uint16_t srcunit,
429 uint16_t xferunit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 u_char buf[SECTOR_SIZE];
432 struct eun_info_t *eun;
433 struct xfer_info_t *xfer;
David Woodhouse3854be72008-12-10 14:06:42 +0000434 uint32_t src, dest, free, i;
435 uint16_t unit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 int ret;
437 ssize_t retlen;
438 loff_t offset;
David Woodhouse3854be72008-12-10 14:06:42 +0000439 uint16_t srcunitswap = cpu_to_le16(srcunit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
441 eun = &part->EUNInfo[srcunit];
442 xfer = &part->XferInfo[xferunit];
Brian Norris289c0522011-07-19 10:06:09 -0700443 pr_debug("ftl_cs: copying block 0x%x to 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 eun->Offset, xfer->Offset);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000445
446
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 /* Read current BAM */
448 if (part->bam_index != srcunit) {
449
450 offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
451
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200452 ret = mtd_read(part->mbd.mtd, offset,
453 part->BlocksPerUnit * sizeof(uint32_t), &retlen,
454 (u_char *)(part->bam_cache));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
456 /* mark the cache bad, in case we get an error later */
457 part->bam_index = 0xffff;
458
459 if (ret) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000460 printk( KERN_WARNING "ftl: Failed to read BAM cache in copy_erase_unit()!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 return ret;
462 }
463 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000464
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 /* Write the LogicalEUN for the transfer unit */
466 xfer->state = XFER_UNKNOWN;
467 offset = xfer->Offset + 20; /* Bad! */
468 unit = cpu_to_le16(0x7fff);
469
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200470 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint16_t), &retlen,
471 (u_char *)&unit);
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000472
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 if (ret) {
474 printk( KERN_WARNING "ftl: Failed to write back to BAM cache in copy_erase_unit()!\n");
475 return ret;
476 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 /* Copy all data blocks from source unit to transfer unit */
479 src = eun->Offset; dest = xfer->Offset;
480
481 free = 0;
482 ret = 0;
483 for (i = 0; i < part->BlocksPerUnit; i++) {
484 switch (BLOCK_TYPE(le32_to_cpu(part->bam_cache[i]))) {
485 case BLOCK_CONTROL:
486 /* This gets updated later */
487 break;
488 case BLOCK_DATA:
489 case BLOCK_REPLACEMENT:
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200490 ret = mtd_read(part->mbd.mtd, src, SECTOR_SIZE, &retlen,
491 (u_char *)buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 if (ret) {
493 printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
494 return ret;
495 }
496
497
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200498 ret = mtd_write(part->mbd.mtd, dest, SECTOR_SIZE, &retlen,
499 (u_char *)buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 if (ret) {
501 printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
502 return ret;
503 }
504
505 break;
506 default:
507 /* All other blocks must be free */
508 part->bam_cache[i] = cpu_to_le32(0xffffffff);
509 free++;
510 break;
511 }
512 src += SECTOR_SIZE;
513 dest += SECTOR_SIZE;
514 }
515
516 /* Write the BAM to the transfer unit */
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200517 ret = mtd_write(part->mbd.mtd,
518 xfer->Offset + le32_to_cpu(part->header.BAMOffset),
519 part->BlocksPerUnit * sizeof(int32_t),
520 &retlen,
521 (u_char *)part->bam_cache);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 if (ret) {
523 printk( KERN_WARNING "ftl: Error writing BAM in copy_erase_unit\n");
524 return ret;
525 }
526
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 /* All clear? Then update the LogicalEUN again */
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200529 ret = mtd_write(part->mbd.mtd, xfer->Offset + 20, sizeof(uint16_t),
530 &retlen, (u_char *)&srcunitswap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
532 if (ret) {
533 printk(KERN_WARNING "ftl: Error writing new LogicalEUN in copy_erase_unit\n");
534 return ret;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000535 }
536
537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 /* Update the maps and usage stats*/
Fabian Frederick6166a762015-06-10 18:31:06 +0200539 swap(xfer->EraseCount, eun->EraseCount);
540 swap(xfer->Offset, eun->Offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 part->FreeTotal -= eun->Free;
542 part->FreeTotal += free;
543 eun->Free = free;
544 eun->Deleted = 0;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000545
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 /* Now, the cache should be valid for the new block */
547 part->bam_index = srcunit;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 return 0;
550} /* copy_erase_unit */
551
552/*======================================================================
553
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.
557
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
Brian Norris92394b5c2011-07-20 09:53:42 -0700563 the next reclamation as long as possible, but shuffle static
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 stuff around a bit for wear leveling.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566======================================================================*/
567
568static int reclaim_block(partition_t *part)
569{
David Woodhouse3854be72008-12-10 14:06:42 +0000570 uint16_t i, eun, xfer;
571 uint32_t best;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 int queued, ret;
573
Brian Norris289c0522011-07-19 10:06:09 -0700574 pr_debug("ftl_cs: reclaiming space...\n");
575 pr_debug("NumTransferUnits == %x\n", part->header.NumTransferUnits);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 /* Pick the least erased transfer unit */
577 best = 0xffffffff; xfer = 0xffff;
578 do {
579 queued = 0;
580 for (i = 0; i < part->header.NumTransferUnits; i++) {
581 int n=0;
582 if (part->XferInfo[i].state == XFER_UNKNOWN) {
Brian Norris289c0522011-07-19 10:06:09 -0700583 pr_debug("XferInfo[%d].state == XFER_UNKNOWN\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 n=1;
585 erase_xfer(part, i);
586 }
587 if (part->XferInfo[i].state == XFER_ERASING) {
Brian Norris289c0522011-07-19 10:06:09 -0700588 pr_debug("XferInfo[%d].state == XFER_ERASING\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 n=1;
590 queued = 1;
591 }
592 else if (part->XferInfo[i].state == XFER_ERASED) {
Brian Norris289c0522011-07-19 10:06:09 -0700593 pr_debug("XferInfo[%d].state == XFER_ERASED\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 n=1;
595 prepare_xfer(part, i);
596 }
597 if (part->XferInfo[i].state == XFER_PREPARED) {
Brian Norris289c0522011-07-19 10:06:09 -0700598 pr_debug("XferInfo[%d].state == XFER_PREPARED\n",i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 n=1;
600 if (part->XferInfo[i].EraseCount <= best) {
601 best = part->XferInfo[i].EraseCount;
602 xfer = i;
603 }
604 }
605 if (!n)
Brian Norris289c0522011-07-19 10:06:09 -0700606 pr_debug("XferInfo[%d].state == %x\n",i, part->XferInfo[i].state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607
608 }
609 if (xfer == 0xffff) {
610 if (queued) {
Brian Norris289c0522011-07-19 10:06:09 -0700611 pr_debug("ftl_cs: waiting for transfer "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 "unit to be prepared...\n");
Artem Bityutskiy327cf292011-12-30 16:35:35 +0200613 mtd_sync(part->mbd.mtd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 } else {
615 static int ne = 0;
616 if (++ne < 5)
617 printk(KERN_NOTICE "ftl_cs: reclaim failed: no "
618 "suitable transfer units!\n");
619 else
Brian Norris289c0522011-07-19 10:06:09 -0700620 pr_debug("ftl_cs: reclaim failed: no "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 "suitable transfer units!\n");
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000622
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return -EIO;
624 }
625 }
626 } while (xfer == 0xffff);
627
628 eun = 0;
629 if ((jiffies % shuffle_freq) == 0) {
Brian Norris289c0522011-07-19 10:06:09 -0700630 pr_debug("ftl_cs: recycling freshest block...\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 best = 0xffffffff;
632 for (i = 0; i < part->DataUnits; i++)
633 if (part->EUNInfo[i].EraseCount <= best) {
634 best = part->EUNInfo[i].EraseCount;
635 eun = i;
636 }
637 } else {
638 best = 0;
639 for (i = 0; i < part->DataUnits; i++)
640 if (part->EUNInfo[i].Deleted >= best) {
641 best = part->EUNInfo[i].Deleted;
642 eun = i;
643 }
644 if (best == 0) {
645 static int ne = 0;
646 if (++ne < 5)
647 printk(KERN_NOTICE "ftl_cs: reclaim failed: "
648 "no free blocks!\n");
649 else
Brian Norris289c0522011-07-19 10:06:09 -0700650 pr_debug("ftl_cs: reclaim failed: "
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 "no free blocks!\n");
652
653 return -EIO;
654 }
655 }
656 ret = copy_erase_unit(part, eun, xfer);
657 if (!ret)
658 erase_xfer(part, xfer);
659 else
660 printk(KERN_NOTICE "ftl_cs: copy_erase_unit failed!\n");
661 return ret;
662} /* reclaim_block */
663
664/*======================================================================
665
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.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000671
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672======================================================================*/
673
674#ifdef PSYCHO_DEBUG
675static void dump_lists(partition_t *part)
676{
677 int i;
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, "
681 "%d deleted\n", i,
682 part->EUNInfo[i].Offset >> part->header.EraseUnitSize,
683 part->EUNInfo[i].Free, part->EUNInfo[i].Deleted);
684}
685#endif
686
David Woodhouse3854be72008-12-10 14:06:42 +0000687static uint32_t find_free(partition_t *part)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688{
David Woodhouse3854be72008-12-10 14:06:42 +0000689 uint16_t stop, eun;
690 uint32_t blk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 size_t retlen;
692 int ret;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000693
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 /* Find an erase unit with some free space */
695 stop = (part->bam_index == 0xffff) ? 0 : part->bam_index;
696 eun = stop;
697 do {
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);
702
703 if (part->EUNInfo[eun].Free == 0)
704 return 0;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000705
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 /* Is this unit's BAM cached? */
707 if (eun != part->bam_index) {
708 /* Invalidate cache */
709 part->bam_index = 0xffff;
710
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200711 ret = mtd_read(part->mbd.mtd,
712 part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
713 part->BlocksPerUnit * sizeof(uint32_t),
714 &retlen,
715 (u_char *)(part->bam_cache));
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000716
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 if (ret) {
718 printk(KERN_WARNING"ftl: Error reading BAM in find_free\n");
719 return 0;
720 }
721 part->bam_index = eun;
722 }
723
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) {
728#ifdef PSYCHO_DEBUG
729 static int ne = 0;
730 if (++ne == 1)
731 dump_lists(part);
732#endif
733 printk(KERN_NOTICE "ftl_cs: bad free list!\n");
734 return 0;
735 }
Brian Norris289c0522011-07-19 10:06:09 -0700736 pr_debug("ftl_cs: found free block at %d in %d\n", blk, eun);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 return blk;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739} /* find_free */
740
741
742/*======================================================================
743
744 Read a series of sectors from an FTL partition.
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000745
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746======================================================================*/
747
748static int ftl_read(partition_t *part, caddr_t buffer,
749 u_long sector, u_long nblocks)
750{
David Woodhouse3854be72008-12-10 14:06:42 +0000751 uint32_t log_addr, bsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 u_long i;
753 int ret;
754 size_t offset, retlen;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000755
Brian Norris289c0522011-07-19 10:06:09 -0700756 pr_debug("ftl_cs: ftl_read(0x%p, 0x%lx, %ld)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 part, sector, nblocks);
758 if (!(part->state & FTL_FORMATTED)) {
759 printk(KERN_NOTICE "ftl_cs: bad partition\n");
760 return -EIO;
761 }
762 bsize = 1 << part->header.EraseUnitSize;
763
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");
767 return -EIO;
768 }
769 log_addr = part->VirtualBlockMap[sector+i];
770 if (log_addr == 0xffffffff)
771 memset(buffer, 0, SECTOR_SIZE);
772 else {
773 offset = (part->EUNInfo[log_addr / bsize].Offset
774 + (log_addr % bsize));
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200775 ret = mtd_read(part->mbd.mtd, offset, SECTOR_SIZE, &retlen,
776 (u_char *)buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
778 if (ret) {
779 printk(KERN_WARNING "Error reading MTD device in ftl_read()\n");
780 return ret;
781 }
782 }
783 buffer += SECTOR_SIZE;
784 }
785 return 0;
786} /* ftl_read */
787
788/*======================================================================
789
790 Write a series of sectors to an FTL partition
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792======================================================================*/
793
David Woodhouse3854be72008-12-10 14:06:42 +0000794static int set_bam_entry(partition_t *part, uint32_t log_addr,
795 uint32_t virt_addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796{
David Woodhouse3854be72008-12-10 14:06:42 +0000797 uint32_t bsize, blk, le_virt_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798#ifdef PSYCHO_DEBUG
David Woodhouse3854be72008-12-10 14:06:42 +0000799 uint32_t old_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800#endif
David Woodhouse3854be72008-12-10 14:06:42 +0000801 uint16_t eun;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 int ret;
803 size_t retlen, offset;
804
Brian Norris289c0522011-07-19 10:06:09 -0700805 pr_debug("ftl_cs: set_bam_entry(0x%p, 0x%x, 0x%x)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 part, log_addr, virt_addr);
807 bsize = 1 << part->header.EraseUnitSize;
808 eun = log_addr / bsize;
809 blk = (log_addr % bsize) / SECTOR_SIZE;
David Woodhouse3854be72008-12-10 14:06:42 +0000810 offset = (part->EUNInfo[eun].Offset + blk * sizeof(uint32_t) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 le32_to_cpu(part->header.BAMOffset));
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000812
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813#ifdef PSYCHO_DEBUG
Artem Bityutskiy329ad392011-12-23 17:30:16 +0200814 ret = mtd_read(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
815 (u_char *)&old_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 if (ret) {
817 printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
818 return ret;
819 }
820 old_addr = le32_to_cpu(old_addr);
821
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))) {
825 static int ne = 0;
826 if (++ne < 5) {
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);
830 }
831 return -EIO;
832 }
833#endif
834 le_virt_addr = cpu_to_le32(virt_addr);
835 if (part->bam_index == eun) {
836#ifdef PSYCHO_DEBUG
837 if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
838 static int ne = 0;
839 if (++ne < 5) {
840 printk(KERN_NOTICE "ftl_cs: set_bam_entry() "
841 "inconsistency!\n");
842 printk(KERN_NOTICE "ftl_cs: log_addr = 0x%x, cache"
843 " = 0x%x\n",
844 le32_to_cpu(part->bam_cache[blk]), old_addr);
845 }
846 return -EIO;
847 }
848#endif
849 part->bam_cache[blk] = le_virt_addr;
850 }
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200851 ret = mtd_write(part->mbd.mtd, offset, sizeof(uint32_t), &retlen,
852 (u_char *)&le_virt_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
854 if (ret) {
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);
858 }
859 return ret;
860} /* set_bam_entry */
861
862static int ftl_write(partition_t *part, caddr_t buffer,
863 u_long sector, u_long nblocks)
864{
David Woodhouse3854be72008-12-10 14:06:42 +0000865 uint32_t bsize, log_addr, virt_addr, old_addr, blk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 u_long i;
867 int ret;
868 size_t retlen, offset;
869
Brian Norris289c0522011-07-19 10:06:09 -0700870 pr_debug("ftl_cs: ftl_write(0x%p, %ld, %ld)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 part, sector, nblocks);
872 if (!(part->state & FTL_FORMATTED)) {
873 printk(KERN_NOTICE "ftl_cs: bad partition\n");
874 return -EIO;
875 }
876 /* See if we need to reclaim space, before we start */
877 while (part->FreeTotal < nblocks) {
878 ret = reclaim_block(part);
879 if (ret)
880 return ret;
881 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000882
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 bsize = 1 << part->header.EraseUnitSize;
884
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");
889 return -EIO;
890 }
891
892 /* Grab a free block */
893 blk = find_free(part);
894 if (blk == 0) {
895 static int ne = 0;
896 if (++ne < 5)
897 printk(KERN_NOTICE "ftl_cs: internal error: "
898 "no free blocks!\n");
899 return -ENOSPC;
900 }
901
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--;
905 part->FreeTotal--;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000906 if (set_bam_entry(part, log_addr, 0xfffffffe))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 return -EIO;
908 part->EUNInfo[part->bam_index].Deleted++;
909 offset = (part->EUNInfo[part->bam_index].Offset +
910 blk * SECTOR_SIZE);
Artem Bityutskiyeda95cb2011-12-23 17:35:41 +0200911 ret = mtd_write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
913 if (ret) {
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,
917 offset);
918 return -EIO;
919 }
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000920
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 /* 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))
927 return -EIO;
928 }
929
930 /* Finally, set up the new pointers */
931 if (set_bam_entry(part, log_addr, virt_addr))
932 return -EIO;
933 part->VirtualBlockMap[sector+i] = log_addr;
934 part->EUNInfo[part->bam_index].Deleted--;
Thomas Gleixner97894cd2005-11-07 11:15:26 +0000935
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 buffer += SECTOR_SIZE;
937 virt_addr += SECTOR_SIZE;
938 }
939 return 0;
940} /* ftl_write */
941
942static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
943{
944 partition_t *part = (void *)dev;
945 u_long sect;
946
947 /* Sort of arbitrary: round size down to 4KiB boundary */
948 sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
949
950 geo->heads = 1;
951 geo->sectors = 8;
952 geo->cylinders = sect >> 3;
953
954 return 0;
955}
956
957static int ftl_readsect(struct mtd_blktrans_dev *dev,
958 unsigned long block, char *buf)
959{
960 return ftl_read((void *)dev, buf, block, 1);
961}
962
963static int ftl_writesect(struct mtd_blktrans_dev *dev,
964 unsigned long block, char *buf)
965{
966 return ftl_write((void *)dev, buf, block, 1);
967}
968
David Woodhousefdc53972008-08-05 18:08:56 +0100969static int ftl_discardsect(struct mtd_blktrans_dev *dev,
970 unsigned long sector, unsigned nr_sects)
971{
972 partition_t *part = (void *)dev;
973 uint32_t bsize = 1 << part->header.EraseUnitSize;
974
Brian Norris289c0522011-07-19 10:06:09 -0700975 pr_debug("FTL erase sector %ld for %d sectors\n",
David Woodhousefdc53972008-08-05 18:08:56 +0100976 sector, nr_sects);
977
978 while (nr_sects) {
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))
984 return -EIO;
985 }
986 nr_sects--;
987 sector++;
988 }
989
990 return 0;
991}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992/*====================================================================*/
993
Adrian Bunk5ce45d52008-04-14 17:20:24 +0300994static void ftl_freepart(partition_t *part)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 vfree(part->VirtualBlockMap);
997 part->VirtualBlockMap = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 kfree(part->EUNInfo);
999 part->EUNInfo = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 kfree(part->XferInfo);
1001 part->XferInfo = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 kfree(part->bam_cache);
1003 part->bam_cache = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004} /* ftl_freepart */
1005
1006static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
1007{
1008 partition_t *partition;
1009
Burman Yan95b93a02006-11-15 21:10:29 +02001010 partition = kzalloc(sizeof(partition_t), GFP_KERNEL);
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001011
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 if (!partition) {
1013 printk(KERN_WARNING "No memory to scan for FTL on %s\n",
1014 mtd->name);
1015 return;
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001016 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 partition->mbd.mtd = mtd;
1019
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001020 if ((scan_header(partition) == 0) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 (build_maps(partition) == 0)) {
Thomas Gleixner97894cd2005-11-07 11:15:26 +00001022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 partition->state = FTL_FORMATTED;
1024#ifdef PCMCIA_DEBUG
1025 printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
1026 le32_to_cpu(partition->header.FormattedSize) >> 10);
1027#endif
1028 partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
Richard Purdie19187672006-10-27 09:09:33 +01001029
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 partition->mbd.tr = tr;
1031 partition->mbd.devnum = -1;
1032 if (!add_mtd_blktrans_dev((void *)partition))
1033 return;
1034 }
1035
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 kfree(partition);
1037}
1038
1039static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
1040{
1041 del_mtd_blktrans_dev(dev);
1042 ftl_freepart((partition_t *)dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043}
1044
Adrian Bunk5ce45d52008-04-14 17:20:24 +03001045static struct mtd_blktrans_ops ftl_tr = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 .name = "ftl",
1047 .major = FTL_MAJOR,
1048 .part_bits = PART_BITS,
Richard Purdie19187672006-10-27 09:09:33 +01001049 .blksize = SECTOR_SIZE,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 .readsect = ftl_readsect,
1051 .writesect = ftl_writesect,
David Woodhousefdc53972008-08-05 18:08:56 +01001052 .discard = ftl_discardsect,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 .getgeo = ftl_getgeo,
1054 .add_mtd = ftl_add_mtd,
1055 .remove_dev = ftl_remove_dev,
1056 .owner = THIS_MODULE,
1057};
1058
Peter Huewe627df232009-06-11 02:23:33 +02001059static int __init init_ftl(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return register_mtd_blktrans(&ftl_tr);
1062}
1063
1064static void __exit cleanup_ftl(void)
1065{
1066 deregister_mtd_blktrans(&ftl_tr);
1067}
1068
1069module_init(init_ftl);
1070module_exit(cleanup_ftl);
1071
1072
1073MODULE_LICENSE("Dual MPL/GPL");
1074MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
1075MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices");