18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * rfd_ftl.c -- resident flash disk (flash translation layer)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright © 2005  Sean Young <sean@mess.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This type of flash translation layer (FTL) is used by the Embedded BIOS
88c2ecf20Sopenharmony_ci * by General Software. It is known as the Resident Flash Disk (RFD), see:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *	http://www.gensw.com/pages/prod/bios/rfd.htm
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * based on ftl.c
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/hdreg.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/mtd/blktrans.h>
188c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
198c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <asm/types.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int block_size = 0;
278c2ecf20Sopenharmony_cimodule_param(block_size, int, 0);
288c2ecf20Sopenharmony_ciMODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define PREFIX "rfd_ftl: "
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* This major has been assigned by device@lanana.org */
338c2ecf20Sopenharmony_ci#ifndef RFD_FTL_MAJOR
348c2ecf20Sopenharmony_ci#define RFD_FTL_MAJOR		256
358c2ecf20Sopenharmony_ci#endif
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Maximum number of partitions in an FTL region */
388c2ecf20Sopenharmony_ci#define PART_BITS		4
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* An erase unit should start with this value */
418c2ecf20Sopenharmony_ci#define RFD_MAGIC		0x9193
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* the second value is 0xffff or 0xffc8; function unknown */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* the third value is always 0xffff, ignored */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* next is an array of mapping for each corresponding sector */
488c2ecf20Sopenharmony_ci#define HEADER_MAP_OFFSET	3
498c2ecf20Sopenharmony_ci#define SECTOR_DELETED		0x0000
508c2ecf20Sopenharmony_ci#define SECTOR_ZERO		0xfffe
518c2ecf20Sopenharmony_ci#define SECTOR_FREE		0xffff
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define SECTOR_SIZE		512
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define SECTORS_PER_TRACK	63
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct block {
588c2ecf20Sopenharmony_ci	enum {
598c2ecf20Sopenharmony_ci		BLOCK_OK,
608c2ecf20Sopenharmony_ci		BLOCK_ERASING,
618c2ecf20Sopenharmony_ci		BLOCK_ERASED,
628c2ecf20Sopenharmony_ci		BLOCK_UNUSED,
638c2ecf20Sopenharmony_ci		BLOCK_FAILED
648c2ecf20Sopenharmony_ci	} state;
658c2ecf20Sopenharmony_ci	int free_sectors;
668c2ecf20Sopenharmony_ci	int used_sectors;
678c2ecf20Sopenharmony_ci	int erases;
688c2ecf20Sopenharmony_ci	u_long offset;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct partition {
728c2ecf20Sopenharmony_ci	struct mtd_blktrans_dev mbd;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	u_int block_size;		/* size of erase unit */
758c2ecf20Sopenharmony_ci	u_int total_blocks;		/* number of erase units */
768c2ecf20Sopenharmony_ci	u_int header_sectors_per_block;	/* header sectors in erase unit */
778c2ecf20Sopenharmony_ci	u_int data_sectors_per_block;	/* data sectors in erase unit */
788c2ecf20Sopenharmony_ci	u_int sector_count;		/* sectors in translated disk */
798c2ecf20Sopenharmony_ci	u_int header_size;		/* bytes in header sector */
808c2ecf20Sopenharmony_ci	int reserved_block;		/* block next up for reclaim */
818c2ecf20Sopenharmony_ci	int current_block;		/* block to write to */
828c2ecf20Sopenharmony_ci	u16 *header_cache;		/* cached header */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	int is_reclaiming;
858c2ecf20Sopenharmony_ci	int cylinders;
868c2ecf20Sopenharmony_ci	int errors;
878c2ecf20Sopenharmony_ci	u_long *sector_map;
888c2ecf20Sopenharmony_ci	struct block *blocks;
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int build_block_map(struct partition *part, int block_no)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct block *block = &part->blocks[block_no];
968c2ecf20Sopenharmony_ci	int i;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	block->offset = part->block_size * block_no;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
1018c2ecf20Sopenharmony_ci		block->state = BLOCK_UNUSED;
1028c2ecf20Sopenharmony_ci		return -ENOENT;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	block->state = BLOCK_OK;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	for (i=0; i<part->data_sectors_per_block; i++) {
1088c2ecf20Sopenharmony_ci		u16 entry;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if (entry == SECTOR_DELETED)
1138c2ecf20Sopenharmony_ci			continue;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		if (entry == SECTOR_FREE) {
1168c2ecf20Sopenharmony_ci			block->free_sectors++;
1178c2ecf20Sopenharmony_ci			continue;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		if (entry == SECTOR_ZERO)
1218c2ecf20Sopenharmony_ci			entry = 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		if (entry >= part->sector_count) {
1248c2ecf20Sopenharmony_ci			printk(KERN_WARNING PREFIX
1258c2ecf20Sopenharmony_ci				"'%s': unit #%d: entry %d corrupt, "
1268c2ecf20Sopenharmony_ci				"sector %d out of range\n",
1278c2ecf20Sopenharmony_ci				part->mbd.mtd->name, block_no, i, entry);
1288c2ecf20Sopenharmony_ci			continue;
1298c2ecf20Sopenharmony_ci		}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		if (part->sector_map[entry] != -1) {
1328c2ecf20Sopenharmony_ci			printk(KERN_WARNING PREFIX
1338c2ecf20Sopenharmony_ci				"'%s': more than one entry for sector %d\n",
1348c2ecf20Sopenharmony_ci				part->mbd.mtd->name, entry);
1358c2ecf20Sopenharmony_ci			part->errors = 1;
1368c2ecf20Sopenharmony_ci			continue;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		part->sector_map[entry] = block->offset +
1408c2ecf20Sopenharmony_ci			(i + part->header_sectors_per_block) * SECTOR_SIZE;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci		block->used_sectors++;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (block->free_sectors == part->data_sectors_per_block)
1468c2ecf20Sopenharmony_ci		part->reserved_block = block_no;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return 0;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int scan_header(struct partition *part)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	int sectors_per_block;
1548c2ecf20Sopenharmony_ci	int i, rc = -ENOMEM;
1558c2ecf20Sopenharmony_ci	int blocks_found;
1568c2ecf20Sopenharmony_ci	size_t retlen;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	sectors_per_block = part->block_size / SECTOR_SIZE;
1598c2ecf20Sopenharmony_ci	part->total_blocks = (u32)part->mbd.mtd->size / part->block_size;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (part->total_blocks < 2)
1628c2ecf20Sopenharmony_ci		return -ENOENT;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* each erase block has three bytes header, followed by the map */
1658c2ecf20Sopenharmony_ci	part->header_sectors_per_block =
1668c2ecf20Sopenharmony_ci			((HEADER_MAP_OFFSET + sectors_per_block) *
1678c2ecf20Sopenharmony_ci			sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	part->data_sectors_per_block = sectors_per_block -
1708c2ecf20Sopenharmony_ci			part->header_sectors_per_block;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	part->header_size = (HEADER_MAP_OFFSET +
1738c2ecf20Sopenharmony_ci			part->data_sectors_per_block) * sizeof(u16);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	part->cylinders = (part->data_sectors_per_block *
1768c2ecf20Sopenharmony_ci			(part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	part->sector_count = part->cylinders * SECTORS_PER_TRACK;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	part->current_block = -1;
1818c2ecf20Sopenharmony_ci	part->reserved_block = -1;
1828c2ecf20Sopenharmony_ci	part->is_reclaiming = 0;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	part->header_cache = kmalloc(part->header_size, GFP_KERNEL);
1858c2ecf20Sopenharmony_ci	if (!part->header_cache)
1868c2ecf20Sopenharmony_ci		goto err;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
1898c2ecf20Sopenharmony_ci			GFP_KERNEL);
1908c2ecf20Sopenharmony_ci	if (!part->blocks)
1918c2ecf20Sopenharmony_ci		goto err;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	part->sector_map = vmalloc(array_size(sizeof(u_long),
1948c2ecf20Sopenharmony_ci					      part->sector_count));
1958c2ecf20Sopenharmony_ci	if (!part->sector_map) {
1968c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "'%s': unable to allocate memory for "
1978c2ecf20Sopenharmony_ci			"sector map", part->mbd.mtd->name);
1988c2ecf20Sopenharmony_ci		goto err;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	for (i=0; i<part->sector_count; i++)
2028c2ecf20Sopenharmony_ci		part->sector_map[i] = -1;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i=0, blocks_found=0; i<part->total_blocks; i++) {
2058c2ecf20Sopenharmony_ci		rc = mtd_read(part->mbd.mtd, i * part->block_size,
2068c2ecf20Sopenharmony_ci			      part->header_size, &retlen,
2078c2ecf20Sopenharmony_ci			      (u_char *)part->header_cache);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		if (!rc && retlen != part->header_size)
2108c2ecf20Sopenharmony_ci			rc = -EIO;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (rc)
2138c2ecf20Sopenharmony_ci			goto err;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		if (!build_block_map(part, i))
2168c2ecf20Sopenharmony_ci			blocks_found++;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (blocks_found == 0) {
2208c2ecf20Sopenharmony_ci		printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
2218c2ecf20Sopenharmony_ci				part->mbd.mtd->name);
2228c2ecf20Sopenharmony_ci		rc = -ENOENT;
2238c2ecf20Sopenharmony_ci		goto err;
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (part->reserved_block == -1) {
2278c2ecf20Sopenharmony_ci		printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
2288c2ecf20Sopenharmony_ci				part->mbd.mtd->name);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		part->errors = 1;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cierr:
2368c2ecf20Sopenharmony_ci	vfree(part->sector_map);
2378c2ecf20Sopenharmony_ci	kfree(part->header_cache);
2388c2ecf20Sopenharmony_ci	kfree(part->blocks);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return rc;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct partition *part = (struct partition*)dev;
2468c2ecf20Sopenharmony_ci	u_long addr;
2478c2ecf20Sopenharmony_ci	size_t retlen;
2488c2ecf20Sopenharmony_ci	int rc;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (sector >= part->sector_count)
2518c2ecf20Sopenharmony_ci		return -EIO;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	addr = part->sector_map[sector];
2548c2ecf20Sopenharmony_ci	if (addr != -1) {
2558c2ecf20Sopenharmony_ci		rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
2568c2ecf20Sopenharmony_ci			      (u_char *)buf);
2578c2ecf20Sopenharmony_ci		if (!rc && retlen != SECTOR_SIZE)
2588c2ecf20Sopenharmony_ci			rc = -EIO;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		if (rc) {
2618c2ecf20Sopenharmony_ci			printk(KERN_WARNING PREFIX "error reading '%s' at "
2628c2ecf20Sopenharmony_ci				"0x%lx\n", part->mbd.mtd->name, addr);
2638c2ecf20Sopenharmony_ci			return rc;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci	} else
2668c2ecf20Sopenharmony_ci		memset(buf, 0, SECTOR_SIZE);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	return 0;
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic int erase_block(struct partition *part, int block)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct erase_info *erase;
2748c2ecf20Sopenharmony_ci	int rc;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
2778c2ecf20Sopenharmony_ci	if (!erase)
2788c2ecf20Sopenharmony_ci		return -ENOMEM;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	erase->addr = part->blocks[block].offset;
2818c2ecf20Sopenharmony_ci	erase->len = part->block_size;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	part->blocks[block].state = BLOCK_ERASING;
2848c2ecf20Sopenharmony_ci	part->blocks[block].free_sectors = 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	rc = mtd_erase(part->mbd.mtd, erase);
2878c2ecf20Sopenharmony_ci	if (rc) {
2888c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
2898c2ecf20Sopenharmony_ci				"failed\n", (unsigned long long)erase->addr,
2908c2ecf20Sopenharmony_ci				(unsigned long long)erase->len, part->mbd.mtd->name);
2918c2ecf20Sopenharmony_ci		part->blocks[block].state = BLOCK_FAILED;
2928c2ecf20Sopenharmony_ci		part->blocks[block].free_sectors = 0;
2938c2ecf20Sopenharmony_ci		part->blocks[block].used_sectors = 0;
2948c2ecf20Sopenharmony_ci	} else {
2958c2ecf20Sopenharmony_ci		u16 magic = cpu_to_le16(RFD_MAGIC);
2968c2ecf20Sopenharmony_ci		size_t retlen;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		part->blocks[block].state = BLOCK_ERASED;
2998c2ecf20Sopenharmony_ci		part->blocks[block].free_sectors = part->data_sectors_per_block;
3008c2ecf20Sopenharmony_ci		part->blocks[block].used_sectors = 0;
3018c2ecf20Sopenharmony_ci		part->blocks[block].erases++;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
3048c2ecf20Sopenharmony_ci			       sizeof(magic), &retlen, (u_char *)&magic);
3058c2ecf20Sopenharmony_ci		if (!rc && retlen != sizeof(magic))
3068c2ecf20Sopenharmony_ci			rc = -EIO;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		if (rc) {
3098c2ecf20Sopenharmony_ci			pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
3108c2ecf20Sopenharmony_ci			       part->mbd.mtd->name, part->blocks[block].offset);
3118c2ecf20Sopenharmony_ci			part->blocks[block].state = BLOCK_FAILED;
3128c2ecf20Sopenharmony_ci		} else {
3138c2ecf20Sopenharmony_ci			part->blocks[block].state = BLOCK_OK;
3148c2ecf20Sopenharmony_ci		}
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	kfree(erase);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return rc;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	void *sector_data;
3258c2ecf20Sopenharmony_ci	u16 *map;
3268c2ecf20Sopenharmony_ci	size_t retlen;
3278c2ecf20Sopenharmony_ci	int i, rc = -ENOMEM;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	part->is_reclaiming = 1;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
3328c2ecf20Sopenharmony_ci	if (!sector_data)
3338c2ecf20Sopenharmony_ci		goto err3;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	map = kmalloc(part->header_size, GFP_KERNEL);
3368c2ecf20Sopenharmony_ci	if (!map)
3378c2ecf20Sopenharmony_ci		goto err2;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	rc = mtd_read(part->mbd.mtd, part->blocks[block_no].offset,
3408c2ecf20Sopenharmony_ci		      part->header_size, &retlen, (u_char *)map);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	if (!rc && retlen != part->header_size)
3438c2ecf20Sopenharmony_ci		rc = -EIO;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (rc) {
3468c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "error reading '%s' at "
3478c2ecf20Sopenharmony_ci			"0x%lx\n", part->mbd.mtd->name,
3488c2ecf20Sopenharmony_ci			part->blocks[block_no].offset);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		goto err;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	for (i=0; i<part->data_sectors_per_block; i++) {
3548c2ecf20Sopenharmony_ci		u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
3558c2ecf20Sopenharmony_ci		u_long addr;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci		if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
3598c2ecf20Sopenharmony_ci			continue;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		if (entry == SECTOR_ZERO)
3628c2ecf20Sopenharmony_ci			entry = 0;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		/* already warned about and ignored in build_block_map() */
3658c2ecf20Sopenharmony_ci		if (entry >= part->sector_count)
3668c2ecf20Sopenharmony_ci			continue;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		addr = part->blocks[block_no].offset +
3698c2ecf20Sopenharmony_ci			(i + part->header_sectors_per_block) * SECTOR_SIZE;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		if (*old_sector == addr) {
3728c2ecf20Sopenharmony_ci			*old_sector = -1;
3738c2ecf20Sopenharmony_ci			if (!part->blocks[block_no].used_sectors--) {
3748c2ecf20Sopenharmony_ci				rc = erase_block(part, block_no);
3758c2ecf20Sopenharmony_ci				break;
3768c2ecf20Sopenharmony_ci			}
3778c2ecf20Sopenharmony_ci			continue;
3788c2ecf20Sopenharmony_ci		}
3798c2ecf20Sopenharmony_ci		rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
3808c2ecf20Sopenharmony_ci			      sector_data);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		if (!rc && retlen != SECTOR_SIZE)
3838c2ecf20Sopenharmony_ci			rc = -EIO;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		if (rc) {
3868c2ecf20Sopenharmony_ci			printk(KERN_ERR PREFIX "'%s': Unable to "
3878c2ecf20Sopenharmony_ci				"read sector for relocation\n",
3888c2ecf20Sopenharmony_ci				part->mbd.mtd->name);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci			goto err;
3918c2ecf20Sopenharmony_ci		}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
3948c2ecf20Sopenharmony_ci				entry, sector_data);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		if (rc)
3978c2ecf20Sopenharmony_ci			goto err;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cierr:
4018c2ecf20Sopenharmony_ci	kfree(map);
4028c2ecf20Sopenharmony_cierr2:
4038c2ecf20Sopenharmony_ci	kfree(sector_data);
4048c2ecf20Sopenharmony_cierr3:
4058c2ecf20Sopenharmony_ci	part->is_reclaiming = 0;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return rc;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int reclaim_block(struct partition *part, u_long *old_sector)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	int block, best_block, score, old_sector_block;
4138c2ecf20Sopenharmony_ci	int rc;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* we have a race if sync doesn't exist */
4168c2ecf20Sopenharmony_ci	mtd_sync(part->mbd.mtd);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	score = 0x7fffffff; /* MAX_INT */
4198c2ecf20Sopenharmony_ci	best_block = -1;
4208c2ecf20Sopenharmony_ci	if (*old_sector != -1)
4218c2ecf20Sopenharmony_ci		old_sector_block = *old_sector / part->block_size;
4228c2ecf20Sopenharmony_ci	else
4238c2ecf20Sopenharmony_ci		old_sector_block = -1;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	for (block=0; block<part->total_blocks; block++) {
4268c2ecf20Sopenharmony_ci		int this_score;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci		if (block == part->reserved_block)
4298c2ecf20Sopenharmony_ci			continue;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		/*
4328c2ecf20Sopenharmony_ci		 * Postpone reclaiming if there is a free sector as
4338c2ecf20Sopenharmony_ci		 * more removed sectors is more efficient (have to move
4348c2ecf20Sopenharmony_ci		 * less).
4358c2ecf20Sopenharmony_ci		 */
4368c2ecf20Sopenharmony_ci		if (part->blocks[block].free_sectors)
4378c2ecf20Sopenharmony_ci			return 0;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		this_score = part->blocks[block].used_sectors;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		if (block == old_sector_block)
4428c2ecf20Sopenharmony_ci			this_score--;
4438c2ecf20Sopenharmony_ci		else {
4448c2ecf20Sopenharmony_ci			/* no point in moving a full block */
4458c2ecf20Sopenharmony_ci			if (part->blocks[block].used_sectors ==
4468c2ecf20Sopenharmony_ci					part->data_sectors_per_block)
4478c2ecf20Sopenharmony_ci				continue;
4488c2ecf20Sopenharmony_ci		}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		this_score += part->blocks[block].erases;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci		if (this_score < score) {
4538c2ecf20Sopenharmony_ci			best_block = block;
4548c2ecf20Sopenharmony_ci			score = this_score;
4558c2ecf20Sopenharmony_ci		}
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	if (best_block == -1)
4598c2ecf20Sopenharmony_ci		return -ENOSPC;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	part->current_block = -1;
4628c2ecf20Sopenharmony_ci	part->reserved_block = best_block;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	pr_debug("reclaim_block: reclaiming block #%d with %d used "
4658c2ecf20Sopenharmony_ci		 "%d free sectors\n", best_block,
4668c2ecf20Sopenharmony_ci		 part->blocks[best_block].used_sectors,
4678c2ecf20Sopenharmony_ci		 part->blocks[best_block].free_sectors);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	if (part->blocks[best_block].used_sectors)
4708c2ecf20Sopenharmony_ci		rc = move_block_contents(part, best_block, old_sector);
4718c2ecf20Sopenharmony_ci	else
4728c2ecf20Sopenharmony_ci		rc = erase_block(part, best_block);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	return rc;
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/*
4788c2ecf20Sopenharmony_ci * IMPROVE: It would be best to choose the block with the most deleted sectors,
4798c2ecf20Sopenharmony_ci * because if we fill that one up first it'll have the most chance of having
4808c2ecf20Sopenharmony_ci * the least live sectors at reclaim.
4818c2ecf20Sopenharmony_ci */
4828c2ecf20Sopenharmony_cistatic int find_free_block(struct partition *part)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	int block, stop;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	block = part->current_block == -1 ?
4878c2ecf20Sopenharmony_ci			jiffies % part->total_blocks : part->current_block;
4888c2ecf20Sopenharmony_ci	stop = block;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	do {
4918c2ecf20Sopenharmony_ci		if (part->blocks[block].free_sectors &&
4928c2ecf20Sopenharmony_ci				block != part->reserved_block)
4938c2ecf20Sopenharmony_ci			return block;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		if (part->blocks[block].state == BLOCK_UNUSED)
4968c2ecf20Sopenharmony_ci			erase_block(part, block);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci		if (++block >= part->total_blocks)
4998c2ecf20Sopenharmony_ci			block = 0;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	} while (block != stop);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	return -1;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int find_writable_block(struct partition *part, u_long *old_sector)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	int rc, block;
5098c2ecf20Sopenharmony_ci	size_t retlen;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	block = find_free_block(part);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (block == -1) {
5148c2ecf20Sopenharmony_ci		if (!part->is_reclaiming) {
5158c2ecf20Sopenharmony_ci			rc = reclaim_block(part, old_sector);
5168c2ecf20Sopenharmony_ci			if (rc)
5178c2ecf20Sopenharmony_ci				goto err;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci			block = find_free_block(part);
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		if (block == -1) {
5238c2ecf20Sopenharmony_ci			rc = -ENOSPC;
5248c2ecf20Sopenharmony_ci			goto err;
5258c2ecf20Sopenharmony_ci		}
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	rc = mtd_read(part->mbd.mtd, part->blocks[block].offset,
5298c2ecf20Sopenharmony_ci		      part->header_size, &retlen,
5308c2ecf20Sopenharmony_ci		      (u_char *)part->header_cache);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (!rc && retlen != part->header_size)
5338c2ecf20Sopenharmony_ci		rc = -EIO;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	if (rc) {
5368c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "'%s': unable to read header at "
5378c2ecf20Sopenharmony_ci				"0x%lx\n", part->mbd.mtd->name,
5388c2ecf20Sopenharmony_ci				part->blocks[block].offset);
5398c2ecf20Sopenharmony_ci		goto err;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	part->current_block = block;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cierr:
5458c2ecf20Sopenharmony_ci	return rc;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic int mark_sector_deleted(struct partition *part, u_long old_addr)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	int block, offset, rc;
5518c2ecf20Sopenharmony_ci	u_long addr;
5528c2ecf20Sopenharmony_ci	size_t retlen;
5538c2ecf20Sopenharmony_ci	u16 del = cpu_to_le16(SECTOR_DELETED);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	block = old_addr / part->block_size;
5568c2ecf20Sopenharmony_ci	offset = (old_addr % part->block_size) / SECTOR_SIZE -
5578c2ecf20Sopenharmony_ci		part->header_sectors_per_block;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	addr = part->blocks[block].offset +
5608c2ecf20Sopenharmony_ci			(HEADER_MAP_OFFSET + offset) * sizeof(u16);
5618c2ecf20Sopenharmony_ci	rc = mtd_write(part->mbd.mtd, addr, sizeof(del), &retlen,
5628c2ecf20Sopenharmony_ci		       (u_char *)&del);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (!rc && retlen != sizeof(del))
5658c2ecf20Sopenharmony_ci		rc = -EIO;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (rc) {
5688c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "error writing '%s' at "
5698c2ecf20Sopenharmony_ci			"0x%lx\n", part->mbd.mtd->name, addr);
5708c2ecf20Sopenharmony_ci		goto err;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	if (block == part->current_block)
5738c2ecf20Sopenharmony_ci		part->header_cache[offset + HEADER_MAP_OFFSET] = del;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	part->blocks[block].used_sectors--;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (!part->blocks[block].used_sectors &&
5788c2ecf20Sopenharmony_ci	    !part->blocks[block].free_sectors)
5798c2ecf20Sopenharmony_ci		rc = erase_block(part, block);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cierr:
5828c2ecf20Sopenharmony_ci	return rc;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic int find_free_sector(const struct partition *part, const struct block *block)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci	int i, stop;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	i = stop = part->data_sectors_per_block - block->free_sectors;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	do {
5928c2ecf20Sopenharmony_ci		if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
5938c2ecf20Sopenharmony_ci				== SECTOR_FREE)
5948c2ecf20Sopenharmony_ci			return i;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		if (++i == part->data_sectors_per_block)
5978c2ecf20Sopenharmony_ci			i = 0;
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci	while(i != stop);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	return -1;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct partition *part = (struct partition*)dev;
6078c2ecf20Sopenharmony_ci	struct block *block;
6088c2ecf20Sopenharmony_ci	u_long addr;
6098c2ecf20Sopenharmony_ci	int i;
6108c2ecf20Sopenharmony_ci	int rc;
6118c2ecf20Sopenharmony_ci	size_t retlen;
6128c2ecf20Sopenharmony_ci	u16 entry;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (part->current_block == -1 ||
6158c2ecf20Sopenharmony_ci		!part->blocks[part->current_block].free_sectors) {
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		rc = find_writable_block(part, old_addr);
6188c2ecf20Sopenharmony_ci		if (rc)
6198c2ecf20Sopenharmony_ci			goto err;
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	block = &part->blocks[part->current_block];
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	i = find_free_sector(part, block);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (i < 0) {
6278c2ecf20Sopenharmony_ci		rc = -ENOSPC;
6288c2ecf20Sopenharmony_ci		goto err;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
6328c2ecf20Sopenharmony_ci		block->offset;
6338c2ecf20Sopenharmony_ci	rc = mtd_write(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
6348c2ecf20Sopenharmony_ci		       (u_char *)buf);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!rc && retlen != SECTOR_SIZE)
6378c2ecf20Sopenharmony_ci		rc = -EIO;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (rc) {
6408c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
6418c2ecf20Sopenharmony_ci				part->mbd.mtd->name, addr);
6428c2ecf20Sopenharmony_ci		goto err;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	part->sector_map[sector] = addr;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	part->header_cache[i + HEADER_MAP_OFFSET] = entry;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
6528c2ecf20Sopenharmony_ci	rc = mtd_write(part->mbd.mtd, addr, sizeof(entry), &retlen,
6538c2ecf20Sopenharmony_ci		       (u_char *)&entry);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (!rc && retlen != sizeof(entry))
6568c2ecf20Sopenharmony_ci		rc = -EIO;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (rc) {
6598c2ecf20Sopenharmony_ci		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
6608c2ecf20Sopenharmony_ci				part->mbd.mtd->name, addr);
6618c2ecf20Sopenharmony_ci		goto err;
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci	block->used_sectors++;
6648c2ecf20Sopenharmony_ci	block->free_sectors--;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cierr:
6678c2ecf20Sopenharmony_ci	return rc;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct partition *part = (struct partition*)dev;
6738c2ecf20Sopenharmony_ci	u_long old_addr;
6748c2ecf20Sopenharmony_ci	int i;
6758c2ecf20Sopenharmony_ci	int rc = 0;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	if (part->reserved_block == -1) {
6808c2ecf20Sopenharmony_ci		rc = -EACCES;
6818c2ecf20Sopenharmony_ci		goto err;
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (sector >= part->sector_count) {
6858c2ecf20Sopenharmony_ci		rc = -EIO;
6868c2ecf20Sopenharmony_ci		goto err;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	old_addr = part->sector_map[sector];
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	for (i=0; i<SECTOR_SIZE; i++) {
6928c2ecf20Sopenharmony_ci		if (!buf[i])
6938c2ecf20Sopenharmony_ci			continue;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		rc = do_writesect(dev, sector, buf, &old_addr);
6968c2ecf20Sopenharmony_ci		if (rc)
6978c2ecf20Sopenharmony_ci			goto err;
6988c2ecf20Sopenharmony_ci		break;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	if (i == SECTOR_SIZE)
7028c2ecf20Sopenharmony_ci		part->sector_map[sector] = -1;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (old_addr != -1)
7058c2ecf20Sopenharmony_ci		rc = mark_sector_deleted(part, old_addr);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cierr:
7088c2ecf20Sopenharmony_ci	return rc;
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct partition *part = (struct partition*)dev;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	geo->heads = 1;
7168c2ecf20Sopenharmony_ci	geo->sectors = SECTORS_PER_TRACK;
7178c2ecf20Sopenharmony_ci	geo->cylinders = part->cylinders;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	return 0;
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct partition *part;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX)
7278c2ecf20Sopenharmony_ci		return;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	part = kzalloc(sizeof(struct partition), GFP_KERNEL);
7308c2ecf20Sopenharmony_ci	if (!part)
7318c2ecf20Sopenharmony_ci		return;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	part->mbd.mtd = mtd;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (block_size)
7368c2ecf20Sopenharmony_ci		part->block_size = block_size;
7378c2ecf20Sopenharmony_ci	else {
7388c2ecf20Sopenharmony_ci		if (!mtd->erasesize) {
7398c2ecf20Sopenharmony_ci			printk(KERN_WARNING PREFIX "please provide block_size");
7408c2ecf20Sopenharmony_ci			goto out;
7418c2ecf20Sopenharmony_ci		} else
7428c2ecf20Sopenharmony_ci			part->block_size = mtd->erasesize;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (scan_header(part) == 0) {
7468c2ecf20Sopenharmony_ci		part->mbd.size = part->sector_count;
7478c2ecf20Sopenharmony_ci		part->mbd.tr = tr;
7488c2ecf20Sopenharmony_ci		part->mbd.devnum = -1;
7498c2ecf20Sopenharmony_ci		if (!(mtd->flags & MTD_WRITEABLE))
7508c2ecf20Sopenharmony_ci			part->mbd.readonly = 1;
7518c2ecf20Sopenharmony_ci		else if (part->errors) {
7528c2ecf20Sopenharmony_ci			printk(KERN_WARNING PREFIX "'%s': errors found, "
7538c2ecf20Sopenharmony_ci					"setting read-only\n", mtd->name);
7548c2ecf20Sopenharmony_ci			part->mbd.readonly = 1;
7558c2ecf20Sopenharmony_ci		}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
7588c2ecf20Sopenharmony_ci				mtd->name, mtd->type, mtd->flags);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci		if (!add_mtd_blktrans_dev((void*)part))
7618c2ecf20Sopenharmony_ci			return;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ciout:
7648c2ecf20Sopenharmony_ci	kfree(part);
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	struct partition *part = (struct partition*)dev;
7708c2ecf20Sopenharmony_ci	int i;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	for (i=0; i<part->total_blocks; i++) {
7738c2ecf20Sopenharmony_ci		pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
7748c2ecf20Sopenharmony_ci			part->mbd.mtd->name, i, part->blocks[i].erases);
7758c2ecf20Sopenharmony_ci	}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	del_mtd_blktrans_dev(dev);
7788c2ecf20Sopenharmony_ci	vfree(part->sector_map);
7798c2ecf20Sopenharmony_ci	kfree(part->header_cache);
7808c2ecf20Sopenharmony_ci	kfree(part->blocks);
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_cistatic struct mtd_blktrans_ops rfd_ftl_tr = {
7848c2ecf20Sopenharmony_ci	.name		= "rfd",
7858c2ecf20Sopenharmony_ci	.major		= RFD_FTL_MAJOR,
7868c2ecf20Sopenharmony_ci	.part_bits	= PART_BITS,
7878c2ecf20Sopenharmony_ci	.blksize 	= SECTOR_SIZE,
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	.readsect	= rfd_ftl_readsect,
7908c2ecf20Sopenharmony_ci	.writesect	= rfd_ftl_writesect,
7918c2ecf20Sopenharmony_ci	.getgeo		= rfd_ftl_getgeo,
7928c2ecf20Sopenharmony_ci	.add_mtd	= rfd_ftl_add_mtd,
7938c2ecf20Sopenharmony_ci	.remove_dev	= rfd_ftl_remove_dev,
7948c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7958c2ecf20Sopenharmony_ci};
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic int __init init_rfd_ftl(void)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	return register_mtd_blktrans(&rfd_ftl_tr);
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic void __exit cleanup_rfd_ftl(void)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	deregister_mtd_blktrans(&rfd_ftl_tr);
8058c2ecf20Sopenharmony_ci}
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cimodule_init(init_rfd_ftl);
8088c2ecf20Sopenharmony_cimodule_exit(cleanup_rfd_ftl);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sean Young <sean@mess.org>");
8128c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
8138c2ecf20Sopenharmony_ci		"used by General Software's Embedded BIOS");
8148c2ecf20Sopenharmony_ci
815