/*
 * Freescale GPMI NFC NAND Flash Driver
 *
 * Copyright (C) 2010 Freescale Semiconductor, Inc.
 * Copyright (C) 2008 Embedded Alley Solutions, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "gpmi-nfc.h"

/*
 * Indicates the driver should register the MTD that represents the entire
 * medium, thus making it visible.
 */

static int register_main_mtd;
module_param(register_main_mtd, int, 0400);

/*
 * Indicates the driver should attempt to perform DMA directly to/from buffers
 * passed into this driver. This is true by default. If false, the driver will
 * *always* copy incoming/outgoing data to/from its own DMA buffers.
 */

static int map_io_buffers = true;
module_param(map_io_buffers, int, 0600);

/**
 * mil_outgoing_buffer_dma_begin() - Begins DMA on an outgoing buffer.
 *
 * @this:      Per-device data.
 * @source:    The source buffer.
 * @length:    The length of the data in the source buffer.
 * @alt_virt:  The virtual address of an alternate buffer which is ready to be
 *             used for DMA.
 * @alt_phys:  The physical address of an alternate buffer which is ready to be
 *             used for DMA.
 * @alt_size:  The size of the alternate buffer.
 * @use_virt:  A pointer to a variable that will receive the virtual address to
 *             use.
 * @use_phys:  A pointer to a variable that will receive the physical address to
 *             use.
 */
static int mil_outgoing_buffer_dma_begin(struct gpmi_nfc_data *this,
			const void *source, unsigned length,
			void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
			const void **use_virt, dma_addr_t *use_phys)
{
	struct device  *dev = this->dev;
	dma_addr_t     source_phys = ~0;

	/*
	 * If we can, we want to use the caller's buffer directly for DMA. Check
	 * if the system will let us map them.
	 */

	if (map_io_buffers && virt_addr_valid(source))
		source_phys =
			dma_map_single(dev,
				(void *) source, length, DMA_TO_DEVICE);

	if (dma_mapping_error(dev, source_phys)) {

		/*
		 * If control arrives here, we're not mapping the source buffer.
		 * Make sure the alternate is large enough.
		 */

		if (alt_size < length) {
			dev_err(dev, "Alternate buffer is too small "
							"for outgoing I/O\n");
			return -ENOMEM;
		}

		/*
		 * Copy the contents of the source buffer into the alternate
		 * buffer and set up the return values accordingly.
		 */

		memcpy(alt_virt, source, length);

		*use_virt = alt_virt;
		*use_phys = alt_phys;

	} else {

		/*
		 * If control arrives here, we're mapping the source buffer. Set
		 * up the return values accordingly.
		 */

		*use_virt = source;
		*use_phys = source_phys;

	}

	/* If control arrives here, all is well. */

	return 0;

}

/**
 * mil_outgoing_buffer_dma_end() - Ends DMA on an outgoing buffer.
 *
 * @this:       Per-device data.
 * @source:     The source buffer.
 * @length:     The length of the data in the source buffer.
 * @alt_virt:   The virtual address of an alternate buffer which was ready to be
 *              used for DMA.
 * @alt_phys:   The physical address of an alternate buffer which was ready to
 *              be used for DMA.
 * @alt_size:   The size of the alternate buffer.
 * @used_virt:  The virtual address that was used.
 * @used_phys:  The physical address that was used.
 */
static void mil_outgoing_buffer_dma_end(struct gpmi_nfc_data *this,
		const void *source, unsigned length,
		void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
		const void *used_virt, dma_addr_t used_phys)
{
	struct device  *dev = this->dev;

	/*
	 * Check if we used the source buffer, and it's not one of our own DMA
	 * buffers. If so, we need to unmap it.
	 */

	if (used_virt == source)
		dma_unmap_single(dev, used_phys, length, DMA_TO_DEVICE);

}

/**
 * mil_incoming_buffer_dma_begin() - Begins DMA on an incoming buffer.
 *
 * @this:         Per-device data.
 * @destination:  The destination buffer.
 * @length:       The length of the data that will arrive.
 * @alt_virt:     The virtual address of an alternate buffer which is ready
 *                to be used for DMA.
 * @alt_phys:     The physical address of an alternate buffer which is ready
 *                to be used for DMA.
 * @alt_size:     The size of the alternate buffer.
 * @use_virt:     A pointer to a variable that will receive the virtual address
 *                to use.
 * @use_phys:     A pointer to a variable that will receive the physical address
 *                to use.
 */
static int mil_incoming_buffer_dma_begin(struct gpmi_nfc_data *this,
			void *destination, unsigned length,
			void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
			void **use_virt, dma_addr_t *use_phys)
{
	struct device  *dev = this->dev;
	dma_addr_t     destination_phys = ~0;

	/*
	 * If we can, we want to use the caller's buffer directly for DMA. Check
	 * if the system will let us map them.
	 */

	if (map_io_buffers && virt_addr_valid(destination))
		destination_phys =
			dma_map_single(dev,
				(void *) destination, length, DMA_FROM_DEVICE);

	if (dma_mapping_error(dev, destination_phys)) {

		/*
		 * If control arrives here, we're not mapping the destination
		 * buffer. Make sure the alternate is large enough.
		 */

		if (alt_size < length) {
			dev_err(dev, "Alternate buffer is too small "
							"for incoming I/O\n");
			return -ENOMEM;
		}

		/* Set up the return values to use the alternate. */

		*use_virt = alt_virt;
		*use_phys = alt_phys;

	} else {

		/*
		 * If control arrives here, we're mapping the destination
		 * buffer. Set up the return values accordingly.
		 */

		*use_virt = destination;
		*use_phys = destination_phys;

	}

	/* If control arrives here, all is well. */

	return 0;

}

/**
 * mil_incoming_buffer_dma_end() - Ends DMA on an incoming buffer.
 *
 * @this:         Per-device data.
 * @destination:  The destination buffer.
 * @length:       The length of the data that arrived.
 * @alt_virt:     The virtual address of an alternate buffer which was ready to
 *                be used for DMA.
 * @alt_phys:     The physical address of an alternate buffer which was ready to
 *                be used for DMA.
 * @alt_size:     The size of the alternate buffer.
 * @used_virt:    The virtual address that was used.
 * @used_phys:    The physical address that was used.
 */
static void mil_incoming_buffer_dma_end(struct gpmi_nfc_data *this,
			void *destination, unsigned length,
			void *alt_virt, dma_addr_t alt_phys, unsigned alt_size,
			void *used_virt, dma_addr_t used_phys)
{
	struct device  *dev = this->dev;

	/*
	 * Check if we used the destination buffer, and it's not one of our own
	 * DMA buffers. If so, we need to unmap it.
	 */

	if (used_virt == destination)
		dma_unmap_single(dev, used_phys, length, DMA_FROM_DEVICE);
	else
		memcpy(destination, alt_virt, length);

}

/**
 * mil_cmd_ctrl - MTD Interface cmd_ctrl()
 *
 * This is the function that we install in the cmd_ctrl function pointer of the
 * owning struct nand_chip. The only functions in the reference implementation
 * that use these functions pointers are cmdfunc and select_chip.
 *
 * In this driver, we implement our own select_chip, so this function will only
 * be called by the reference implementation's cmdfunc. For this reason, we can
 * ignore the chip enable bit and concentrate only on sending bytes to the
 * NAND Flash.
 *
 * @mtd:   The owning MTD.
 * @data:  The value to push onto the data signals.
 * @ctrl:  The values to push onto the control signals.
 */
static void mil_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
{
	struct nand_chip      *nand = mtd->priv;
	struct gpmi_nfc_data  *this = nand->priv;
	struct device         *dev  =  this->dev;
	struct mil            *mil  = &this->mil;
	struct nfc_hal        *nfc  =  this->nfc;
	int                   error;
#if defined(CONFIG_MTD_DEBUG)
	unsigned int          i;
	char                  display[MIL_COMMAND_BUFFER_SIZE * 5];
#endif

	/*
	 * Every operation begins with a command byte and a series of zero or
	 * more address bytes. These are distinguished by either the Address
	 * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
	 * asserted. When MTD is ready to execute the command, it will deassert
	 * both latch enables.
	 *
	 * Rather than run a separate DMA operation for every single byte, we
	 * queue them up and run a single DMA operation for the entire series
	 * of command and data bytes.
	 */

	if ((ctrl & (NAND_ALE | NAND_CLE))) {
		if (data != NAND_CMD_NONE)
			mil->cmd_virt[mil->command_length++] = data;
		return;
	}

	/*
	 * If control arrives here, MTD has deasserted both the ALE and CLE,
	 * which means it's ready to run an operation. Check if we have any
	 * bytes to send.
	 */

	if (!mil->command_length)
		return;

	/* Hand the command over to the NFC. */

	gpmi_nfc_add_event("mil_cmd_ctrl sending command...", 1);

#if defined(CONFIG_MTD_DEBUG)
	display[0] = 0;
	for (i = 0; i < mil->command_length; i++)
		sprintf(display + strlen(display), " 0x%02x",
						mil->cmd_virt[i] & 0xff);
	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc cmd_ctrl] command: %s\n", display);
#endif

	error = nfc->send_command(this,
			mil->current_chip, mil->cmd_phys, mil->command_length);

	if (error) {
		dev_err(dev, "[%s] Chip: %u, Error %d\n",
					__func__, mil->current_chip, error);
		print_hex_dump(KERN_ERR,
			"    Command Bytes: ", DUMP_PREFIX_NONE, 16, 1,
					mil->cmd_virt, mil->command_length, 0);
	}

	gpmi_nfc_add_event("...Finished", -1);

	/* Reset. */

	mil->command_length = 0;

}

/**
 * mil_dev_ready() - MTD Interface dev_ready()
 *
 * @mtd:   A pointer to the owning MTD.
 */
