109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e27a9960SSean Young /*
3e27a9960SSean Young * rfd_ftl.c -- resident flash disk (flash translation layer)
4e27a9960SSean Young *
5a1452a37SDavid Woodhouse * Copyright © 2005 Sean Young <sean@mess.org>
6e27a9960SSean Young *
7e27a9960SSean Young * This type of flash translation layer (FTL) is used by the Embedded BIOS
8e27a9960SSean Young * by General Software. It is known as the Resident Flash Disk (RFD), see:
9e27a9960SSean Young *
10e27a9960SSean Young * http://www.gensw.com/pages/prod/bios/rfd.htm
11e27a9960SSean Young *
12e27a9960SSean Young * based on ftl.c
13e27a9960SSean Young */
14e27a9960SSean Young
15e27a9960SSean Young #include <linux/hdreg.h>
16e27a9960SSean Young #include <linux/init.h>
17e27a9960SSean Young #include <linux/mtd/blktrans.h>
18e27a9960SSean Young #include <linux/mtd/mtd.h>
19e27a9960SSean Young #include <linux/vmalloc.h>
20de25968cSTim Schmielau #include <linux/slab.h>
21b80b5832SAndrew Morton #include <linux/jiffies.h>
22a0e5cc58SPaul Gortmaker #include <linux/module.h>
23e27a9960SSean Young
24e27a9960SSean Young #include <asm/types.h>
25e27a9960SSean Young
26e27a9960SSean Young static int block_size = 0;
27e27a9960SSean Young module_param(block_size, int, 0);
28e27a9960SSean Young MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
29e27a9960SSean Young
30e27a9960SSean Young #define PREFIX "rfd_ftl: "
31e27a9960SSean Young
32bc4117f8SSean Young /* This major has been assigned by device@lanana.org */
33e27a9960SSean Young #ifndef RFD_FTL_MAJOR
34bc4117f8SSean Young #define RFD_FTL_MAJOR 256
35e27a9960SSean Young #endif
36e27a9960SSean Young
37e27a9960SSean Young /* Maximum number of partitions in an FTL region */
38e27a9960SSean Young #define PART_BITS 4
39e27a9960SSean Young
40e27a9960SSean Young /* An erase unit should start with this value */
41e27a9960SSean Young #define RFD_MAGIC 0x9193
42e27a9960SSean Young
43e27a9960SSean Young /* the second value is 0xffff or 0xffc8; function unknown */
44e27a9960SSean Young
45e27a9960SSean Young /* the third value is always 0xffff, ignored */
46e27a9960SSean Young
47e27a9960SSean Young /* next is an array of mapping for each corresponding sector */
48e27a9960SSean Young #define HEADER_MAP_OFFSET 3
49e27a9960SSean Young #define SECTOR_DELETED 0x0000
50e27a9960SSean Young #define SECTOR_ZERO 0xfffe
51e27a9960SSean Young #define SECTOR_FREE 0xffff
52e27a9960SSean Young
53e27a9960SSean Young #define SECTOR_SIZE 512
54e27a9960SSean Young
55e27a9960SSean Young #define SECTORS_PER_TRACK 63
56e27a9960SSean Young
57e27a9960SSean Young struct block {
58e27a9960SSean Young enum {
59e27a9960SSean Young BLOCK_OK,
60e27a9960SSean Young BLOCK_ERASING,
61e27a9960SSean Young BLOCK_ERASED,
62683b30c8SSean Young BLOCK_UNUSED,
63e27a9960SSean Young BLOCK_FAILED
64e27a9960SSean Young } state;
65e27a9960SSean Young int free_sectors;
66e27a9960SSean Young int used_sectors;
67e27a9960SSean Young int erases;
68e27a9960SSean Young u_long offset;
69e27a9960SSean Young };
70e27a9960SSean Young
71e27a9960SSean Young struct partition {
72e27a9960SSean Young struct mtd_blktrans_dev mbd;
73e27a9960SSean Young
74e27a9960SSean Young u_int block_size; /* size of erase unit */
75e27a9960SSean Young u_int total_blocks; /* number of erase units */
76e27a9960SSean Young u_int header_sectors_per_block; /* header sectors in erase unit */
77e27a9960SSean Young u_int data_sectors_per_block; /* data sectors in erase unit */
78e27a9960SSean Young u_int sector_count; /* sectors in translated disk */
79e27a9960SSean Young u_int header_size; /* bytes in header sector */
80e27a9960SSean Young int reserved_block; /* block next up for reclaim */
81e27a9960SSean Young int current_block; /* block to write to */
82e27a9960SSean Young u16 *header_cache; /* cached header */
83e27a9960SSean Young
84e27a9960SSean Young int is_reclaiming;
85e27a9960SSean Young int cylinders;
86e27a9960SSean Young int errors;
87e27a9960SSean Young u_long *sector_map;
88e27a9960SSean Young struct block *blocks;
89e27a9960SSean Young };
90e27a9960SSean Young
91e27a9960SSean Young static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);
92e27a9960SSean Young
build_block_map(struct partition * part,int block_no)93e27a9960SSean Young static int build_block_map(struct partition *part, int block_no)
94e27a9960SSean Young {
95e27a9960SSean Young struct block *block = &part->blocks[block_no];
96e27a9960SSean Young int i;
97e27a9960SSean Young
98e27a9960SSean Young block->offset = part->block_size * block_no;
99e27a9960SSean Young
100e27a9960SSean Young if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
101683b30c8SSean Young block->state = BLOCK_UNUSED;
102683b30c8SSean Young return -ENOENT;
103e27a9960SSean Young }
104e27a9960SSean Young
105e27a9960SSean Young block->state = BLOCK_OK;
106e27a9960SSean Young
107e27a9960SSean Young for (i=0; i<part->data_sectors_per_block; i++) {
108e27a9960SSean Young u16 entry;
109e27a9960SSean Young
110e27a9960SSean Young entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
111e27a9960SSean Young
112e27a9960SSean Young if (entry == SECTOR_DELETED)
113e27a9960SSean Young continue;
114e27a9960SSean Young
115e27a9960SSean Young if (entry == SECTOR_FREE) {
116e27a9960SSean Young block->free_sectors++;
117e27a9960SSean Young continue;
118e27a9960SSean Young }
119e27a9960SSean Young
120e27a9960SSean Young if (entry == SECTOR_ZERO)
121e27a9960SSean Young entry = 0;
122e27a9960SSean Young
123e27a9960SSean Young if (entry >= part->sector_count) {
124683b30c8SSean Young printk(KERN_WARNING PREFIX
125e27a9960SSean Young "'%s': unit #%d: entry %d corrupt, "
126e27a9960SSean Young "sector %d out of range\n",
127e27a9960SSean Young part->mbd.mtd->name, block_no, i, entry);
128e27a9960SSean Young continue;
129e27a9960SSean Young }
130e27a9960SSean Young
131e27a9960SSean Young if (part->sector_map[entry] != -1) {
132683b30c8SSean Young printk(KERN_WARNING PREFIX
133e27a9960SSean Young "'%s': more than one entry for sector %d\n",
134e27a9960SSean Young part->mbd.mtd->name, entry);
135e27a9960SSean Young part->errors = 1;
136e27a9960SSean Young continue;
137e27a9960SSean Young }
138e27a9960SSean Young
139e27a9960SSean Young part->sector_map[entry] = block->offset +
140e27a9960SSean Young (i + part->header_sectors_per_block) * SECTOR_SIZE;
141e27a9960SSean Young
142e27a9960SSean Young block->used_sectors++;
143e27a9960SSean Young }
144e27a9960SSean Young
145e27a9960SSean Young if (block->free_sectors == part->data_sectors_per_block)
146e27a9960SSean Young part->reserved_block = block_no;
147e27a9960SSean Young
148e27a9960SSean Young return 0;
149e27a9960SSean Young }
150e27a9960SSean Young
scan_header(struct partition * part)151e27a9960SSean Young static int scan_header(struct partition *part)
152e27a9960SSean Young {
153e27a9960SSean Young int sectors_per_block;
154e27a9960SSean Young int i, rc = -ENOMEM;
155e27a9960SSean Young int blocks_found;
156e27a9960SSean Young size_t retlen;
157e27a9960SSean Young
158e27a9960SSean Young sectors_per_block = part->block_size / SECTOR_SIZE;
15969423d99SAdrian Hunter part->total_blocks = (u32)part->mbd.mtd->size / part->block_size;
160e27a9960SSean Young
161e27a9960SSean Young if (part->total_blocks < 2)
162e27a9960SSean Young return -ENOENT;
163e27a9960SSean Young
164e27a9960SSean Young /* each erase block has three bytes header, followed by the map */
165e27a9960SSean Young part->header_sectors_per_block =
166e27a9960SSean Young ((HEADER_MAP_OFFSET + sectors_per_block) *
167e27a9960SSean Young sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
168e27a9960SSean Young
169e27a9960SSean Young part->data_sectors_per_block = sectors_per_block -
170e27a9960SSean Young part->header_sectors_per_block;
171e27a9960SSean Young
172e27a9960SSean Young part->header_size = (HEADER_MAP_OFFSET +
173e27a9960SSean Young part->data_sectors_per_block) * sizeof(u16);
174e27a9960SSean Young
175e27a9960SSean Young part->cylinders = (part->data_sectors_per_block *
176e27a9960SSean Young (part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;
177e27a9960SSean Young
178e27a9960SSean Young part->sector_count = part->cylinders * SECTORS_PER_TRACK;
179e27a9960SSean Young
180e27a9960SSean Young part->current_block = -1;
181e27a9960SSean Young part->reserved_block = -1;
182e27a9960SSean Young part->is_reclaiming = 0;
183e27a9960SSean Young
184e27a9960SSean Young part->header_cache = kmalloc(part->header_size, GFP_KERNEL);
185e27a9960SSean Young if (!part->header_cache)
186e27a9960SSean Young goto err;
187e27a9960SSean Young
188e27a9960SSean Young part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
189e27a9960SSean Young GFP_KERNEL);
190e27a9960SSean Young if (!part->blocks)
191e27a9960SSean Young goto err;
192e27a9960SSean Young
19342bc47b3SKees Cook part->sector_map = vmalloc(array_size(sizeof(u_long),
19442bc47b3SKees Cook part->sector_count));
195cba8b3bcSZhen Lei if (!part->sector_map)
196e27a9960SSean Young goto err;
197e27a9960SSean Young
198e27a9960SSean Young for (i=0; i<part->sector_count; i++)
199e27a9960SSean Young part->sector_map[i] = -1;
200e27a9960SSean Young
201e27a9960SSean Young for (i=0, blocks_found=0; i<part->total_blocks; i++) {
202329ad399SArtem Bityutskiy rc = mtd_read(part->mbd.mtd, i * part->block_size,
203329ad399SArtem Bityutskiy part->header_size, &retlen,
204329ad399SArtem Bityutskiy (u_char *)part->header_cache);
205e27a9960SSean Young
206e27a9960SSean Young if (!rc && retlen != part->header_size)
207e27a9960SSean Young rc = -EIO;
208e27a9960SSean Young
209e27a9960SSean Young if (rc)
210e27a9960SSean Young goto err;
211e27a9960SSean Young
212e27a9960SSean Young if (!build_block_map(part, i))
213e27a9960SSean Young blocks_found++;
214e27a9960SSean Young }
215e27a9960SSean Young
216e27a9960SSean Young if (blocks_found == 0) {
217e27a9960SSean Young printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
218e27a9960SSean Young part->mbd.mtd->name);
219e27a9960SSean Young rc = -ENOENT;
220e27a9960SSean Young goto err;
221e27a9960SSean Young }
222e27a9960SSean Young
223e27a9960SSean Young if (part->reserved_block == -1) {
224683b30c8SSean Young printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
225e27a9960SSean Young part->mbd.mtd->name);
226e27a9960SSean Young
227e27a9960SSean Young part->errors = 1;
228e27a9960SSean Young }
229e27a9960SSean Young
230e27a9960SSean Young return 0;
231e27a9960SSean Young
232e27a9960SSean Young err:
233e27a9960SSean Young vfree(part->sector_map);
234e27a9960SSean Young kfree(part->header_cache);
235e27a9960SSean Young kfree(part->blocks);
236e27a9960SSean Young
237e27a9960SSean Young return rc;
238e27a9960SSean Young }
239e27a9960SSean Young
rfd_ftl_readsect(struct mtd_blktrans_dev * dev,u_long sector,char * buf)240e27a9960SSean Young static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
241e27a9960SSean Young {
242fa451399SSean Young struct partition *part = container_of(dev, struct partition, mbd);
243e27a9960SSean Young u_long addr;
244e27a9960SSean Young size_t retlen;
245e27a9960SSean Young int rc;
246e27a9960SSean Young
247e27a9960SSean Young if (sector >= part->sector_count)
248e27a9960SSean Young return -EIO;
249e27a9960SSean Young
250e27a9960SSean Young addr = part->sector_map[sector];
251e27a9960SSean Young if (addr != -1) {
252329ad399SArtem Bityutskiy rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
253329ad399SArtem Bityutskiy (u_char *)buf);
254e27a9960SSean Young if (!rc && retlen != SECTOR_SIZE)
255e27a9960SSean Young rc = -EIO;
256e27a9960SSean Young
257e27a9960SSean Young if (rc) {
258e27a9960SSean Young printk(KERN_WARNING PREFIX "error reading '%s' at "
259e27a9960SSean Young "0x%lx\n", part->mbd.mtd->name, addr);
260e27a9960SSean Young return rc;
261e27a9960SSean Young }
262e27a9960SSean Young } else
263e27a9960SSean Young memset(buf, 0, SECTOR_SIZE);
264e27a9960SSean Young
265e27a9960SSean Young return 0;
266e27a9960SSean Young }
267e27a9960SSean Young
erase_block(struct partition * part,int block)268e27a9960SSean Young static int erase_block(struct partition *part, int block)
269e27a9960SSean Young {
270e27a9960SSean Young struct erase_info *erase;
271884cfd90SBoris Brezillon int rc;
272e27a9960SSean Young
273e27a9960SSean Young erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
274e27a9960SSean Young if (!erase)
275884cfd90SBoris Brezillon return -ENOMEM;
276e27a9960SSean Young
277e27a9960SSean Young erase->addr = part->blocks[block].offset;
278e27a9960SSean Young erase->len = part->block_size;
279e27a9960SSean Young
280e27a9960SSean Young part->blocks[block].state = BLOCK_ERASING;
281e27a9960SSean Young part->blocks[block].free_sectors = 0;
282e27a9960SSean Young
2837e1f0dc0SArtem Bityutskiy rc = mtd_erase(part->mbd.mtd, erase);
284e27a9960SSean Young if (rc) {
28569423d99SAdrian Hunter printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
28669423d99SAdrian Hunter "failed\n", (unsigned long long)erase->addr,
28769423d99SAdrian Hunter (unsigned long long)erase->len, part->mbd.mtd->name);
288884cfd90SBoris Brezillon part->blocks[block].state = BLOCK_FAILED;
289884cfd90SBoris Brezillon part->blocks[block].free_sectors = 0;
290884cfd90SBoris Brezillon part->blocks[block].used_sectors = 0;
291884cfd90SBoris Brezillon } else {
292884cfd90SBoris Brezillon u16 magic = cpu_to_le16(RFD_MAGIC);
293884cfd90SBoris Brezillon size_t retlen;
294884cfd90SBoris Brezillon
295884cfd90SBoris Brezillon part->blocks[block].state = BLOCK_ERASED;
296884cfd90SBoris Brezillon part->blocks[block].free_sectors = part->data_sectors_per_block;
297884cfd90SBoris Brezillon part->blocks[block].used_sectors = 0;
298884cfd90SBoris Brezillon part->blocks[block].erases++;
299884cfd90SBoris Brezillon
300884cfd90SBoris Brezillon rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
301884cfd90SBoris Brezillon sizeof(magic), &retlen, (u_char *)&magic);
302884cfd90SBoris Brezillon if (!rc && retlen != sizeof(magic))
303884cfd90SBoris Brezillon rc = -EIO;
304884cfd90SBoris Brezillon
305884cfd90SBoris Brezillon if (rc) {
306884cfd90SBoris Brezillon pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
307884cfd90SBoris Brezillon part->mbd.mtd->name, part->blocks[block].offset);
308884cfd90SBoris Brezillon part->blocks[block].state = BLOCK_FAILED;
309884cfd90SBoris Brezillon } else {
310884cfd90SBoris Brezillon part->blocks[block].state = BLOCK_OK;
311884cfd90SBoris Brezillon }
312e27a9960SSean Young }
313e27a9960SSean Young
314884cfd90SBoris Brezillon kfree(erase);
315884cfd90SBoris Brezillon
316e27a9960SSean Young return rc;
317e27a9960SSean Young }
318e27a9960SSean Young
move_block_contents(struct partition * part,int block_no,u_long * old_sector)319e27a9960SSean Young static int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
320e27a9960SSean Young {
321e27a9960SSean Young void *sector_data;
322e27a9960SSean Young u16 *map;
323e27a9960SSean Young size_t retlen;
324e27a9960SSean Young int i, rc = -ENOMEM;
325e27a9960SSean Young
326e27a9960SSean Young part->is_reclaiming = 1;
327e27a9960SSean Young
328e27a9960SSean Young sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
329e27a9960SSean Young if (!sector_data)
330e27a9960SSean Young goto err3;
331e27a9960SSean Young
332e27a9960SSean Young map = kmalloc(part->header_size, GFP_KERNEL);
333e27a9960SSean Young if (!map)
334e27a9960SSean Young goto err2;
335e27a9960SSean Young
336329ad399SArtem Bityutskiy rc = mtd_read(part->mbd.mtd, part->blocks[block_no].offset,
337329ad399SArtem Bityutskiy part->header_size, &retlen, (u_char *)map);
338e27a9960SSean Young
339e27a9960SSean Young if (!rc && retlen != part->header_size)
340e27a9960SSean Young rc = -EIO;
341e27a9960SSean Young
342e27a9960SSean Young if (rc) {
343683b30c8SSean Young printk(KERN_ERR PREFIX "error reading '%s' at "
344e27a9960SSean Young "0x%lx\n", part->mbd.mtd->name,
345e27a9960SSean Young part->blocks[block_no].offset);
346e27a9960SSean Young
347e27a9960SSean Young goto err;
348e27a9960SSean Young }
349e27a9960SSean Young
350e27a9960SSean Young for (i=0; i<part->data_sectors_per_block; i++) {
351e27a9960SSean Young u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
352e27a9960SSean Young u_long addr;
353e27a9960SSean Young
354e27a9960SSean Young
355e27a9960SSean Young if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
356e27a9960SSean Young continue;
357e27a9960SSean Young
358e27a9960SSean Young if (entry == SECTOR_ZERO)
359e27a9960SSean Young entry = 0;
360e27a9960SSean Young
361e27a9960SSean Young /* already warned about and ignored in build_block_map() */
362e27a9960SSean Young if (entry >= part->sector_count)
363e27a9960SSean Young continue;
364e27a9960SSean Young
365e27a9960SSean Young addr = part->blocks[block_no].offset +
366e27a9960SSean Young (i + part->header_sectors_per_block) * SECTOR_SIZE;
367e27a9960SSean Young
368e27a9960SSean Young if (*old_sector == addr) {
369e27a9960SSean Young *old_sector = -1;
370e27a9960SSean Young if (!part->blocks[block_no].used_sectors--) {
371e27a9960SSean Young rc = erase_block(part, block_no);
372e27a9960SSean Young break;
373e27a9960SSean Young }
374e27a9960SSean Young continue;
375e27a9960SSean Young }
376329ad399SArtem Bityutskiy rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
377329ad399SArtem Bityutskiy sector_data);
378e27a9960SSean Young
379e27a9960SSean Young if (!rc && retlen != SECTOR_SIZE)
380e27a9960SSean Young rc = -EIO;
381e27a9960SSean Young
382e27a9960SSean Young if (rc) {
383683b30c8SSean Young printk(KERN_ERR PREFIX "'%s': Unable to "
384e27a9960SSean Young "read sector for relocation\n",
385e27a9960SSean Young part->mbd.mtd->name);
386e27a9960SSean Young
387e27a9960SSean Young goto err;
388e27a9960SSean Young }
389e27a9960SSean Young
390e27a9960SSean Young rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
391e27a9960SSean Young entry, sector_data);
392e27a9960SSean Young
393e27a9960SSean Young if (rc)
394e27a9960SSean Young goto err;
395e27a9960SSean Young }
396e27a9960SSean Young
397e27a9960SSean Young err:
398e27a9960SSean Young kfree(map);
399e27a9960SSean Young err2:
400e27a9960SSean Young kfree(sector_data);
401e27a9960SSean Young err3:
402e27a9960SSean Young part->is_reclaiming = 0;
403e27a9960SSean Young
404e27a9960SSean Young return rc;
405e27a9960SSean Young }
406e27a9960SSean Young
reclaim_block(struct partition * part,u_long * old_sector)407e27a9960SSean Young static int reclaim_block(struct partition *part, u_long *old_sector)
408e27a9960SSean Young {
409e27a9960SSean Young int block, best_block, score, old_sector_block;
410e27a9960SSean Young int rc;
411e27a9960SSean Young
412e27a9960SSean Young /* we have a race if sync doesn't exist */
41385f2f2a8SArtem Bityutskiy mtd_sync(part->mbd.mtd);
414e27a9960SSean Young
415e27a9960SSean Young score = 0x7fffffff; /* MAX_INT */
416e27a9960SSean Young best_block = -1;
417e27a9960SSean Young if (*old_sector != -1)
418e27a9960SSean Young old_sector_block = *old_sector / part->block_size;
419e27a9960SSean Young else
420e27a9960SSean Young old_sector_block = -1;
421e27a9960SSean Young
422e27a9960SSean Young for (block=0; block<part->total_blocks; block++) {
423e27a9960SSean Young int this_score;
424e27a9960SSean Young
425e27a9960SSean Young if (block == part->reserved_block)
426e27a9960SSean Young continue;
427e27a9960SSean Young
428e27a9960SSean Young /*
429e27a9960SSean Young * Postpone reclaiming if there is a free sector as
430e27a9960SSean Young * more removed sectors is more efficient (have to move
431e27a9960SSean Young * less).
432e27a9960SSean Young */
433e27a9960SSean Young if (part->blocks[block].free_sectors)
434e27a9960SSean Young return 0;
435e27a9960SSean Young
436e27a9960SSean Young this_score = part->blocks[block].used_sectors;
437e27a9960SSean Young
438e27a9960SSean Young if (block == old_sector_block)
439e27a9960SSean Young this_score--;
440e27a9960SSean Young else {
441e27a9960SSean Young /* no point in moving a full block */
442e27a9960SSean Young if (part->blocks[block].used_sectors ==
443e27a9960SSean Young part->data_sectors_per_block)
444e27a9960SSean Young continue;
445e27a9960SSean Young }
446e27a9960SSean Young
447e27a9960SSean Young this_score += part->blocks[block].erases;
448e27a9960SSean Young
449e27a9960SSean Young if (this_score < score) {
450e27a9960SSean Young best_block = block;
451e27a9960SSean Young score = this_score;
452e27a9960SSean Young }
453e27a9960SSean Young }
454e27a9960SSean Young
455e27a9960SSean Young if (best_block == -1)
456e27a9960SSean Young return -ENOSPC;
457e27a9960SSean Young
458e27a9960SSean Young part->current_block = -1;
459e27a9960SSean Young part->reserved_block = best_block;
460e27a9960SSean Young
461e27a9960SSean Young pr_debug("reclaim_block: reclaiming block #%d with %d used "
462e27a9960SSean Young "%d free sectors\n", best_block,
463e27a9960SSean Young part->blocks[best_block].used_sectors,
464e27a9960SSean Young part->blocks[best_block].free_sectors);
465e27a9960SSean Young
466e27a9960SSean Young if (part->blocks[best_block].used_sectors)
467e27a9960SSean Young rc = move_block_contents(part, best_block, old_sector);
468e27a9960SSean Young else
469e27a9960SSean Young rc = erase_block(part, best_block);
470e27a9960SSean Young
471e27a9960SSean Young return rc;
472e27a9960SSean Young }
473e27a9960SSean Young
474e27a9960SSean Young /*
475e27a9960SSean Young * IMPROVE: It would be best to choose the block with the most deleted sectors,
476e27a9960SSean Young * because if we fill that one up first it'll have the most chance of having
477e27a9960SSean Young * the least live sectors at reclaim.
478e27a9960SSean Young */
find_free_block(struct partition * part)479683b30c8SSean Young static int find_free_block(struct partition *part)
480e27a9960SSean Young {
481e27a9960SSean Young int block, stop;
482e27a9960SSean Young
483e27a9960SSean Young block = part->current_block == -1 ?
484e27a9960SSean Young jiffies % part->total_blocks : part->current_block;
485e27a9960SSean Young stop = block;
486e27a9960SSean Young
487e27a9960SSean Young do {
488e27a9960SSean Young if (part->blocks[block].free_sectors &&
489e27a9960SSean Young block != part->reserved_block)
490e27a9960SSean Young return block;
491e27a9960SSean Young
492683b30c8SSean Young if (part->blocks[block].state == BLOCK_UNUSED)
493683b30c8SSean Young erase_block(part, block);
494683b30c8SSean Young
495e27a9960SSean Young if (++block >= part->total_blocks)
496e27a9960SSean Young block = 0;
497e27a9960SSean Young
498e27a9960SSean Young } while (block != stop);
499e27a9960SSean Young
500e27a9960SSean Young return -1;
501e27a9960SSean Young }
502e27a9960SSean Young
find_writable_block(struct partition * part,u_long * old_sector)503683b30c8SSean Young static int find_writable_block(struct partition *part, u_long *old_sector)
504e27a9960SSean Young {
505e27a9960SSean Young int rc, block;
506e27a9960SSean Young size_t retlen;
507e27a9960SSean Young
508e27a9960SSean Young block = find_free_block(part);
509e27a9960SSean Young
510e27a9960SSean Young if (block == -1) {
511e27a9960SSean Young if (!part->is_reclaiming) {
512e27a9960SSean Young rc = reclaim_block(part, old_sector);
513e27a9960SSean Young if (rc)
514e27a9960SSean Young goto err;
515e27a9960SSean Young
516e27a9960SSean Young block = find_free_block(part);
517e27a9960SSean Young }
518e27a9960SSean Young
519e27a9960SSean Young if (block == -1) {
520e27a9960SSean Young rc = -ENOSPC;
521e27a9960SSean Young goto err;
522e27a9960SSean Young }
523e27a9960SSean Young }
524e27a9960SSean Young
525329ad399SArtem Bityutskiy rc = mtd_read(part->mbd.mtd, part->blocks[block].offset,
526329ad399SArtem Bityutskiy part->header_size, &retlen,
527329ad399SArtem Bityutskiy (u_char *)part->header_cache);
528e27a9960SSean Young
529e27a9960SSean Young if (!rc && retlen != part->header_size)
530e27a9960SSean Young rc = -EIO;
531e27a9960SSean Young
532e27a9960SSean Young if (rc) {
533683b30c8SSean Young printk(KERN_ERR PREFIX "'%s': unable to read header at "
534e27a9960SSean Young "0x%lx\n", part->mbd.mtd->name,
535e27a9960SSean Young part->blocks[block].offset);
536e27a9960SSean Young goto err;
537e27a9960SSean Young }
538e27a9960SSean Young
539e27a9960SSean Young part->current_block = block;
540e27a9960SSean Young
541e27a9960SSean Young err:
542e27a9960SSean Young return rc;
543e27a9960SSean Young }
544e27a9960SSean Young
mark_sector_deleted(struct partition * part,u_long old_addr)545e27a9960SSean Young static int mark_sector_deleted(struct partition *part, u_long old_addr)
546e27a9960SSean Young {
547e27a9960SSean Young int block, offset, rc;
548e27a9960SSean Young u_long addr;
549e27a9960SSean Young size_t retlen;
550c80a7b26SHarvey Harrison u16 del = cpu_to_le16(SECTOR_DELETED);
551e27a9960SSean Young
552e27a9960SSean Young block = old_addr / part->block_size;
553e27a9960SSean Young offset = (old_addr % part->block_size) / SECTOR_SIZE -
554e27a9960SSean Young part->header_sectors_per_block;
555e27a9960SSean Young
556e27a9960SSean Young addr = part->blocks[block].offset +
557e27a9960SSean Young (HEADER_MAP_OFFSET + offset) * sizeof(u16);
558eda95cbfSArtem Bityutskiy rc = mtd_write(part->mbd.mtd, addr, sizeof(del), &retlen,
559eda95cbfSArtem Bityutskiy (u_char *)&del);
560e27a9960SSean Young
561e27a9960SSean Young if (!rc && retlen != sizeof(del))
562e27a9960SSean Young rc = -EIO;
563e27a9960SSean Young
564e27a9960SSean Young if (rc) {
565683b30c8SSean Young printk(KERN_ERR PREFIX "error writing '%s' at "
566e27a9960SSean Young "0x%lx\n", part->mbd.mtd->name, addr);
567e27a9960SSean Young goto err;
568e27a9960SSean Young }
569e27a9960SSean Young if (block == part->current_block)
570e27a9960SSean Young part->header_cache[offset + HEADER_MAP_OFFSET] = del;
571e27a9960SSean Young
572e27a9960SSean Young part->blocks[block].used_sectors--;
573e27a9960SSean Young
574e27a9960SSean Young if (!part->blocks[block].used_sectors &&
575e27a9960SSean Young !part->blocks[block].free_sectors)
576e27a9960SSean Young rc = erase_block(part, block);
577e27a9960SSean Young
578e27a9960SSean Young err:
579e27a9960SSean Young return rc;
580e27a9960SSean Young }
581e27a9960SSean Young
find_free_sector(const struct partition * part,const struct block * block)582e27a9960SSean Young static int find_free_sector(const struct partition *part, const struct block *block)
583e27a9960SSean Young {
584e27a9960SSean Young int i, stop;
585e27a9960SSean Young
586e27a9960SSean Young i = stop = part->data_sectors_per_block - block->free_sectors;
587e27a9960SSean Young
588e27a9960SSean Young do {
589e27a9960SSean Young if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
590e27a9960SSean Young == SECTOR_FREE)
591e27a9960SSean Young return i;
592e27a9960SSean Young
593e27a9960SSean Young if (++i == part->data_sectors_per_block)
594e27a9960SSean Young i = 0;
595e27a9960SSean Young }
596e27a9960SSean Young while(i != stop);
597e27a9960SSean Young
598e27a9960SSean Young return -1;
599e27a9960SSean Young }
600e27a9960SSean Young
do_writesect(struct mtd_blktrans_dev * dev,u_long sector,char * buf,ulong * old_addr)601e27a9960SSean Young static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
602e27a9960SSean Young {
603fa451399SSean Young struct partition *part = container_of(dev, struct partition, mbd);
604e27a9960SSean Young struct block *block;
605e27a9960SSean Young u_long addr;
606e27a9960SSean Young int i;
607e27a9960SSean Young int rc;
608e27a9960SSean Young size_t retlen;
609e27a9960SSean Young u16 entry;
610e27a9960SSean Young
611e27a9960SSean Young if (part->current_block == -1 ||
612e27a9960SSean Young !part->blocks[part->current_block].free_sectors) {
613e27a9960SSean Young
614683b30c8SSean Young rc = find_writable_block(part, old_addr);
615e27a9960SSean Young if (rc)
616e27a9960SSean Young goto err;
617e27a9960SSean Young }
618e27a9960SSean Young
619e27a9960SSean Young block = &part->blocks[part->current_block];
620e27a9960SSean Young
621e27a9960SSean Young i = find_free_sector(part, block);
622e27a9960SSean Young
623e27a9960SSean Young if (i < 0) {
624e27a9960SSean Young rc = -ENOSPC;
625e27a9960SSean Young goto err;
626e27a9960SSean Young }
627e27a9960SSean Young
628e27a9960SSean Young addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
629e27a9960SSean Young block->offset;
630eda95cbfSArtem Bityutskiy rc = mtd_write(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
631eda95cbfSArtem Bityutskiy (u_char *)buf);
632e27a9960SSean Young
633e27a9960SSean Young if (!rc && retlen != SECTOR_SIZE)
634e27a9960SSean Young rc = -EIO;
635e27a9960SSean Young
636e27a9960SSean Young if (rc) {
637683b30c8SSean Young printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
638e27a9960SSean Young part->mbd.mtd->name, addr);
639e27a9960SSean Young goto err;
640e27a9960SSean Young }
641e27a9960SSean Young
642e27a9960SSean Young part->sector_map[sector] = addr;
643e27a9960SSean Young
644e27a9960SSean Young entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
645e27a9960SSean Young
646e27a9960SSean Young part->header_cache[i + HEADER_MAP_OFFSET] = entry;
647e27a9960SSean Young
648e27a9960SSean Young addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
649eda95cbfSArtem Bityutskiy rc = mtd_write(part->mbd.mtd, addr, sizeof(entry), &retlen,
650eda95cbfSArtem Bityutskiy (u_char *)&entry);
651e27a9960SSean Young
652e27a9960SSean Young if (!rc && retlen != sizeof(entry))
653e27a9960SSean Young rc = -EIO;
654e27a9960SSean Young
655e27a9960SSean Young if (rc) {
656683b30c8SSean Young printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
657e27a9960SSean Young part->mbd.mtd->name, addr);
658e27a9960SSean Young goto err;
659e27a9960SSean Young }
660e27a9960SSean Young block->used_sectors++;
661e27a9960SSean Young block->free_sectors--;
662e27a9960SSean Young
663e27a9960SSean Young err:
664e27a9960SSean Young return rc;
665e27a9960SSean Young }
666e27a9960SSean Young
rfd_ftl_writesect(struct mtd_blktrans_dev * dev,u_long sector,char * buf)667e27a9960SSean Young static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
668e27a9960SSean Young {
669fa451399SSean Young struct partition *part = container_of(dev, struct partition, mbd);
670e27a9960SSean Young u_long old_addr;
671e27a9960SSean Young int i;
672e27a9960SSean Young int rc = 0;
673e27a9960SSean Young
674e27a9960SSean Young pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
675e27a9960SSean Young
676e27a9960SSean Young if (part->reserved_block == -1) {
677e27a9960SSean Young rc = -EACCES;
678e27a9960SSean Young goto err;
679e27a9960SSean Young }
680e27a9960SSean Young
681e27a9960SSean Young if (sector >= part->sector_count) {
682e27a9960SSean Young rc = -EIO;
683e27a9960SSean Young goto err;
684e27a9960SSean Young }
685e27a9960SSean Young
686e27a9960SSean Young old_addr = part->sector_map[sector];
687e27a9960SSean Young
688e27a9960SSean Young for (i=0; i<SECTOR_SIZE; i++) {
689e27a9960SSean Young if (!buf[i])
690e27a9960SSean Young continue;
691e27a9960SSean Young
692e27a9960SSean Young rc = do_writesect(dev, sector, buf, &old_addr);
693e27a9960SSean Young if (rc)
694e27a9960SSean Young goto err;
695e27a9960SSean Young break;
696e27a9960SSean Young }
697e27a9960SSean Young
698e27a9960SSean Young if (i == SECTOR_SIZE)
699e27a9960SSean Young part->sector_map[sector] = -1;
700e27a9960SSean Young
701e27a9960SSean Young if (old_addr != -1)
702e27a9960SSean Young rc = mark_sector_deleted(part, old_addr);
703e27a9960SSean Young
704e27a9960SSean Young err:
705e27a9960SSean Young return rc;
706e27a9960SSean Young }
707e27a9960SSean Young
rfd_ftl_discardsect(struct mtd_blktrans_dev * dev,unsigned long sector,unsigned int nr_sects)708a3a44784SSean Young static int rfd_ftl_discardsect(struct mtd_blktrans_dev *dev,
709a3a44784SSean Young unsigned long sector, unsigned int nr_sects)
710a3a44784SSean Young {
711fa451399SSean Young struct partition *part = container_of(dev, struct partition, mbd);
712a3a44784SSean Young u_long addr;
713a3a44784SSean Young int rc;
714a3a44784SSean Young
715a3a44784SSean Young while (nr_sects) {
716a3a44784SSean Young if (sector >= part->sector_count)
717a3a44784SSean Young return -EIO;
718a3a44784SSean Young
719a3a44784SSean Young addr = part->sector_map[sector];
720a3a44784SSean Young
721a3a44784SSean Young if (addr != -1) {
722a3a44784SSean Young rc = mark_sector_deleted(part, addr);
723a3a44784SSean Young if (rc)
724a3a44784SSean Young return rc;
725a3a44784SSean Young
726a3a44784SSean Young part->sector_map[sector] = -1;
727a3a44784SSean Young }
728a3a44784SSean Young
729a3a44784SSean Young sector++;
730a3a44784SSean Young nr_sects--;
731a3a44784SSean Young }
732a3a44784SSean Young
733a3a44784SSean Young return 0;
734a3a44784SSean Young }
735a3a44784SSean Young
rfd_ftl_getgeo(struct mtd_blktrans_dev * dev,struct hd_geometry * geo)736e27a9960SSean Young static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
737e27a9960SSean Young {
738fa451399SSean Young struct partition *part = container_of(dev, struct partition, mbd);
739e27a9960SSean Young
740e27a9960SSean Young geo->heads = 1;
741e27a9960SSean Young geo->sectors = SECTORS_PER_TRACK;
742e27a9960SSean Young geo->cylinders = part->cylinders;
743e27a9960SSean Young
744e27a9960SSean Young return 0;
745e27a9960SSean Young }
746e27a9960SSean Young
rfd_ftl_add_mtd(struct mtd_blktrans_ops * tr,struct mtd_info * mtd)747e27a9960SSean Young static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
748e27a9960SSean Young {
749e27a9960SSean Young struct partition *part;
750e27a9960SSean Young
751e03a8121SSean Young if ((mtd->type != MTD_NORFLASH && mtd->type != MTD_RAM) ||
752e03a8121SSean Young mtd->size > UINT_MAX)
753e27a9960SSean Young return;
754e27a9960SSean Young
755cd861280SRobert P. J. Day part = kzalloc(sizeof(struct partition), GFP_KERNEL);
756e27a9960SSean Young if (!part)
757e27a9960SSean Young return;
758e27a9960SSean Young
759e27a9960SSean Young part->mbd.mtd = mtd;
760e27a9960SSean Young
761e27a9960SSean Young if (block_size)
762e27a9960SSean Young part->block_size = block_size;
763e27a9960SSean Young else {
764e27a9960SSean Young if (!mtd->erasesize) {
765683b30c8SSean Young printk(KERN_WARNING PREFIX "please provide block_size");
766030f9e13Sakpm@linux-foundation.org goto out;
767030f9e13Sakpm@linux-foundation.org } else
768e27a9960SSean Young part->block_size = mtd->erasesize;
769e27a9960SSean Young }
770e27a9960SSean Young
771e27a9960SSean Young if (scan_header(part) == 0) {
772e27a9960SSean Young part->mbd.size = part->sector_count;
773e27a9960SSean Young part->mbd.tr = tr;
774e27a9960SSean Young part->mbd.devnum = -1;
775e27a9960SSean Young if (!(mtd->flags & MTD_WRITEABLE))
776e27a9960SSean Young part->mbd.readonly = 1;
777e27a9960SSean Young else if (part->errors) {
778683b30c8SSean Young printk(KERN_WARNING PREFIX "'%s': errors found, "
779683b30c8SSean Young "setting read-only\n", mtd->name);
780e27a9960SSean Young part->mbd.readonly = 1;
781e27a9960SSean Young }
782e27a9960SSean Young
783e27a9960SSean Young printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
784e27a9960SSean Young mtd->name, mtd->type, mtd->flags);
785e27a9960SSean Young
786*a0faf5fdSChristoph Hellwig if (!add_mtd_blktrans_dev(&part->mbd))
787e27a9960SSean Young return;
788e27a9960SSean Young }
789030f9e13Sakpm@linux-foundation.org out:
790e27a9960SSean Young kfree(part);
791e27a9960SSean Young }
792e27a9960SSean Young
rfd_ftl_remove_dev(struct mtd_blktrans_dev * dev)793e27a9960SSean Young static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
794e27a9960SSean Young {
795fa451399SSean Young struct partition *part = container_of(dev, struct partition, mbd);
796e27a9960SSean Young int i;
797e27a9960SSean Young
798e27a9960SSean Young for (i=0; i<part->total_blocks; i++) {
799e27a9960SSean Young pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
800e27a9960SSean Young part->mbd.mtd->name, i, part->blocks[i].erases);
801e27a9960SSean Young }
802e27a9960SSean Young
803e27a9960SSean Young vfree(part->sector_map);
804e27a9960SSean Young kfree(part->header_cache);
805e27a9960SSean Young kfree(part->blocks);
806fa451399SSean Young del_mtd_blktrans_dev(&part->mbd);
807e27a9960SSean Young }
808e27a9960SSean Young
8097fe9296cSAdrian Bunk static struct mtd_blktrans_ops rfd_ftl_tr = {
810e27a9960SSean Young .name = "rfd",
811e27a9960SSean Young .major = RFD_FTL_MAJOR,
812e27a9960SSean Young .part_bits = PART_BITS,
81319187672SRichard Purdie .blksize = SECTOR_SIZE,
81419187672SRichard Purdie
815e27a9960SSean Young .readsect = rfd_ftl_readsect,
816e27a9960SSean Young .writesect = rfd_ftl_writesect,
817a3a44784SSean Young .discard = rfd_ftl_discardsect,
818e27a9960SSean Young .getgeo = rfd_ftl_getgeo,
819e27a9960SSean Young .add_mtd = rfd_ftl_add_mtd,
820e27a9960SSean Young .remove_dev = rfd_ftl_remove_dev,
821e27a9960SSean Young .owner = THIS_MODULE,
822e27a9960SSean Young };
823e27a9960SSean Young
824d38c2b93SDejin Zheng module_mtd_blktrans(rfd_ftl_tr);
825e27a9960SSean Young
826e27a9960SSean Young MODULE_LICENSE("GPL");
827e27a9960SSean Young MODULE_AUTHOR("Sean Young <sean@mess.org>");
828e27a9960SSean Young MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
829e27a9960SSean Young "used by General Software's Embedded BIOS");
830e27a9960SSean Young
831