diff options
Diffstat (limited to 'fs/yaffs2/yaffs_nandif.c')
| -rw-r--r-- | fs/yaffs2/yaffs_nandif.c | 251 | 
1 files changed, 251 insertions, 0 deletions
| diff --git a/fs/yaffs2/yaffs_nandif.c b/fs/yaffs2/yaffs_nandif.c new file mode 100644 index 000000000..79b00ab3b --- /dev/null +++ b/fs/yaffs2/yaffs_nandif.c @@ -0,0 +1,251 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. + * + * Copyright (C) 2002-2011 Aleph One Ltd. + *   for Toby Churchill Ltd and Brightstar Engineering + * + * Created by Charles Manning <charles@aleph1.co.uk> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "yportenv.h" +#include "yaffs_guts.h" + + +#include "yaffs_nandif.h" +#include "yaffs_packedtags2.h" + +#include "yramsim.h" + +#include "yaffs_trace.h" +#include "yaffsfs.h" + + +/* NB For use with inband tags.... + * We assume that the data buffer is of size totalBytersPerChunk so that + * we can also use it to load the tags. + */ +int ynandif_WriteChunkWithTagsToNAND(struct yaffs_dev *dev, int nand_chunk, +				      const u8 *data, +				      const struct yaffs_ext_tags *tags) +{ + +	int retval = 0; +	struct yaffs_packed_tags2 pt; +	void *spare; +	unsigned spareSize = 0; +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	yaffs_trace(YAFFS_TRACE_MTD, +		"nandmtd2_WriteChunkWithTagsToNAND chunk %d data %p tags %p", +		nand_chunk, data, tags); + + +	/* For yaffs2 writing there must be both data and tags. +	 * If we're using inband tags, then the tags are stuffed into +	 * the end of the data buffer. +	 */ + +	if (dev->param.inband_tags) { +		struct yaffs_packed_tags2_tags_only *pt2tp; + +		pt2tp = (struct yaffs_packed_tags2_tags_only *) +			(data + dev->data_bytes_per_chunk); +		yaffs_pack_tags2_tags_only(pt2tp, tags); +		spare = NULL; +		spareSize = 0; +	} else { +		yaffs_pack_tags2(&pt, tags, !dev->param.no_tags_ecc); +		spare = &pt; +		spareSize = sizeof(struct yaffs_packed_tags2); +	} + +	retval = geometry->writeChunk(dev, nand_chunk, +				data, dev->param.total_bytes_per_chunk, +				spare, spareSize); + +	return retval; +} + +int ynandif_ReadChunkWithTagsFromNAND(struct yaffs_dev *dev, int nand_chunk, +				       u8 *data, struct yaffs_ext_tags *tags) +{ +	struct yaffs_packed_tags2 pt; +	int localData = 0; +	void *spare = NULL; +	unsigned spareSize; +	int retval = 0; +	int eccStatus; /* 0 = ok, 1 = fixed, -1 = unfixed */ +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	yaffs_trace(YAFFS_TRACE_MTD, +		"nandmtd2_ReadChunkWithTagsFromNAND chunk %d data %p tags %p", +		nand_chunk, data, tags); + +	if (!tags) { +		spare = NULL; +		spareSize = 0; +	} else if (dev->param.inband_tags) { + +		if (!data) { +			localData = 1; +			data = yaffs_get_temp_buffer(dev); +		} +		spare = NULL; +		spareSize = 0; +	} else { +		spare = &pt; +		spareSize = sizeof(struct yaffs_packed_tags2); +	} + +	retval = geometry->readChunk(dev, nand_chunk, +				 data, +				 data ? dev->param.total_bytes_per_chunk : 0, +				 spare, spareSize, +				 &eccStatus); + +	if (dev->param.inband_tags) { +		if (tags) { +			struct yaffs_packed_tags2_tags_only *pt2tp; +			pt2tp = (struct yaffs_packed_tags2_tags_only *) +					&data[dev->data_bytes_per_chunk]; +			yaffs_unpack_tags2_tags_only(tags, pt2tp); +		} +	} else { +		if (tags) +			yaffs_unpack_tags2(tags, &pt, !dev->param.no_tags_ecc); +	} + +	if (tags && tags->chunk_used) { +		if (eccStatus < 0 || +		   tags->ecc_result == YAFFS_ECC_RESULT_UNFIXED) +			tags->ecc_result = YAFFS_ECC_RESULT_UNFIXED; +		else if (eccStatus > 0 || +			     tags->ecc_result == YAFFS_ECC_RESULT_FIXED) +			tags->ecc_result = YAFFS_ECC_RESULT_FIXED; +		else +			tags->ecc_result = YAFFS_ECC_RESULT_NO_ERROR; +	} + +	if (localData) +		yaffs_release_temp_buffer(dev, data); + +	return retval; +} + +int ynandif_MarkNANDBlockBad(struct yaffs_dev *dev, int blockId) +{ +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	return geometry->markBlockBad(dev, blockId); +} + +int ynandif_EraseBlockInNAND(struct yaffs_dev *dev, int blockId) +{ +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	return geometry->eraseBlock(dev, blockId); + +} + + +static int ynandif_IsBlockOk(struct yaffs_dev *dev, int blockId) +{ +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	return geometry->checkBlockOk(dev, blockId); +} + +int ynandif_QueryNANDBlock(struct yaffs_dev *dev, int blockId, +		enum yaffs_block_state *state, u32 *seq_number) +{ +	unsigned chunkNo; +	struct yaffs_ext_tags tags; + +	*seq_number = 0; + +	chunkNo = blockId * dev->param.chunks_per_block; + +	if (!ynandif_IsBlockOk(dev, blockId)) { +		*state = YAFFS_BLOCK_STATE_DEAD; +	} else { +		ynandif_ReadChunkWithTagsFromNAND(dev, chunkNo, NULL, &tags); + +		if (!tags.chunk_used) { +			*state = YAFFS_BLOCK_STATE_EMPTY; +		} else { +			*state = YAFFS_BLOCK_STATE_NEEDS_SCAN; +			*seq_number = tags.seq_number; +		} +	} + +	return YAFFS_OK; +} + + +int ynandif_InitialiseNAND(struct yaffs_dev *dev) +{ +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	geometry->initialise(dev); + +	return YAFFS_OK; +} + +int ynandif_Deinitialise_flash_fn(struct yaffs_dev *dev) +{ +	struct ynandif_Geometry *geometry = (struct ynandif_Geometry *)(dev->driver_context); + +	geometry->deinitialise(dev); + +	return YAFFS_OK; +} + + +struct yaffs_dev * +	yaffs_add_dev_from_geometry(const YCHAR *name, +					const struct ynandif_Geometry *geometry) +{ +	YCHAR *clonedName = malloc(sizeof(YCHAR) * +				(strnlen(name, YAFFS_MAX_NAME_LENGTH)+1)); +	struct yaffs_dev *dev = malloc(sizeof(struct yaffs_dev)); +	struct yaffs_param *param; + +	if (dev && clonedName) { +		memset(dev, 0, sizeof(struct yaffs_dev)); +		strcpy(clonedName, name); + +		param = &dev->param; + +		param->name = clonedName; +		param->write_chunk_tags_fn = ynandif_WriteChunkWithTagsToNAND; +		param->read_chunk_tags_fn = ynandif_ReadChunkWithTagsFromNAND; +		param->erase_fn = ynandif_EraseBlockInNAND; +		param->initialise_flash_fn = ynandif_InitialiseNAND; +		param->query_block_fn = ynandif_QueryNANDBlock; +		param->bad_block_fn = ynandif_MarkNANDBlockBad; +		param->n_caches = 20; +		param->start_block = geometry->start_block; +		param->end_block   = geometry->end_block; +		param->total_bytes_per_chunk  = geometry->dataSize; +		param->spare_bytes_per_chunk  = geometry->spareSize; +		param->inband_tags		  = geometry->inband_tags; +		param->chunks_per_block	  = geometry->pagesPerBlock; +		param->use_nand_ecc		  = geometry->hasECC; +		param->is_yaffs2		  = geometry->useYaffs2; +		param->n_reserved_blocks	  = 5; +		dev->driver_context		  = (void *)geometry; + +		yaffs_add_device(dev); + +		return dev; +	} + +	free(dev); +	free(clonedName); + +	return NULL; +} |