static int mil_dev_ready(struct mtd_info *mtd)
{
	struct nand_chip      *nand = mtd->priv;
	struct gpmi_nfc_data  *this = nand->priv;
	struct nfc_hal        *nfc  = this->nfc;
	struct mil            *mil  = &this->mil;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc dev_ready]\n");

	gpmi_nfc_add_event("> mil_dev_ready", 1);

	if (nfc->is_ready(this, mil->current_chip)) {
		gpmi_nfc_add_event("< mil_dev_ready - Returning ready", -1);
		return !0;
	} else {
		gpmi_nfc_add_event("< mil_dev_ready - Returning busy", -1);
		return 0;
	}

}

/**
 * mil_select_chip() - MTD Interface select_chip()
 *
 * @mtd:   A pointer to the owning MTD.
 * @chip:  The chip number to select, or -1 to select no chip.
 */
static void mil_select_chip(struct mtd_info *mtd, int chip)
{
	struct nand_chip      *nand  = mtd->priv;
	struct gpmi_nfc_data  *this  = nand->priv;
	struct mil            *mil   = &this->mil;
	struct nfc_hal        *nfc   =  this->nfc;
	struct clk            *clock =  this->resources.clock;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc select_chip] chip: %d\n", chip);

	/* Figure out what kind of transition this is. */

	if ((mil->current_chip < 0) && (chip >= 0)) {
		gpmi_nfc_start_event_trace("> mil_select_chip");
		clk_enable(clock);
		nfc->begin(this);
		gpmi_nfc_add_event("< mil_select_chip", -1);
	} else if ((mil->current_chip >= 0) && (chip < 0)) {
		gpmi_nfc_add_event("> mil_select_chip", 1);
		clk_disable(clock);
		nfc->end(this);
		gpmi_nfc_stop_event_trace("< mil_select_chip");
	} else {
		gpmi_nfc_add_event("> mil_select_chip", 1);
		gpmi_nfc_add_event("< mil_select_chip", -1);
	}

	mil->current_chip = chip;

}

/**
 * mil_read_buf() - MTD Interface read_buf().
 *
 * @mtd:  A pointer to the owning MTD.
 * @buf:  The destination buffer.
 * @len:  The number of bytes to read.
 */
static void mil_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
	struct nand_chip      *nand     = mtd->priv;
	struct gpmi_nfc_data  *this     = nand->priv;
	struct device         *dev      =  this->dev;
	struct nfc_hal        *nfc      =  this->nfc;
	struct nfc_geometry   *nfc_geo  = &this->nfc_geometry;
	struct mil            *mil      = &this->mil;
	void                  *use_virt =  0;
	dma_addr_t            use_phys  = ~0;
	int                   error;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc readbuf] len: %d\n", len);

	gpmi_nfc_add_event("> mil_read_buf", 1);

	/* Set up DMA. */

	error = mil_incoming_buffer_dma_begin(this, buf, len,
					mil->payload_virt, mil->payload_phys,
					nfc_geo->payload_size_in_bytes,
					&use_virt, &use_phys);

	if (error) {
		dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__);
		goto exit;
	}

	/* Ask the NFC. */

	nfc->read_data(this, mil->current_chip, use_phys, len);

	/* Finish with DMA. */

	mil_incoming_buffer_dma_end(this, buf, len,
					mil->payload_virt, mil->payload_phys,
					nfc_geo->payload_size_in_bytes,
					use_virt, use_phys);

	/* Return. */

exit:

	gpmi_nfc_add_event("< mil_read_buf", -1);

}

/**
 * mil_write_buf() - MTD Interface write_buf().
 *
 * @mtd:  A pointer to the owning MTD.
 * @buf:  The source buffer.
 * @len:  The number of bytes to read.
 */
static void mil_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
	struct nand_chip      *nand     = mtd->priv;
	struct gpmi_nfc_data  *this     = nand->priv;
	struct device         *dev      =  this->dev;
	struct nfc_hal        *nfc      =  this->nfc;
	struct nfc_geometry   *nfc_geo  = &this->nfc_geometry;
	struct mil            *mil      = &this->mil;
	const void            *use_virt =  0;
	dma_addr_t            use_phys  = ~0;
	int                   error;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc writebuf] len: %d\n", len);

	gpmi_nfc_add_event("> mil_write_buf", 1);

	/* Set up DMA. */

	error = mil_outgoing_buffer_dma_begin(this, buf, len,
					mil->payload_virt, mil->payload_phys,
					nfc_geo->payload_size_in_bytes,
					&use_virt, &use_phys);

	if (error) {
		dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__);
		goto exit;
	}

	/* Ask the NFC. */

	nfc->send_data(this, mil->current_chip, use_phys, len);

	/* Finish with DMA. */

	mil_outgoing_buffer_dma_end(this, buf, len,
					mil->payload_virt, mil->payload_phys,
					nfc_geo->payload_size_in_bytes,
					use_virt, use_phys);

	/* Return. */

exit:

	gpmi_nfc_add_event("< mil_write_buf", -1);

}

/**
 * mil_read_byte() - MTD Interface read_byte().
 *
 * @mtd:  A pointer to the owning MTD.
 */
static uint8_t mil_read_byte(struct mtd_info *mtd)
{
	uint8_t  byte;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc read_byte]\n");

	gpmi_nfc_add_event("> mil_read_byte", 1);

	mil_read_buf(mtd, (uint8_t *) &byte, 1);

	gpmi_nfc_add_event("< mil_read_byte", -1);

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc read_byte]: 0x%02x\n", byte);

	return byte;

}

/**
 * mil_handle_block_mark_swapping() - Handles block mark swapping.
 *
 * Note that, when this function is called, it doesn't know whether it's
 * swapping the block mark, or swapping it *back* -- but it doesn't matter
 * because the the operation is the same.
 *
 * @this:       Per-device data.
 * @payload:    A pointer to the payload buffer.
 * @auxiliary:  A pointer to the auxiliary buffer.
 */
static void mil_handle_block_mark_swapping(struct gpmi_nfc_data *this,
						void *payload, void *auxiliary)
{
	struct nfc_geometry     *nfc_geo = &this->nfc_geometry;
	struct boot_rom_helper  *rom     =  this->rom;
	unsigned char           *p;
	unsigned char           *a;
	unsigned int            bit;
	unsigned char           mask;
	unsigned char           from_data;
	unsigned char           from_oob;

	/* Check if we're doing block mark swapping. */

	if (!rom->swap_block_mark)
		return;

	/*
	 * If control arrives here, we're swapping. Make some convenience
	 * variables.
	 */

	bit = nfc_geo->block_mark_bit_offset;
	p   = ((unsigned char *) payload) + nfc_geo->block_mark_byte_offset;
	a   = auxiliary;

	/*
	 * Get the byte from the data area that overlays the block mark. Since
	 * the ECC engine applies its own view to the bits in the page, the
	 * physical block mark won't (in general) appear on a byte boundary in
	 * the data.
	 */

	from_data = (p[0] >> bit) | (p[1] << (8 - bit));

	/* Get the byte from the OOB. */

	from_oob = a[0];

	/* Swap them. */

	a[0] = from_data;

	mask = (0x1 << bit) - 1;
	p[0] = (p[0] & mask) | (from_oob << bit);

	mask = ~0 << bit;
	p[1] = (p[1] & mask) | (from_oob >> (8 - bit));

}

/**
 * mil_ecc_read_page() - MTD Interface ecc.read_page().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @buf:   A pointer to the destination buffer.
 */
static int mil_ecc_read_page(struct mtd_info *mtd,
					struct nand_chip *nand, uint8_t *buf)
{
	struct gpmi_nfc_data    *this    = nand->priv;
	struct device           *dev     =  this->dev;
	struct nfc_hal          *nfc     =  this->nfc;
	struct nfc_geometry     *nfc_geo = &this->nfc_geometry;
	struct mil              *mil     = &this->mil;
	void                    *payload_virt   =  0;
	dma_addr_t              payload_phys    = ~0;
	void                    *auxiliary_virt =  0;
	dma_addr_t              auxiliary_phys  = ~0;
	unsigned int            i;
	unsigned char           *status;
	unsigned int            failed;
	unsigned int            corrected;
	int                     error = 0;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_read_page]\n");

	gpmi_nfc_add_event("> mil_ecc_read_page", 1);

	/*
	 * Set up DMA.
	 *
	 * Notice that we don't try to use the caller's buffer as the auxiliary.
	 * We need to do a lot of fiddling to deliver the OOB, so there's no
	 * point.
	 */

	error = mil_incoming_buffer_dma_begin(this, buf, mtd->writesize,
					mil->payload_virt, mil->payload_phys,
					nfc_geo->payload_size_in_bytes,
					&payload_virt, &payload_phys);

	if (error) {
		dev_err(dev, "[%s] Inadequate DMA buffer\n", __func__);
		error = -ENOMEM;
		goto exit_payload;
	}

	auxiliary_virt = mil->auxiliary_virt;
	auxiliary_phys = mil->auxiliary_phys;

	/* Ask the NFC. */

	error = nfc->read_page(this, mil->current_chip,
						payload_phys, auxiliary_phys);

	if (error) {
		dev_err(dev, "[%s] Error in ECC-based read: %d\n",
							__func__, error);
		goto exit_nfc;
	}

	/* Handle block mark swapping. */

	mil_handle_block_mark_swapping(this, payload_virt, auxiliary_virt);

	/* Loop over status bytes, accumulating ECC status. */

	failed    = 0;
	corrected = 0;

	status = ((unsigned char *) auxiliary_virt) +
					nfc_geo->auxiliary_status_offset;

	for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {

		if ((*status == 0x00) || (*status == 0xff))
			continue;

		if (*status == 0xfe) {
			failed++;
			continue;
		}

		corrected += *status;

	}

	/* Propagate ECC status to the owning MTD. */

	mtd->ecc_stats.failed    += failed;
	mtd->ecc_stats.corrected += corrected;

	/*
	 * It's time to deliver the OOB bytes. See mil_ecc_read_oob() for
	 * details about our policy for delivering the OOB.
	 *
	 * We fill the caller's buffer with set bits, and then copy the block
	 * mark to th caller's buffer. Note that, if block mark swapping was
	 * necessary, it has already been done, so we can rely on the first
	 * byte of the auxiliary buffer to contain the block mark.
	 */

	memset(nand->oob_poi, ~0, mtd->oobsize);

	nand->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];

	/* Return. */

exit_nfc:
	mil_incoming_buffer_dma_end(this, buf, mtd->writesize,
					mil->payload_virt, mil->payload_phys,
					nfc_geo->payload_size_in_bytes,
					payload_virt, payload_phys);
exit_payload:

	gpmi_nfc_add_event("< mil_ecc_read_page", -1);

	return error;

}

/**
 * mil_ecc_write_page() - MTD Interface ecc.write_page().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @buf:   A pointer to the source buffer.
 */
static void mil_ecc_write_page(struct mtd_info *mtd,
				struct nand_chip *nand, const uint8_t *buf)
{
	struct gpmi_nfc_data    *this    = nand->priv;
	struct device           *dev     =  this->dev;
	struct nfc_hal          *nfc     =  this->nfc;
	struct nfc_geometry     *nfc_geo = &this->nfc_geometry;
	struct boot_rom_helper  *rom     =  this->rom;
	struct mil              *mil     = &this->mil;
	const void              *payload_virt   =  0;
	dma_addr_t              payload_phys    = ~0;
	const void              *auxiliary_virt =  0;
	dma_addr_t              auxiliary_phys  = ~0;
	int                     error;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_write_page]\n");

	gpmi_nfc_add_event("> mil_ecc_write_page", 1);

	/* Set up DMA. */

	if (rom->swap_block_mark) {

		/*
		 * If control arrives here, we're doing block mark swapping.
		 * Since we can't modify the caller's buffers, we must copy them
		 * into our own.
		 */

		memcpy(mil->payload_virt, buf, mtd->writesize);
		payload_virt = mil->payload_virt;
		payload_phys = mil->payload_phys;

		memcpy(mil->auxiliary_virt, nand->oob_poi, mtd->oobsize);
		auxiliary_virt = mil->auxiliary_virt;
		auxiliary_phys = mil->auxiliary_phys;

		/* Handle block mark swapping. */

		mil_handle_block_mark_swapping(this,
				(void *) payload_virt, (void *) auxiliary_virt);

	} else {

		/*
		 * If control arrives here, we're not doing block mark swapping,
		 * so we can to try and use the caller's buffers.
		 */

		error = mil_outgoing_buffer_dma_begin(this,
				buf, mtd->writesize,
				mil->payload_virt, mil->payload_phys,
				nfc_geo->payload_size_in_bytes,
				&payload_virt, &payload_phys);

		if (error) {
			dev_err(dev, "[%s] Inadequate payload DMA buffer\n",
								__func__);
			goto exit_payload;
		}

		error = mil_outgoing_buffer_dma_begin(this,
				nand->oob_poi, mtd->oobsize,
				mil->auxiliary_virt, mil->auxiliary_phys,
				nfc_geo->auxiliary_size_in_bytes,
				&auxiliary_virt, &auxiliary_phys);

		if (error) {
			dev_err(dev, "[%s] Inadequate auxiliary DMA buffer\n",
								__func__);
			goto exit_auxiliary;
		}

	}

	/* Ask the NFC. */

	error = nfc->send_page(this, mil->current_chip,
						payload_phys, auxiliary_phys);

	if (error)
		dev_err(dev, "[%s] Error in ECC-based write: %d\n",
							__func__, error);

	/* Return. */

	if (!rom->swap_block_mark)
		mil_outgoing_buffer_dma_end(this, nand->oob_poi, mtd->oobsize,
				mil->auxiliary_virt, mil->auxiliary_phys,
				nfc_geo->auxiliary_size_in_bytes,
				auxiliary_virt, auxiliary_phys);
exit_auxiliary:
	if (!rom->swap_block_mark)
		mil_outgoing_buffer_dma_end(this, buf, mtd->writesize,
				mil->payload_virt, mil->payload_phys,
				nfc_geo->payload_size_in_bytes,
				payload_virt, payload_phys);
exit_payload:

	gpmi_nfc_add_event("< mil_ecc_write_page", -1);

}

/**
 * mil_hook_read_oob() - Hooked MTD Interface read_oob().
 *
 * This function is a veneer that replaces the function originally installed by
 * the NAND Flash MTD code. See the description of the raw_oob_mode field in
 * struct mil for more information about this.
 *
 * @mtd:   A pointer to the MTD.
 * @from:  The starting address to read.
 * @ops:   Describes the operation.
 */
static int mil_hook_read_oob(struct mtd_info *mtd,
					loff_t from, struct mtd_oob_ops *ops)
{
	register struct nand_chip  *chip = mtd->priv;
	struct gpmi_nfc_data       *this = chip->priv;
	struct mil                 *mil  = &this->mil;
	int                        ret;

	mil->raw_oob_mode = ops->mode == MTD_OOB_RAW;
	ret = mil->hooked_read_oob(mtd, from, ops);
	mil->raw_oob_mode = false;
	return ret;
}

/**
 * mil_hook_write_oob() - Hooked MTD Interface write_oob().
 *
 * This function is a veneer that replaces the function originally installed by
 * the NAND Flash MTD code. See the description of the raw_oob_mode field in
 * struct mil for more information about this.
 *
 * @mtd:   A pointer to the MTD.
 * @to:    The starting address to write.
 * @ops:   Describes the operation.
 */
static int mil_hook_write_oob(struct mtd_info *mtd,
					loff_t to, struct mtd_oob_ops *ops)
{
	register struct nand_chip  *chip = mtd->priv;
	struct gpmi_nfc_data       *this = chip->priv;
	struct mil                 *mil  = &this->mil;
	int                        ret;

	mil->raw_oob_mode = ops->mode == MTD_OOB_RAW;
	ret = mil->hooked_write_oob(mtd, to, ops);
	mil->raw_oob_mode = false;
	return ret;
}

/**
 * mil_hook_block_markbad() - Hooked MTD Interface block_markbad().
 *
 * This function is a veneer that replaces the function originally installed by
 * the NAND Flash MTD code. See the description of the marking_a_bad_block field
 * in struct mil for more information about this.
 *
 * @mtd:  A pointer to the MTD.
 * @ofs:  Byte address of the block to mark.
 */
static int mil_hook_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
	register struct nand_chip  *chip = mtd->priv;
	struct gpmi_nfc_data       *this = chip->priv;
	struct mil                 *mil  = &this->mil;
	int                        ret;

	mil->marking_a_bad_block = true;
	ret = mil->hooked_block_markbad(mtd, ofs);
	mil->marking_a_bad_block = false;
	return ret;
}

/**
 * mil_ecc_read_oob() - MTD Interface ecc.read_oob().
 *
 * There are several places in this driver where we have to handle the OOB and
 * block marks. This is the function where things are the most complicated, so
 * this is where we try to explain it all. All the other places refer back to
 * here.
 *
 * These are the rules, in order of decreasing importance:
 *
 * 1) Nothing the caller does can be allowed to imperil the block mark, so all
 *    write operations take measures to protect it.
 *
 * 2) In read operations, the first byte of the OOB we return must reflect the
 *    true state of the block mark, no matter where that block mark appears in
 *    the physical page.
 *
 * 3) ECC-based read operations return an OOB full of set bits (since we never
 *    allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
 *    return).
 *
 * 4) "Raw" read operations return a direct view of the physical bytes in the
 *    page, using the conventional definition of which bytes are data and which
 *    are OOB. This gives the caller a way to see the actual, physical bytes
 *    in the page, without the distortions applied by our ECC engine.
 *
 *
 * What we do for this specific read operation depends on two questions:
 *
 * 1) Are we doing a "raw" read, or an ECC-based read?
 *
 * 2) Are we using block mark swapping or transcription?
 *
 * There are four cases, illustrated by the following Karnaugh map:
 *
 *                    |           Raw           |         ECC-based       |
 *       -------------+-------------------------+-------------------------+
 *                    | Read the conventional   |                         |
 *                    | OOB at the end of the   |                         |
 *       Swapping     | page and return it. It  |                         |
 *                    | contains exactly what   |                         |
 *                    | we want.                | Read the block mark and |
 *       -------------+-------------------------+ return it in a buffer   |
 *                    | Read the conventional   | full of set bits.       |
 *                    | OOB at the end of the   |                         |
 *                    | page and also the block |                         |
 *       Transcribing | mark in the metadata.   |                         |
 *                    | Copy the block mark     |                         |
 *                    | into the first byte of  |                         |
 *                    | the OOB.                |                         |
 *       -------------+-------------------------+-------------------------+
 *
 * Note that we break rule #4 in the Transcribing/Raw case because we're not
 * giving an accurate view of the actual, physical bytes in the page (we're
 * overwriting the block mark). That's OK because it's more important to follow
 * rule #2.
 *
 * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
 * easy. When reading a page, for example, the NAND Flash MTD code calls our
 * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
 * ECC-based or raw view of the page is implicit in which function it calls
 * (there is a similar pair of ECC-based/raw functions for writing).
 *
 * Since MTD assumes the OOB is not covered by ECC, there is no pair of
 * ECC-based/raw functions for reading or or writing the OOB. The fact that the
 * caller wants an ECC-based or raw view of the page is not propagated down to
 * this driver.
 *
 * Since our OOB *is* covered by ECC, we need this information. So, we hook the
 * ecc.read_oob and ecc.write_oob function pointers in the owning
 * struct mtd_info with our own functions. These hook functions set the
 * raw_oob_mode field so that, when control finally arrives here, we'll know
 * what to do.
 *
 * @mtd:     A pointer to the owning MTD.
 * @nand:    A pointer to the owning NAND Flash MTD.
 * @page:    The page number to read.
 * @sndcmd:  Indicates this function should send a command to the chip before
 *           reading the out-of-band bytes. This is only false for small page
 *           chips that support auto-increment.
 */
static int mil_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand,
							int page, int sndcmd)
{
	struct gpmi_nfc_data      *this     = nand->priv;
	struct physical_geometry  *physical = &this->physical_geometry;
	struct mil                *mil      = &this->mil;
	struct boot_rom_helper    *rom      =  this->rom;
	int                       block_mark_column;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc ecc_read_oob] "
		"page: 0x%06x, sndcmd: %s\n", page, sndcmd ? "Yes" : "No");

	gpmi_nfc_add_event("> mil_ecc_read_oob", 1);

	/*
	 * First, fill in the OOB buffer. If we're doing a raw read, we need to
	 * get the bytes from the physical page. If we're not doing a raw read,
	 * we need to fill the buffer with set bits.
	 */

	if (mil->raw_oob_mode) {

		/*
		 * If control arrives here, we're doing a "raw" read. Send the
		 * command to read the conventional OOB.
		 */

		nand->cmdfunc(mtd, NAND_CMD_READ0,
				physical->page_data_size_in_bytes, page);

		/* Read out the conventional OOB. */

		nand->read_buf(mtd, nand->oob_poi, mtd->oobsize);

	} else {

		/*
		 * If control arrives here, we're not doing a "raw" read. Fill
		 * the OOB buffer with set bits.
		 */

		memset(nand->oob_poi, ~0, mtd->oobsize);

	}

	/*
	 * Now, we want to make sure the block mark is correct. In the
	 * Swapping/Raw case, we already have it. Otherwise, we need to
	 * explicitly read it.
	 */

	if (!(rom->swap_block_mark && mil->raw_oob_mode)) {

		/* First, figure out where the block mark is. */

		if (rom->swap_block_mark)
			block_mark_column = physical->page_data_size_in_bytes;
		else
			block_mark_column = 0;

		/* Send the command to read the block mark. */

		nand->cmdfunc(mtd, NAND_CMD_READ0, block_mark_column, page);

		/* Read the block mark into the first byte of the OOB buffer. */

		nand->oob_poi[0] = nand->read_byte(mtd);

	}

	/*
	 * Return true, indicating that the next call to this function must send
	 * a command.
	 */

	gpmi_nfc_add_event("< mil_ecc_read_oob", -1);

	return true;

}

/**
 * mil_ecc_write_oob() - MTD Interface ecc.write_oob().
 *
 * @mtd:   A pointer to the owning MTD.
 * @nand:  A pointer to the owning NAND Flash MTD.
 * @page:  The page number to write.
 */
static int mil_ecc_write_oob(struct mtd_info *mtd,
					struct nand_chip *nand, int page)
{
	struct gpmi_nfc_data      *this     = nand->priv;
	struct device             *dev      =  this->dev;
	struct physical_geometry  *physical = &this->physical_geometry;
	struct mil                *mil      = &this->mil;
	struct boot_rom_helper    *rom      =  this->rom;
	uint8_t                   block_mark = 0;
	int                       block_mark_column;
	int                       status;
	int                       error = 0;

	DEBUG(MTD_DEBUG_LEVEL2,
			"[gpmi_nfc ecc_write_oob] page: 0x%06x\n", page);

	gpmi_nfc_add_event("> mil_ecc_write_oob", -1);

	/*
	 * There are fundamental incompatibilities between the i.MX GPMI NFC and
	 * the NAND Flash MTD model that make it essentially impossible to write
	 * the out-of-band bytes.
	 *
	 * We permit *ONE* exception. If the *intent* of writing the OOB is to
	 * mark a block bad, we can do that.
	 */

	if (!mil->marking_a_bad_block) {
		dev_emerg(dev, "This driver doesn't support writing the OOB\n");
		WARN_ON(1);
		error = -EIO;
		goto exit;
	}

	/*
	 * If control arrives here, we're marking a block bad. First, figure out
	 * where the block mark is.
	 *
	 * If we're using swapping, the block mark is in the conventional
	 * location. Otherwise, we're using transcription, and the block mark
	 * appears in the first byte of the page.
	 */

	if (rom->swap_block_mark)
		block_mark_column = physical->page_data_size_in_bytes;
	else
		block_mark_column = 0;

	/* Write the block mark. */

	nand->cmdfunc(mtd, NAND_CMD_SEQIN, block_mark_column, page);
	nand->write_buf(mtd, &block_mark, 1);
	nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);

	status = nand->waitfunc(mtd, nand);

	/* Check if it worked. */

	if (status & NAND_STATUS_FAIL)
		error = -EIO;

	/* Return. */

exit:

	gpmi_nfc_add_event("< mil_ecc_write_oob", -1);

	return error;

}

/**
 * mil_block_bad - Claims all blocks are good.
 *
 * In principle, this function is *only* called when the NAND Flash MTD system
 * isn't allowed to keep an in-memory bad block table, so it is forced to ask
 * the driver for bad block information.
 *
 * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so
 * this function is *only* called when we take it away.
 *
 * We take away the in-memory BBT when the user sets the "ignorebad" parameter,
 * which indicates that all blocks should be reported good.
 *
 * Thus, this function is only called when we want *all* blocks to look good,
 * so it *always* return success.
 *
 * @mtd:      Ignored.
 * @ofs:      Ignored.
 * @getchip:  Ignored.
 */
static int mil_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
	return 0;
}

/**
 * mil_set_physical_geometry() - Set up the physical medium geometry.
 *
 * This function retrieves the physical geometry information discovered by
 * nand_scan(), corrects it, and records it in the per-device data structure.
 *
 * @this:  Per-device data.
 */
static int mil_set_physical_geometry(struct gpmi_nfc_data  *this)
{
	struct mil                *mil      = &this->mil;
	struct physical_geometry  *physical = &this->physical_geometry;
	struct nand_chip          *nand     = &mil->nand;
	struct nand_device_info   *info     = &this->device_info;
	unsigned int              block_size_in_pages;
	unsigned int              chip_size_in_blocks;
	unsigned int              chip_size_in_pages;
	uint64_t                  medium_size_in_bytes;

	/*
	 * Record the number of physical chips that MTD found.
	 */

	physical->chip_count = nand->numchips;

	/*
	 * We know the total size of a page. We need to break that down into the
	 * data size and OOB size. The data size is the largest power of two
	 * that will fit in the given page size. The OOB size is what's left
	 * over.
	 */

	physical->page_data_size_in_bytes =
				1 << (fls(info->page_total_size_in_bytes) - 1);

	physical->page_oob_size_in_bytes =
				info->page_total_size_in_bytes -
					physical->page_data_size_in_bytes;

	/*
	 * Now that we know the page data size, we can multiply this by the
	 * number of pages in a block to compute the block size.
	 */

	physical->block_size_in_bytes =
		physical->page_data_size_in_bytes * info->block_size_in_pages;

	/* Get the chip size. */

	physical->chip_size_in_bytes = info->chip_size_in_bytes;

	/* Compute some interesting facts. */

	block_size_in_pages  =
			physical->block_size_in_bytes >>
				(fls(physical->page_data_size_in_bytes) - 1);
	chip_size_in_pages   =
			physical->chip_size_in_bytes >>
				(fls(physical->page_data_size_in_bytes) - 1);
	chip_size_in_blocks  =
			physical->chip_size_in_bytes >>
				(fls(physical->block_size_in_bytes) - 1);
	medium_size_in_bytes =
			physical->chip_size_in_bytes * physical->chip_count;

	/* Report. */

	#if defined(DETAILED_INFO)

	pr_info("-----------------\n");
	pr_info("Physical Geometry\n");
	pr_info("-----------------\n");
	pr_info("Chip Count             : %d\n", physical->chip_count);
	pr_info("Page Data Size in Bytes: %u (0x%x)\n",
			physical->page_data_size_in_bytes,
			physical->page_data_size_in_bytes);
	pr_info("Page OOB Size in Bytes : %u\n",
			physical->page_oob_size_in_bytes);
	pr_info("Block Size in Bytes    : %u (0x%x)\n",
			physical->block_size_in_bytes,
			physical->block_size_in_bytes);
	pr_info("Block Size in Pages    : %u (0x%x)\n",
			block_size_in_pages,
			block_size_in_pages);
	pr_info("Chip Size in Bytes     : %llu (0x%llx)\n",
			physical->chip_size_in_bytes,
			physical->chip_size_in_bytes);
	pr_info("Chip Size in Pages     : %u (0x%x)\n",
			chip_size_in_pages, chip_size_in_pages);
	pr_info("Chip Size in Blocks    : %u (0x%x)\n",
			chip_size_in_blocks, chip_size_in_blocks);
	pr_info("Medium Size in Bytes   : %llu (0x%llx)\n",
			medium_size_in_bytes, medium_size_in_bytes);

	#endif

	/* Return success. */

	return 0;

}

/**
 * mil_set_nfc_geometry() - Set up the NFC geometry.
 *
 * This function calls the NFC HAL to select an NFC geometry that is compatible
 * with the medium's physical geometry.
 *
 * @this:  Per-device data.
 */
static int mil_set_nfc_geometry(struct gpmi_nfc_data  *this)
{
	struct nfc_hal       *nfc =  this->nfc;
#if defined(DETAILED_INFO)
	struct nfc_geometry  *geo = &this->nfc_geometry;
#endif
	/* Set the NFC geometry. */

	if (nfc->set_geometry(this))
		return !0;

	/* Report. */

	#if defined(DETAILED_INFO)

	pr_info("------------\n");
	pr_info("NFC Geometry\n");
	pr_info("------------\n");
	pr_info("ECC Algorithm          : %s\n", geo->ecc_algorithm);
	pr_info("ECC Strength           : %u\n", geo->ecc_strength);
	pr_info("Page Size in Bytes     : %u\n", geo->page_size_in_bytes);
	pr_info("Metadata Size in Bytes : %u\n", geo->metadata_size_in_bytes);
	pr_info("ECC Chunk Size in Bytes: %u\n", geo->ecc_chunk_size_in_bytes);
	pr_info("ECC Chunk Count        : %u\n", geo->ecc_chunk_count);
	pr_info("Payload Size in Bytes  : %u\n", geo->payload_size_in_bytes);
	pr_info("Auxiliary Size in Bytes: %u\n", geo->auxiliary_size_in_bytes);
	pr_info("Auxiliary Status Offset: %u\n", geo->auxiliary_status_offset);
	pr_info("Block Mark Byte Offset : %u\n", geo->block_mark_byte_offset);
	pr_info("Block Mark Bit Offset  : %u\n", geo->block_mark_bit_offset);

	#endif

	/* Return success. */

	return 0;

}

/**
 * mil_set_boot_rom_helper_geometry() - Set up the Boot ROM Helper geometry.
 *
 * @this:  Per-device data.
 */
static int mil_set_boot_rom_helper_geometry(struct gpmi_nfc_data  *this)
{
	struct boot_rom_helper    *rom =  this->rom;
#if defined(DETAILED_INFO)
	struct boot_rom_geometry  *geo = &this->rom_geometry;
#endif

	/* Set the Boot ROM Helper geometry. */

	if (rom->set_geometry(this))
		return !0;

	/* Report. */

	#if defined(DETAILED_INFO)

	pr_info("-----------------\n");
	pr_info("Boot ROM Geometry\n");
	pr_info("-----------------\n");
	pr_info("Boot Area Count            : %u\n", geo->boot_area_count);
	pr_info("Boot Area Size in Bytes    : %u (0x%x)\n",
		geo->boot_area_size_in_bytes, geo->boot_area_size_in_bytes);
	pr_info("Stride Size in Pages       : %u\n", geo->stride_size_in_pages);
	pr_info("Search Area Stride Exponent: %u\n",
					geo->search_area_stride_exponent);

	#endif

	/* Return success. */

	return 0;

}

/**
 * mil_set_mtd_geometry() - Set up the MTD geometry.
 *
 * This function adjusts the owning MTD data structures to match the logical
 * geometry we've chosen.
 *
 * @this:  Per-device data.
 */
static int mil_set_mtd_geometry(struct gpmi_nfc_data *this)
{
	struct physical_geometry  *physical = &this->physical_geometry;
	struct mil                *mil      = &this->mil;
	struct nand_ecclayout     *layout   = &mil->oob_layout;
	struct nand_chip          *nand     = &mil->nand;
	struct mtd_info           *mtd      = &mil->mtd;

	/* Configure the struct nand_ecclayout. */

	layout->eccbytes          = 0;
	layout->oobavail          = physical->page_oob_size_in_bytes;
	layout->oobfree[0].offset = 0;
	layout->oobfree[0].length = physical->page_oob_size_in_bytes;

	/* Configure the struct mtd_info. */

	mtd->size        = nand->numchips * physical->chip_size_in_bytes;
	mtd->erasesize   = physical->block_size_in_bytes;
	mtd->writesize   = physical->page_data_size_in_bytes;
	mtd->ecclayout   = layout;
	mtd->oobavail    = mtd->ecclayout->oobavail;
	mtd->oobsize     = mtd->ecclayout->oobavail + mtd->ecclayout->eccbytes;
	mtd->subpage_sft = 0; /* We don't support sub-page writing. */

	/* Configure the struct nand_chip. */

	nand->chipsize         = physical->chip_size_in_bytes;
	nand->page_shift       = ffs(mtd->writesize) - 1;
	nand->pagemask         = (nand->chipsize >> nand->page_shift) - 1;
	nand->subpagesize      = mtd->writesize >> mtd->subpage_sft;
	nand->phys_erase_shift = ffs(mtd->erasesize) - 1;
	nand->bbt_erase_shift  = nand->phys_erase_shift;
	nand->oob_poi          = nand->buffers->databuf + mtd->writesize;
	nand->ecc.layout       = layout;
	if (nand->chipsize & 0xffffffff)
		nand->chip_shift = ffs((unsigned) nand->chipsize) - 1;
	else
		nand->chip_shift =
				ffs((unsigned) (nand->chipsize >> 32)) + 32 - 1;

	/* Return success. */

	return 0;

}

/**
 * mil_set_geometry() - Set up the medium geometry.
 *
 * @this:  Per-device data.
 */
static int mil_set_geometry(struct gpmi_nfc_data  *this)
{
	struct device        *dev      =  this->dev;
	struct nfc_geometry  *nfc_geo  = &this->nfc_geometry;
	struct mil           *mil      = &this->mil;

	/* Set up the various layers of geometry, in this specific order. */

	if (mil_set_physical_geometry(this))
		return -ENXIO;

	if (mil_set_nfc_geometry(this))
		return -ENXIO;

	if (mil_set_boot_rom_helper_geometry(this))
		return -ENXIO;

	if (mil_set_mtd_geometry(this))
		return -ENXIO;

	/*
	 * Allocate the page buffer.
	 *
	 * Both the payload buffer and the auxiliary buffer must appear on
	 * 32-bit boundaries. We presume the size of the payload buffer is a
	 * power of two and is much larger than four, which guarantees the
	 * auxiliary buffer will appear on a 32-bit boundary.
	 */

	mil->page_buffer_size = nfc_geo->payload_size_in_bytes +
					nfc_geo->auxiliary_size_in_bytes;

	mil->page_buffer_virt =
		dma_alloc_coherent(dev, mil->page_buffer_size,
					&mil->page_buffer_phys, GFP_DMA);

	if (!mil->page_buffer_virt)
		return -ENOMEM;

	/* Slice up the page buffer. */

	mil->payload_virt = mil->page_buffer_virt;
	mil->payload_phys = mil->page_buffer_phys;

	mil->auxiliary_virt = ((char *) mil->payload_virt) +
						nfc_geo->payload_size_in_bytes;
	mil->auxiliary_phys = mil->payload_phys +
						nfc_geo->payload_size_in_bytes;

	/* Return success. */

	return 0;

}

/**
 * mil_pre_bbt_scan() - Prepare for the BBT scan.
 *
 * @this:  Per-device data.
 */
static int mil_pre_bbt_scan(struct gpmi_nfc_data  *this)
{
	struct device             *dev      =  this->dev;
	struct physical_geometry  *physical = &this->physical_geometry;
	struct boot_rom_helper    *rom      =  this->rom;
	struct mil                *mil      = &this->mil;
	struct nand_chip          *nand     = &mil->nand;
	struct mtd_info           *mtd      = &mil->mtd;
	unsigned int              block_count;
	unsigned int              block;
	int                       chip;
	int                       page;
	loff_t                    byte;
	uint8_t                   block_mark;
	int                       error;

	/*
	 * Check if we can use block mark swapping, which enables us to leave
	 * the block marks where they are. If so, we don't need to do anything
	 * at all.
	 */

	if (rom->swap_block_mark)
		return 0;

	/*
	 * If control arrives here, we can't use block mark swapping, which
	 * means we're forced to use transcription. First, scan for the
	 * transcription stamp. If we find it, then we don't have to do
	 * anything -- the block marks are already transcribed.
	 */

	if (rom->check_transcription_stamp(this))
		return 0;

	/*
	 * If control arrives here, we couldn't find a transcription stamp, so
	 * so we presume the block marks are in the conventional location.
	 */

	pr_info("Transcribing bad block marks...\n");

	/* Compute the number of blocks in the entire medium. */

	block_count =
		physical->chip_size_in_bytes >> nand->phys_erase_shift;

	/*
	 * Loop over all the blocks in the medium, transcribing block marks as
	 * we go.
	 */

	for (block = 0; block < block_count; block++) {

		/*
		 * Compute the chip, page and byte addresses for this block's
		 * conventional mark.
		 */

		chip = block >> (nand->chip_shift - nand->phys_erase_shift);
		page = block << (nand->phys_erase_shift - nand->page_shift);
		byte = block <<  nand->phys_erase_shift;

		/* Select the chip. */

		nand->select_chip(mtd, chip);

		/* Send the command to read the conventional block mark. */

		nand->cmdfunc(mtd, NAND_CMD_READ0,
				physical->page_data_size_in_bytes, page);

		/* Read the conventional block mark. */

		block_mark = nand->read_byte(mtd);

		/*
		 * Check if the block is marked bad. If so, we need to mark it
		 * again, but this time the result will be a mark in the
		 * location where we transcribe block marks.
		 *
		 * Notice that we have to explicitly set the marking_a_bad_block
		 * member before we call through the block_markbad function
		 * pointer in the owning struct nand_chip. If we could call
		 * though the block_markbad function pointer in the owning
		 * struct mtd_info, which we have hooked, then this would be
		 * taken care of for us. Unfortunately, we can't because that
		 * higher-level code path will do things like consulting the
		 * in-memory bad block table -- which doesn't even exist yet!
		 * So, we have to call at a lower level and handle some details
		 * ourselves.
		 */

		if (block_mark != 0xff) {
			pr_info("Transcribing mark in block %u\n", block);
			mil->marking_a_bad_block = true;
			error = nand->block_markbad(mtd, byte);
			mil->marking_a_bad_block = false;
			if (error)
				dev_err(dev, "Failed to mark block bad with "
							"error %d\n", error);
		}

		/* Deselect the chip. */

		nand->select_chip(mtd, -1);

	}

	/* Write the stamp that indicates we've transcribed the block marks. */

	rom->write_transcription_stamp(this);

	/* Return success. */

	return 0;

}

/**
 * mil_scan_bbt() - MTD Interface scan_bbt().
 *
 * The HIL calls this function once, when it initializes the NAND Flash MTD.
 *
 * Nominally, the purpose of this function is to look for or create the bad
 * block table. In fact, since the HIL calls this function at the very end of
 * the initialization process started by nand_scan(), and the HIL doesn't have a
 * more formal mechanism, everyone "hooks" this function to continue the
 * initialization process.
 *
 * At this point, the physical NAND Flash chips have been identified and
 * counted, so we know the physical geometry. This enables us to make some
 * important configuration decisions.
 *
 * The return value of this function propogates directly back to this driver's
 * call to nand_scan(). Anything other than zero will cause this driver to
 * tear everything down and declare failure.
 *
 * @mtd:  A pointer to the owning MTD.
 */
static int mil_scan_bbt(struct mtd_info *mtd)
{
	struct nand_chip         *nand = mtd->priv;
	struct gpmi_nfc_data     *this = nand->priv;
	struct nfc_hal           *nfc  =  this->nfc;
	struct mil               *mil  = &this->mil;
	int                      saved_chip_number;
	uint8_t                  id_bytes[NAND_DEVICE_ID_BYTE_COUNT];
	struct nand_device_info  *info;
	struct gpmi_nfc_timing   timing;
	int                      error;

	DEBUG(MTD_DEBUG_LEVEL2, "[gpmi_nfc scan_bbt] \n");

	/*
	 * Tell MTD users that the out-of-band area can't be written.
	 *
	 * This flag is not part of the standard kernel source tree. It comes
	 * from a patch that touches both MTD and JFFS2.
	 *
	 * The problem is that, without this patch, JFFS2 believes it can write
	 * the data area and the out-of-band area separately. This is wrong for
	 * two reasons:
	 *
	 *     1)  Our NFC distributes out-of-band bytes throughout the page,
	 *         intermingled with the data, and covered by the same ECC.
	 *         Thus, it's not possible to write the out-of-band bytes and
	 *         data bytes separately.
	 *
	 *     2)  Large page (MLC) Flash chips don't support partial page
	 *         writes. You must write the entire page at a time. Thus, even
	 *         if our NFC didn't force you to write out-of-band and data
	 *         bytes together, it would *still* be a bad idea to do
	 *         otherwise.
	 */

	mtd->flags &= ~MTD_OOB_WRITEABLE;

	/*
	 * MTD identified the attached NAND Flash devices, but we have a much
	 * better database that we want to consult. First, we need to gather all
	 * the ID bytes from the first chip (MTD only read the first two).
	 */

	saved_chip_number = mil->current_chip;
	nand->select_chip(mtd, 0);

	nand->cmdfunc(mtd, NAND_CMD_READID, 0, -1);
	nand->read_buf(mtd, id_bytes, NAND_DEVICE_ID_BYTE_COUNT);

	nand->select_chip(mtd, saved_chip_number);

	/* Look up this device in our database. */

	info = nand_device_get_info(id_bytes);

	/* Check if we understand this device. */

	if (!info) {
		pr_err("Unrecognized NAND Flash device.\n");
		return !0;
	}

	/* Display the information we discovered. */

	#if defined(DETAILED_INFO)
	pr_info("-----------------------------\n");
	pr_info("NAND Flash Device Information\n");
	pr_info("-----------------------------\n");
	nand_device_print_info(info);
	#endif

	/*
	 * Copy the device info into the per-device data. We can't just keep
	 * the pointer because that storage is reclaimed after initialization.
	 */

	this->device_info = *info;
	this->device_info.description = kstrdup(info->description, GFP_KERNEL);

	/* Set up geometry. */

	error = mil_set_geometry(this);

	if (error)
		return error;

	/* Set up timing. */

	timing.data_setup_in_ns        = info->data_setup_in_ns;
	timing.data_hold_in_ns         = info->data_hold_in_ns;
	timing.address_setup_in_ns     = info->address_setup_in_ns;
	timing.gpmi_sample_delay_in_ns = info->gpmi_sample_delay_in_ns;
	timing.tREA_in_ns              = info->tREA_in_ns;
	timing.tRLOH_in_ns             = info->tRLOH_in_ns;
	timing.tRHOH_in_ns             = info->tRHOH_in_ns;

	error = nfc->set_timing(this, &timing);

	if (error)
		return error;

	/* Prepare for the BBT scan. */

	error = mil_pre_bbt_scan(this);

	if (error)
		return error;

	/* We use the reference implementation for bad block management. */

	error = nand_default_bbt(mtd);

	if (error)
		return error;

	/* Return success. */

	return 0;

}

/**
 * mil_boot_areas_init() - Initializes boot areas.
 *
 * @this:  Per-device data.
 */
static int mil_boot_areas_init(struct gpmi_nfc_data *this)
{
	struct device                  *dev      =  this->dev;
	struct physical_geometry       *physical = &this->physical_geometry;
	struct boot_rom_geometry       *rom      = &this->rom_geometry;
	struct mil                     *mil      = &this->mil;
	struct mtd_info                *mtd      = &mil->mtd;
	struct nand_chip               *nand     = &mil->nand;
	int                            mtd_support_is_adequate;
	unsigned int                   i;
	struct mtd_partition           partitions[4];
	struct mtd_info                *search_mtd;
	struct mtd_info                *chip_0_remainder_mtd = 0;
	struct mtd_info                *medium_remainder_mtd = 0;
	struct mtd_info                *concatenate[2];

	/*
	 * Here we declare the static strings we use to name partitions. We use
	 * static strings because, as of 2.6.31, the partitioning code *always*
	 * registers the partition MTDs it creates and leaves behind *no* other
	 * trace of its work. So, once we've created a partition, we must search
	 * the master MTD table to find the MTDs we created. Since we're using
	 * static strings, we can simply search the master table for an MTD with
	 * a name field pointing to a known address.
	 */

	static char  *chip_0_boot_name      = "gpmi-nfc-0-boot";
	static char  *chip_0_remainder_name = "gpmi-nfc-0-remainder";
	static char  *chip_1_boot_name      = "gpmi-nfc-1-boot";
	static char  *medium_remainder_name = "gpmi-nfc-remainder";
	static char  *general_use_name      = "gpmi-nfc-general-use";

	/* Check if we're protecting the boot areas.*/

	if (!rom->boot_area_count) {

		/*
		 * If control arrives here, we're not protecting the boot areas.
		 * In this case, there are not boot area partitons, and the main
		 * MTD is the general use MTD.
		 */

		mil->general_use_mtd = &mil->mtd;

		return 0;

	}

	/*
	 * If control arrives here, we're protecting the boot areas. Check if we
	 * have the MTD support we need.
	 */

	pr_info("Boot area protection is enabled.\n");

	if (rom->boot_area_count > 1) {

		/*
		 * If the Boot ROM wants more than one boot area, then we'll
		 * need to create partitions *and* concatenate them.
		 */

		#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)
			mtd_support_is_adequate = true;
		#else
			mtd_support_is_adequate = false;
		#endif

	} else if (rom->boot_area_count == 1) {

		/*
		 * If the Boot ROM wants only one boot area, then we only need
		 * to create partitions -- we don't need to concatenate them.
		 */

		#if defined(CONFIG_MTD_PARTITIONS)
			mtd_support_is_adequate = true;
		#else
			mtd_support_is_adequate = false;
		#endif

	} else {

		/*
		 * If control arrives here, we're protecting the boot area, but
		 * somehow the boot area count was set to zero. This doesn't
		 * make any sense.
		 */

		dev_err(dev, "Internal error: boot area count is "
						"incorrectly set to zero.");
		return -ENXIO;

	}

	if (!mtd_support_is_adequate) {
		dev_err(dev, "Configured MTD support is inadequate to "
						"protect the boot area(s).");
		return -ENXIO;
	}

	/*
	 * If control arrives here, we're protecting boot areas and we have
	 * everything we need to do so.
	 *
	 * We have special code to handle the case for one boot area.
	 *
	 * The code that handles "more than one" boot area actually only handles
	 * two. We *could* write the general case, but that would take a lot of
	 * time to both write and test -- and, right now, we don't have a chip
	 * that cares.
	 */

	/* Check if a boot area is larger than a single chip. */

	if (rom->boot_area_size_in_bytes > physical->chip_size_in_bytes) {
		dev_emerg(dev, "Boot area size is larger than a chip");
		return -ENXIO;
	}

	if (rom->boot_area_count == 1) {

#if defined(CONFIG_MTD_PARTITIONS)

		/*
		 * We partition the medium like so:
		 *
		 * +------+----------------------------------------------------+
		 * | Boot |                    General Use                     |
		 * +------+----------------------------------------------------+
		 */

		/* Chip 0 Boot */

		partitions[0].name       = chip_0_boot_name;
		partitions[0].offset     = 0;
		partitions[0].size       = rom->boot_area_size_in_bytes;
		partitions[0].mask_flags = 0;

		/* General Use */

		partitions[1].name       = general_use_name;
		partitions[1].offset     = rom->boot_area_size_in_bytes;
		partitions[1].size       = MTDPART_SIZ_FULL;
		partitions[1].mask_flags = 0;

		/* Construct and register the partitions. */

		add_mtd_partitions(mtd, partitions, 2);

		/* Find the general use MTD. */

		for (i = 0; i < MAX_MTD_DEVICES; i++) {

			/* Get the current MTD so we can examine it. */

			search_mtd = get_mtd_device(0, i);

			/* Check if we got nonsense. */

			if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV)))
				continue;

			/* Check if the current MTD is one of our remainders. */

			if (search_mtd->name == general_use_name)
				mil->general_use_mtd = search_mtd;

			/* Put the MTD back. We only wanted a quick look. */

			put_mtd_device(search_mtd);

		}

		if (!mil->general_use_mtd) {
			dev_emerg(dev, "Can't find general use MTD");
			BUG();
		}

#endif

	} else if (rom->boot_area_count == 2) {

#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)

		/*
		 * If control arrives here, there is more than one boot area.
		 * We partition the medium and concatenate the remainders like
		 * so:
		 *
		 *  --- Chip 0 ---   --- Chip 1 --- ... ------- Chip N -------
		 * /              \ /                                         \
		 * +----+----------+----+--------------- ... ------------------+
		 * |Boot|Remainder |Boot|              Remainder               |
		 * +----+----------+----+--------------- ... ------------------+
		 *      |          |   /                                      /
		 *      |          |  /                                      /
		 *      |          | /                                      /
		 *      |          |/                                      /
		 *      +----------+----------- ... ----------------------+
		 *      |                   General Use                   |
		 *      +---------------------- ... ----------------------+
		 *
		 * Notice that the results we leave in the master MTD table
		 * look like this:
		 *
		 *    * Chip 0 Boot Area
		 *    * Chip 1 Boot Area
		 *    * General Use
		 *
		 * Some user space programs expect the boot partitions to
		 * appear first. This is naive, but let's try not to cause
		 * any trouble, where we can avoid it.
		 */

		/* Chip 0 Boot */

		partitions[0].name       = chip_0_boot_name;
		partitions[0].offset     = 0;
		partitions[0].size       = rom->boot_area_size_in_bytes;
		partitions[0].mask_flags = 0;

		/* Chip 1 Boot */

		partitions[1].name       = chip_1_boot_name;
		partitions[1].offset     = nand->chipsize;
		partitions[1].size       = rom->boot_area_size_in_bytes;
		partitions[1].mask_flags = 0;

		/* Chip 0 Remainder */

		partitions[2].name       = chip_0_remainder_name;
		partitions[2].offset     = rom->boot_area_size_in_bytes;
		partitions[2].size       = nand->chipsize -
						rom->boot_area_size_in_bytes;
		partitions[2].mask_flags = 0;

		/* Medium Remainder */

		partitions[3].name       = medium_remainder_name;
		partitions[3].offset     = nand->chipsize +
						rom->boot_area_size_in_bytes;
		partitions[3].size       = MTDPART_SIZ_FULL;
		partitions[3].mask_flags = 0;

		/* Construct and register the partitions. */

		add_mtd_partitions(mtd, partitions, 4);

		/* Find the remainder partitions. */

		for (i = 0; i < MAX_MTD_DEVICES; i++) {

			/* Get the current MTD so we can examine it. */

			search_mtd = get_mtd_device(0, i);

			/* Check if we got nonsense. */

			if ((!search_mtd) || (search_mtd == ERR_PTR(-ENODEV)))
				continue;

			/* Check if the current MTD is one of our remainders. */

			if (search_mtd->name == chip_0_remainder_name)
				chip_0_remainder_mtd = search_mtd;

			if (search_mtd->name == medium_remainder_name)
				medium_remainder_mtd = search_mtd;

			/* Put the MTD back. We only wanted a quick look. */

			put_mtd_device(search_mtd);

		}

		if (!chip_0_remainder_mtd || !medium_remainder_mtd) {
			dev_emerg(dev, "Can't find remainder partitions");
			BUG();
		}

		/*
		 * Unregister the remainder MTDs. Note that we are *not*
		 * destroying these MTDs -- we're just removing from the
		 * globally-visible list. There's no need for anyone to see
		 * these.
		 */

		del_mtd_device(chip_0_remainder_mtd);
		del_mtd_device(medium_remainder_mtd);

		/* Concatenate the remainders and register the result. */

		concatenate[0] = chip_0_remainder_mtd;
		concatenate[1] = medium_remainder_mtd;

		mil->general_use_mtd = mtd_concat_create(concatenate,
							2, general_use_name);

		add_mtd_device(mil->general_use_mtd);

#endif

	} else {
		dev_err(dev, "Boot area count greater than two is "
							"unimplemented.\n");
		return -ENXIO;
	}

	/* Return success. */

	return 0;

}

/**
 * mil_boot_areas_exit() - Shuts down boot areas.
 *
 * @this:  Per-device data.
 */
static void mil_boot_areas_exit(struct gpmi_nfc_data *this)
{
	struct boot_rom_geometry  *rom = &this->rom_geometry;
	struct mil                *mil = &this->mil;
	struct mtd_info           *mtd = &mil->mtd;

	/* Check if we're protecting the boot areas.*/

	if (!rom->boot_area_count) {

		/*
		 * If control arrives here, we're not protecting the boot areas.
		 * That means we never created any boot area partitions, and the
		 * general use MTD is just the main MTD.
		 */

		mil->general_use_mtd = 0;

		return;

	}

	/*
	 * If control arrives here, we're protecting the boot areas.
	 *
	 * Start by checking if there is more than one boot area. If so, then
	 * we both partitioned the medium and then concatenated some of the
	 * partitions to form the general use MTD. The first step is to get rid
	 * of the concatenation.
	 */

	#if defined(CONFIG_MTD_PARTITIONS) && defined(CONFIG_MTD_CONCAT)
		if (rom->boot_area_count > 1) {
				del_mtd_device(mil->general_use_mtd);
				mtd_concat_destroy(mil->general_use_mtd);
		}
	#endif

	/*
	 * At this point, we're left only with the partitions of the main MTD.
	 * Delete them.
	 */

	#if defined(CONFIG_MTD_PARTITIONS)
		del_mtd_partitions(mtd);
	#endif

	/* The general use MTD no longer exists. */

	mil->general_use_mtd = 0;

}

/**
 * mil_construct_ubi_partitions() - Constructs partitions for UBI.
 *
 * MTD uses a 64-bit value to express the size of MTDs, but UBI is still using
 * a 32-bit value. For this reason, UBI can't work on top of an MTD with size
 * greater than 2GiB. In this function, we examine the general use MTD and, if
 * it's larger than 2GiB, we construct a set of partitions for that MTD such
 * that none are too large for UBI to comprehend.
 *
 * @this:  Per-device data.
 */
static void mil_construct_ubi_partitions(struct gpmi_nfc_data *this)
{
#if defined(CONFIG_MTD_PARTITIONS)
	struct device                  *dev   =  this->dev;
	struct mil                     *mil   = &this->mil;
	unsigned int                   partition_count;
	struct mtd_partition           *partitions;
	unsigned int                   name_size;
	char                           *names;
	unsigned int                   memory_block_size;
	unsigned int                   i;

	static const char              *name_prefix = "gpmi-nfc-ubi-";

	/*
	 * If the general use MTD isn't larger than 2GiB, we have nothing to do.
	 */

	if (mil->general_use_mtd->size <= SZ_2G)
		return;

	/*
	 * If control arrives here, the general use MTD is larger than 2GiB. We
	 * need to split it up into some number of partitions. Find out how many
	 * 2GiB partitions we'll be creating.
	 */

	partition_count = mil->general_use_mtd->size >> 31;

	/*
	 * If the MTD size doesn't evenly divide by 2GiB, we'll need another
	 * partition to hold the extra.
	 */

	if (mil->general_use_mtd->size & ((1 << 30) - 1))
		partition_count++;

	/*
	 * We're going to allocate a single memory block to contain all the
	 * partition structures and their names. Calculate how large it must be.
	 */

	name_size = strlen(name_prefix) + 4;

	memory_block_size = (sizeof(*partitions) + name_size) * partition_count;

	/*
	 * Attempt to allocate the block.
	 */

	partitions = kzalloc(memory_block_size, GFP_KERNEL);

	if (!partitions) {
		dev_err(dev, "Could not allocate memory for UBI partitions.\n");
		return;
	}

	names = (char *)(partitions + partition_count);

	/* Loop over partitions, filling in the details. */

	for (i = 0; i < partition_count; i++) {

		partitions[i].name   = names;
		partitions[i].size   = SZ_2G;
		partitions[i].offset = MTDPART_OFS_NXTBLK;

		sprintf(names, "%s%u", name_prefix, i);
		names += name_size;

	}

	/* Adjust the last partition to take up the remainder. */

	partitions[i - 1].size = MTDPART_SIZ_FULL;

	/* Record everything in the device data structure.  */

	mil->partitions           = partitions;
	mil->partition_count      = partition_count;
	mil->ubi_partition_memory = partitions;

#endif
}

/**
 * mil_partitions_init() - Initializes partitions.
 *
 * @this:  Per-device data.
 */
static int mil_partitions_init(struct gpmi_nfc_data *this)
{
	struct gpmi_nfc_platform_data  *pdata =  this->pdata;
	struct mil                     *mil   = &this->mil;
	struct mtd_info                *mtd   = &mil->mtd;
	int                            error;

	/*
	 * Set up the boot areas. When this function returns, if there has been
	 * no error, the boot area partitions (if any) will have been created
	 * and registered. Also, the general_use_mtd field will point to an MTD
	 * we can use.
	 */

	error = mil_boot_areas_init(this);

	if (error)
		return error;

	/*
	 * If we've been told to, register the MTD that represents the entire
	 * medium. Normally, we don't register the main MTD because we only want
	 * to expose the medium through the boot area partitions and the general
	 * use partition.
	 *
	 * We do this *after* setting up the boot areas because, for historical
	 * reasons, we like the lowest-numbered MTDs to be the boot areas.
	 */

	if (register_main_mtd) {
		pr_info("Registering the main MTD.\n");
		add_mtd_device(mtd);
	}

#if defined(CONFIG_MTD_PARTITIONS)

	/*
	 * If control arrives here, partitioning is available.
	 *
	 * There are three possible sets of partitions we might apply, in order
	 * of decreasing priority:
	 *
	 * 1) Partitions dynamically discovered from sources defined by the
	 *    platform. These can come from, for example, the command line or
	 *    a partition table.
	 *
	 * 2) Partitions attached to the platform data.
	 *
	 * 3) Partitions we generate to deal with limitations in UBI.
	 *
	 * Recall that the pointer to the general use MTD *may* just point to
	 * the main MTD.
	 */

	/*
	 * First, try to get partition information from the sources defined by
	 * the platform.
	 */

	if (pdata->partition_source_types)
		mil->partition_count =
			parse_mtd_partitions(mil->general_use_mtd,
						pdata->partition_source_types,
						&mil->partitions, 0);

	/*
	 * Check if we got anything. If not, then accept whatever partitions are
	 * attached to the platform data.
	 */

	if ((mil->partition_count <= 0) && (pdata->partitions)) {
		mil->partition_count = mil->partition_count;
		mil->partitions      = mil->partitions;
	}

	/*
	 * If we still don't have any partitions to apply, then we might want to
	 * apply some of our own, to account for UBI's limitations.
	 */

	if (!mil->partition_count)
		mil_construct_ubi_partitions(this);

	/* If we came up with any partitions, apply them. */

	if (mil->partition_count)
		add_mtd_partitions(mil->general_use_mtd,
					mil->partitions,
					mil->partition_count);

#endif

	/* Return success. */

	return 0;

}

/**
 * mil_partitions_exit() - Shuts down partitions.
 *
 * @this:  Per-device data.
 */
static void mil_partitions_exit(struct gpmi_nfc_data *this)
{
	struct mil       *mil   = &this->mil;
	struct mtd_info  *mtd   = &mil->mtd;

	/* Check if we applied any partitions to the general use MTD. */

	#if defined(CONFIG_MTD_PARTITIONS)

		if (mil->partition_count)
			del_mtd_partitions(mil->general_use_mtd);

		kfree(mil->ubi_partition_memory);

	#endif

	/*
	 * If we were told to register the MTD that represents the entire
	 * medium, unregister it now. Note that this does *not* "destroy" the
	 * MTD - it merely unregisters it. That's important because all our
	 * other MTDs depend on this one.
	 */

	if (register_main_mtd)
		del_mtd_device(mtd);

	/* Tear down the boot areas. */

	mil_boot_areas_exit(this);

}

/**
 * gpmi_nfc_mil_init() - Initializes the MTD Interface Layer.
 *
 * @this:  Per-device data.
 */
int gpmi_nfc_mil_init(struct gpmi_nfc_data *this)
{
	struct device                  *dev   =  this->dev;
	struct gpmi_nfc_platform_data  *pdata =  this->pdata;
	struct mil                     *mil   = &this->mil;
	struct mtd_info                *mtd   = &mil->mtd;
	struct nand_chip               *nand  = &mil->nand;
	static struct nand_ecclayout   fake_ecc_layout;
	int                            error = 0;

	/* Initialize MIL data. */

	mil->current_chip   = -1;
	mil->command_length =  0;

	mil->page_buffer_virt =  0;
	mil->page_buffer_phys = ~0;
	mil->page_buffer_size =  0;

	/* Initialize the MTD data structures. */

	mtd->priv  = nand;
	mtd->name  = "gpmi-nfc-main";
	mtd->owner = THIS_MODULE;
	nand->priv = this;

	/*
	 * Signal Control
	 */

	nand->cmd_ctrl = mil_cmd_ctrl;

	/*
	 * Chip Control
	 *
	 * We rely on the reference implementations of:
	 *     - cmdfunc
	 *     - waitfunc
	 */

	nand->dev_ready   = mil_dev_ready;
	nand->select_chip = mil_select_chip;

	/*
	 * Low-level I/O
	 *
	 * We don't support a 16-bit NAND Flash bus, so we don't implement
	 * read_word.
	 *
	 * We rely on the reference implentation of verify_buf.
	 */

	nand->read_byte = mil_read_byte;
	nand->read_buf  = mil_read_buf;
	nand->write_buf = mil_write_buf;

	/*
	 * ECC Control
	 *
	 * None of these functions are necessary for us:
	 *     - ecc.hwctl
	 *     - ecc.calculate
	 *     - ecc.correct
	 */

	/*
	 * ECC-aware I/O
	 *
	 * We rely on the reference implementations of:
	 *     - ecc.read_page_raw
	 *     - ecc.write_page_raw
	 */

	nand->ecc.read_page  = mil_ecc_read_page;
	nand->ecc.write_page = mil_ecc_write_page;

	/*
	 * High-level I/O
	 *
	 * We rely on the reference implementations of:
	 *     - write_page
	 *     - erase_cmd
	 */

	nand->ecc.read_oob  = mil_ecc_read_oob;
	nand->ecc.write_oob = mil_ecc_write_oob;

	/*
	 * Bad Block Management
	 *
	 * We rely on the reference implementations of:
	 *     - block_bad
	 *     - block_markbad
	 */

	nand->block_bad = mil_block_bad;
	nand->scan_bbt  = mil_scan_bbt;

	/*
	 * Error Recovery Functions
	 *
	 * We don't fill in the errstat function pointer because it's optional
	 * and we don't have a need for it.
	 */

	/*
	 * Set up NAND Flash options. Specifically:
	 *
	 *     - Disallow partial page writes.
	 */

	nand->options |= NAND_NO_SUBPAGE_WRITE;

	/*
	 * Tell the NAND Flash MTD system that we'll be handling ECC with our
	 * own hardware. It turns out that we still have to fill in the ECC size
	 * because the MTD code will divide by it -- even though it doesn't
	 * actually care.
	 */

	nand->ecc.mode = NAND_ECC_HW;
	nand->ecc.size = 1;

	/*
	 * Install a "fake" ECC layout.
	 *
	 * We'll be calling nand_scan() to do the final MTD setup. If we haven't
	 * already chosen an ECC layout, then nand_scan() will choose one based
	 * on the part geometry it discovers. Unfortunately, it doesn't make
	 * good choices. It would be best if we could install the correct ECC
	 * layout now, before we call nand_scan(). We can't do that because we
	 * don't know the medium geometry yet. Here, we install a "fake" ECC
	 * layout just to stop nand_scan() from trying to pick one for itself.
	 * Later, when we know the medium geometry, we'll install the correct
	 * one.
	 *
	 * Of course, this tactic depends critically on the MTD code not doing
	 * an I/O operation that depends on the ECC layout being sensible. This
	 * is in fact the case.
	 */

	memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout));

	nand->ecc.layout = &fake_ecc_layout;

	/* Allocate a command buffer. */

	mil->cmd_virt =
		dma_alloc_coherent(dev,
			MIL_COMMAND_BUFFER_SIZE, &mil->cmd_phys, GFP_DMA);

	if (!mil->cmd_virt)
		goto exit_cmd_allocation;

	/*
	 * Ask the NAND Flash system to scan for chips.
	 *
	 * This will fill in reference implementations for all the members of
	 * the MTD structures that we didn't set, and will make the medium fully
	 * usable.
	 */

	pr_info("Scanning for NAND Flash chips...\n");

	error = nand_scan(mtd, pdata->max_chip_count);

	if (error) {
		dev_err(dev, "Chip scan failed\n");
		goto exit_nand_scan;
	}

	/*
	 * Hook some operations at the MTD level. See the descriptions of the
	 * saved function pointer fields for details about why we hook these.
	 */

	mil->hooked_read_oob      = mtd->read_oob;
	mtd->read_oob             = mil_hook_read_oob;

	mil->hooked_write_oob     = mtd->write_oob;
	mtd->write_oob            = mil_hook_write_oob;

	mil->hooked_block_markbad = mtd->block_markbad;
	mtd->block_markbad        = mil_hook_block_markbad;

	/* Construct partitions as necessary. */

	error = mil_partitions_init(this);

	if (error)
		goto exit_partitions;

	/* Return success. */

	return 0;

	/* Control arrives here if something went wrong. */

exit_partitions:
	nand_release(&mil->mtd);
exit_nand_scan:
	dma_free_coherent(dev, MIL_COMMAND_BUFFER_SIZE,
						mil->cmd_virt, mil->cmd_phys);
	mil->cmd_virt =  0;
	mil->cmd_phys = ~0;
exit_cmd_allocation:

	return error;

}

/**
 * gpmi_nfc_mil_exit() - Shuts down the MTD Interface Layer.
 *
 * @this:  Per-device data.
 */
void gpmi_nfc_mil_exit(struct gpmi_nfc_data *this)
{
	struct device  *dev = this->dev;
	struct mil     *mil = &this->mil;

	/* Shut down partitions as necessary. */

	mil_partitions_exit(this);

	/* Get MTD to let go of our MTD. */

	nand_release(&mil->mtd);

	/* Free the page buffer, if it's been allocated. */

	if (mil->page_buffer_virt)
		dma_free_coherent(dev, mil->page_buffer_size,
				mil->page_buffer_virt, mil->page_buffer_phys);

	mil->page_buffer_size =  0;
	mil->page_buffer_virt =  0;
	mil->page_buffer_phys = ~0;

	/* Free the command buffer, if it's been allocated. */

	if (mil->cmd_virt)
		dma_free_coherent(dev, MIL_COMMAND_BUFFER_SIZE,
						mil->cmd_virt, mil->cmd_phys);

	mil->cmd_virt =  0;
	mil->cmd_phys = ~0;

}
