/*
 *  ------------------------------------------------------------------------
 *  stm_afm_nand.c STMicroelectronics Advanced Flex Mode NAND Flash driver
 *                 for SoC's with the Hamming NAND Controller.
 *  ------------------------------------------------------------------------
 *
 *  Copyright (c) 2010 STMicroelectronics Limited
 *  Author: Angus Clark <Angus.Clark@st.com>
 *
 *  ------------------------------------------------------------------------
 *  May be copied or modified under the terms of the GNU General Public
 *  License Version 2.0 only.  See linux/COPYING for more information.
 *  ------------------------------------------------------------------------
 *
 */

#include <linux/io.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/err.h>
#include <linux/dma-mapping.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/clk.h>
#include <linux/stm/platform.h>
#include <linux/stm/nand.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

#include "stm_nand_ecc.h"
#include "stm_nandc_regs.h"

#define NAME	"stm-nand-afm"


#ifdef CONFIG_MTD_PARTITIONS
#include <linux/mtd/partitions.h>
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
/*
 * Support for STM boot-mode ECC: The STM NAND Boot Controller uses a different
 * ECC scheme to the AFM controller.  In order to support boot-mode ECC data, we
 * maintian two sets of ECC parameters and switch depending on which partition
 * we are about to read from.
 */
struct ecc_params {
	/* nand_chip params */
	struct nand_ecc_ctrl	ecc_ctrl;
	int			subpagesize;

	/* mtd_info params */
	u_int32_t		subpage_sft;
};

static void afm_select_eccparams(struct mtd_info *mtd, loff_t offs);
static void afm_write_page_boot(struct mtd_info *mtd,
				struct nand_chip *chip,
				const uint8_t *buf);
static int afm_read_page_boot(struct mtd_info *mtd,
			      struct nand_chip *chip,
			      uint8_t *buf, int page);

/* Module parameter for specifying name of boot partition */
static char *nbootpart;
module_param(nbootpart, charp, 0000);
MODULE_PARM_DESC(nbootpart, "MTD name of NAND boot-mode ECC partition");

#else
#define afm_select_eccparams(x, y) do {} while (0);
#endif /* CONFIG_STM_NAND_AFM_BOOTMODESUPPORT */


/* External functions */

/* AFM 32-byte program block */
struct afm_prog {
	uint8_t instr[16];
	uint32_t addr_reg;
	uint32_t extra_reg;
	uint8_t cmds[4];
	uint32_t seq_cfg;
} __attribute__((__packed__));

/* NAND device connected to STM NAND Controller */
struct stm_nand_afm_device {
	struct nand_chip	chip;
	struct mtd_info		mtd;

	int			csn;
	struct stm_nand_timing_data *timing_data;

	struct device		*dev;

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
	unsigned long		boot_start;
	unsigned long		boot_end;
	struct ecc_params	ecc_boot;
	struct ecc_params	ecc_afm;
#endif

#ifdef CONFIG_MTD_PARTITIONS
	int			nr_parts;
	struct mtd_partition	*parts;
#endif

};

/* STM NAND AFM Controller  */
struct stm_nand_afm_controller {
	void __iomem		*base;
	void __iomem		*fifo_cached;
	unsigned long		fifo_phys;

	/* resources */
	struct device		*dev;
	unsigned int		map_base;
	unsigned int		map_size;
	unsigned int		irq;

	spinlock_t		lock; /* save/restore IRQ flags */

	int			current_csn;

	/* access control */
	struct nand_hw_control	hwcontrol;

	/* irq completions */
	struct completion	rbn_completed;
	struct completion	seq_completed;

	/* emulate MTD nand_base.c implementation */
	uint32_t		status;
	uint32_t		col;
	uint32_t		page;

	/* devices connected to controller */
	struct stm_nand_afm_device *devices[0];
};

#define afm_writereg(val, reg)	iowrite32(val, afm->base + (reg))
#define afm_readreg(reg)	ioread32(afm->base + (reg))

static struct stm_nand_afm_controller *mtd_to_afm(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd->priv;
	struct nand_hw_control *controller = chip->controller;
	struct stm_nand_afm_controller *afm;

	afm = container_of(controller, struct stm_nand_afm_controller,
			    hwcontrol);
	return afm;
}


/*
 * Define some template AFM programs
 */
#define AFM_INSTR(cmd, op)	((cmd) | ((op) << 4))

/* AFM Prog: Block Erase */
struct afm_prog afm_prog_erase = {
	.cmds = {
		NAND_CMD_ERASE1,
		NAND_CMD_ERASE1,
		NAND_CMD_ERASE2,
		NAND_CMD_STATUS
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_ADDR,	0),
		AFM_INSTR(AFM_ADDR,	1),
		AFM_INSTR(AFM_CMD,	2),
		AFM_INSTR(AFM_CMD,	3),
		AFM_INSTR(AFM_CHECK,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO,
};

/* AFM Prog: Block Erase, extra address cycle */
struct afm_prog afm_prog_erase_ext = {
	.cmds = {
		NAND_CMD_ERASE1,
		NAND_CMD_ERASE1,
		NAND_CMD_ERASE2,
		NAND_CMD_STATUS
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_ADDR,	0),
		AFM_INSTR(AFM_ADDR,	1),
		AFM_INSTR(AFM_ADDR,	2),
		AFM_INSTR(AFM_CMD,	2),
		AFM_INSTR(AFM_CMD,	3),
		AFM_INSTR(AFM_CHECK,	0),
		AFM_INSTR(AFM_STOP,	0)
		},
	.seq_cfg = AFM_SEQ_CFG_GO,
};

/* AFM Prog: Read OOB [SmallPage] */
struct afm_prog afm_prog_read_oob_sp = {
	.cmds = {
		NAND_CMD_READOOB
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO,
};

/* AFM Prog: Read OOB [LargePage] */
struct afm_prog afm_prog_read_oob_lp = {
	.cmds = {
		NAND_CMD_READ0,
		NAND_CMD_READSTART
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_DATA,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO,
};

/* AFM Prog: Read Raw Page and OOB Data [SmallPage]
 *
 * Note, "SPARE3" would normally be used to read the OOB data.  However, SPARE3
 * has been observed to cause problems with EMI Arbitration resulting in system
 * deadlock.  To avoid such problems, we use the DATA0 command here.  This will
 * have the effect of over-reading 48 bytes from the end of OOB area but should
 * be benign on current NAND devices.
 */
struct afm_prog afm_prog_read_raw_sp = {
	.cmds = {
		NAND_CMD_READ0
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO,
};

/* AFM Prog: Read Raw Page and OOB Data [LargePage] */
struct afm_prog afm_prog_read_raw_lp = {
	.cmds = {
		NAND_CMD_READ0,
		NAND_CMD_READSTART
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO,
};

/* AFM Prog: Write Raw Page [SmallPage]
 *           OOB written with FLEX mode.
 */
struct afm_prog afm_prog_write_raw_sp_a = {
	.cmds = {
		NAND_CMD_SEQIN,
		NAND_CMD_READ0,
		NAND_CMD_PAGEPROG,
		NAND_CMD_STATUS,
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_CMD,	2),
		AFM_INSTR(AFM_CMD,	3),
		AFM_INSTR(AFM_CHECK,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO | AFM_SEQ_CFG_DIR_WRITE,
};

/* AFM Prog: Write Raw Page and OOB Data [LargePage] */
struct afm_prog afm_prog_write_raw_lp = {
	.cmds = {
		NAND_CMD_SEQIN,
		NAND_CMD_PAGEPROG,
		NAND_CMD_STATUS,
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_DATA,	0),
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_CMD,	2),
		AFM_INSTR(AFM_CHECK,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO | AFM_SEQ_CFG_DIR_WRITE,
};

/* AFM Prog: Write Page and OOB Data, with ECC support [SmallPage]
 *
 * Note, the AFM command, "SPARE3", would normally be used to write the OOB
 * data, with ECC inserted on the fly by the NAND controller.  However, the
 * SPARE3 command has been observed to cause EMI Arbitration issues resulting in
 * a bus stall and system deadlock.  To avoid this problem, we switch to FLEX
 * mode to write the OOB data, extracting and inserting the ECC bytes from the
 * NAND ECC checkcode regiter (see afm_write_page_ecc_sp())
 */
struct afm_prog afm_prog_write_ecc_sp_a = {
	.cmds = {
		NAND_CMD_SEQIN,
		NAND_CMD_READ0,
		NAND_CMD_PAGEPROG,
		NAND_CMD_STATUS,
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	1),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO | AFM_SEQ_CFG_DIR_WRITE,
};

/* AFM Prog: Write Page and OOB Data, with ECC support [LargePage] */
struct afm_prog afm_prog_write_ecc_lp = {
	.cmds = {
		NAND_CMD_SEQIN,
		NAND_CMD_PAGEPROG,
		NAND_CMD_STATUS,
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	2),
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_CMD,	2),
		AFM_INSTR(AFM_CHECK,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO | AFM_SEQ_CFG_DIR_WRITE,
};

/* AFM Prog: Write OOB Data [LargePage] */
struct afm_prog afm_prog_write_oob_lp = {
	.cmds = {
		NAND_CMD_SEQIN,
		NAND_CMD_PAGEPROG,
		NAND_CMD_STATUS
	},
	.instr = {
		AFM_INSTR(AFM_CMD,	0),
		AFM_INSTR(AFM_DATA,	0),
		AFM_INSTR(AFM_CMD,	1),
		AFM_INSTR(AFM_CMD,	2),
		AFM_INSTR(AFM_CHECK,	0),
		AFM_INSTR(AFM_STOP,	0)
	},
	.seq_cfg = AFM_SEQ_CFG_GO | AFM_SEQ_CFG_DIR_WRITE,
};


/*
 * STMicroeclectronics H/W ECC layouts
 *
 * AFM4 ECC:      512-byte data records, 3 bytes H/W ECC, 1 byte S/W ECC, 3
 *                bytes marker ({'A', 'F', 'M'})
 * Boot-mode ECC: 128-byte data records, 3 bytes ECC + 1 byte marker ('B')
 *
 */
static struct nand_ecclayout afm_oob_16 = {
	.eccbytes = 7,
	.eccpos = {
		/* { HW_ECC0, HW_ECC1, HW_ECC2, 'A', 'F', 'M', SW_ECC } */
		0, 1, 2, 3, 4, 5, 6},
	.oobfree = {
		{.offset = 7,
		 . length = 9},
	}
};

static struct nand_ecclayout afm_oob_64 = {
	.eccbytes = 28,
	.eccpos = {
		/* { HW_ECC0, HW_ECC1, HW_ECC2, 'A', 'F', 'M', SW_ECC } */
		0,   1,  2,  3,  4,  5,  6,	/* Record 0 */
		16, 17, 18, 19, 20, 21, 22,	/* Record 1 */
		32, 33, 34, 35, 36, 37, 38,	/* Record 2 */
		48, 49, 50, 51, 52, 53, 54,	/* Record 3 */
	},
	.oobfree = {
		{.offset = 7,
		 .length = 9
		},
		{.offset = 23,
		 .length = 9
		},
		{.offset = 39,
		 .length = 9
		},
		{.offset = 55,
		 .length = 9
		},
	}
};

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
static struct nand_ecclayout boot_oob_16 = {
	.eccbytes = 16,
	.eccpos = {
		0, 1, 2, 3,	/* Record 0: ECC0, ECC1, ECC2, 'B' */
		4, 5, 6, 7,	/* Record 1: ECC0, ECC1, ECC2, 'B' */
		8, 9, 10, 11,	/*                  ...            */
		12, 13, 14, 15
	},
	.oobfree = {{0, 0} },	/* No free space in OOB */
};

static struct nand_ecclayout boot_oob_64 = {
	.eccbytes = 64,
	.eccpos = {
		0, 1, 2, 3,	/* Record 0: ECC0, ECC1, ECC2, B */
		4, 5, 6, 7,	/* Record 1: ECC0, ECC1, ECC2, B */
		8,  9, 10, 11,	/*                 ...           */
		12, 13, 14, 15,
		16, 17, 18, 19,
		20, 21, 22, 23,
		24, 25, 26, 27,
		28, 29, 30, 31,
		32, 33, 34, 35,
		36, 37, 38, 39,
		40, 41, 42, 43,
		44, 45, 46, 47,
		48, 49, 50, 51,
		52, 53, 54, 55,
		56, 57, 58, 59,
		60, 61, 62, 63
	},
	.oobfree = {{0, 0} },	/* No free OOB bytes */
};
#endif

/* Pattern descriptors for scanning bad-block scanning - add support for AFM ECC
 * scheme */
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr bbt_scan_sp = {

	.options = (NAND_BBT_SCAN2NDPAGE | NAND_BBT_SCANSTMAFMECC),
	.offs = 5,
	.len = 1,
	.pattern = scan_ff_pattern
};
static struct nand_bbt_descr bbt_scan_lp = {
	.options = (NAND_BBT_SCAN2NDPAGE | NAND_BBT_SCANSTMAFMECC),
	.offs = 0,
	.len = 2,
	.pattern = scan_ff_pattern
};


/*
 * AFM Interrupts
 */

#define AFM_IRQ			(1 << 0)
#define AFM_IRQ_RBN		(1 << 2)
#define AFM_IRQ_DATA_DREQ	(1 << 3)
#define AFM_IRQ_SEQ_DREQ	(1 << 4)
#define AFM_IRQ_CHECK		(1 << 5)
#define AFM_IRQ_ECC_FIX		(1 << 6)

static void afm_enable_interrupts(struct stm_nand_afm_controller *afm,
				  uint32_t irqs)
{
	uint32_t reg;

	reg = afm_readreg(EMINAND_INTERRUPT_ENABLE);
	reg |= irqs;
	afm_writereg(reg, EMINAND_INTERRUPT_ENABLE);

}

static void afm_disable_interrupts(struct stm_nand_afm_controller *afm,
				   uint32_t irqs)
{
	uint32_t reg;

	reg = afm_readreg(EMINAND_INTERRUPT_ENABLE);
	reg &= ~irqs;
	afm_writereg(reg, EMINAND_INTERRUPT_ENABLE);
}

static irqreturn_t afm_irq_handler(int irq, void *dev)
{
	struct stm_nand_afm_controller *afm =
		(struct stm_nand_afm_controller *)dev;
	unsigned int irq_status;

	irq_status = afm_readreg(EMINAND_INTERRUPT_STATUS);

	/* RBn */
	if (irq_status & 0x4) {
		afm_writereg(0x04, EMINAND_INTERRUPT_CLEAR);
		complete(&afm->rbn_completed);
	}

	/* ECC_fix */
	if (irq_status & 0x40)
		afm_writereg(0x10, EMINAND_INTERRUPT_CLEAR);

	/* Seq_req */
	if (irq_status & AFM_IRQ_SEQ_DREQ) {
		afm_writereg(0x40, EMINAND_INTERRUPT_CLEAR);
		complete(&afm->seq_completed);
	}

	return IRQ_HANDLED;
}

/*
 * AFM Initialisation
 */

/* AFM set generic config register */
static void afm_generic_config(struct stm_nand_afm_controller *afm,
			       uint32_t busw, uint32_t page_size,
			       uint32_t chip_size)
{
	uint32_t reg;

	reg = 0x00;
	if ((busw & NAND_BUSWIDTH_16) == 0)
		reg |= 0x1 << 16;		/* data_8_not_16      */

	if (page_size > 512) {
		/* large page */
		reg |= 0x1 << 17;		/* page size	      */
		if (chip_size > (128 << 20))
			reg |= 0x1 << 18;	/* extra addr cycle   */
	} else if (chip_size > (32 << 20)) {
		/* small page */
		reg |= 0x1 << 18;		/* extra addr cycle   */

	}

	dev_dbg(afm->dev, "setting AFM_GENERIC_CONFIG_REG = 0x%08x\n", reg);

	afm_writereg(reg, EMINAND_AFM_GENERIC_CONFIG_REG);
}

/* AFM configure timing parameters */
static void afm_set_timings(struct stm_nand_afm_controller *afm,
			    struct stm_nand_timing_data *tm)
{
	uint32_t n;
	uint32_t reg;

	struct clk *emi_clk;
	uint32_t emi_t_ns;

	/* Timings set in terms of EMI clock... */
	emi_clk = clk_get(NULL, "emi_clk");

	if (!emi_clk || IS_ERR(emi_clk)) {
		printk(KERN_WARNING NAME ": Failed to find EMI clock. "
		       "Using default 100MHz.\n");
		emi_t_ns = 10;
	} else {
		emi_t_ns = 1000000000UL / clk_get_rate(emi_clk);
	}

	/* CONTROL_TIMING */
	n = (tm->sig_setup + emi_t_ns - 1)/emi_t_ns;
	reg = (n & 0xff) << 0;

	n = (tm->sig_hold + emi_t_ns - 1)/emi_t_ns;
	reg |= (n & 0xff) << 8;

	n = (tm->CE_deassert + emi_t_ns - 1)/emi_t_ns;
	reg |= (n & 0xff) << 16;

	n = (tm->WE_to_RBn + emi_t_ns - 1)/emi_t_ns;
	reg |= (n & 0xff) << 24;

	dev_dbg(afm->dev, "setting CONTROL_TIMING = 0x%08x\n", reg);
	afm_writereg(reg, EMINAND_CONTROL_TIMING);

	/* WEN_TIMING */
	n = (tm->wr_on + emi_t_ns - 1)/emi_t_ns;
	reg = (n & 0xff) << 0;

	n = (tm->wr_off + emi_t_ns - 1)/emi_t_ns;
	reg |= (n & 0xff) << 8;

	dev_dbg(afm->dev, "setting WEN_TIMING = 0x%08x\n", reg);
	afm_writereg(reg, EMINAND_WEN_TIMING);

	/* REN_TIMING */
	n = (tm->rd_on + emi_t_ns - 1)/emi_t_ns;
	reg = (n & 0xff) << 0;

	n = (tm->rd_off + emi_t_ns - 1)/emi_t_ns;
	reg |= (n & 0xff) << 8;

	dev_dbg(afm->dev, "setting REN_TIMING = 0x%08x\n", reg);
	afm_writereg(reg, EMINAND_REN_TIMING);
}

/* Initialise the AFM NAND controller */
static struct stm_nand_afm_controller * __init
afm_init_controller(struct platform_device *pdev)
{
	struct stm_plat_nand_flex_data *pdata = pdev->dev.platform_data;
	struct stm_nand_afm_controller *afm;
	struct resource *resource;
	int err = 0;

	afm = kzalloc(sizeof(struct stm_nand_afm_controller) +
		      (sizeof(struct stm_nand_afm_device *) * pdata->nr_banks),
		       GFP_KERNEL);
	if (!afm) {
		dev_err(&pdev->dev, "failed to allocate afm controller data\n");
		err = -ENOMEM;
		goto err0;
	}

	afm->dev = &pdev->dev;

	/* Request IO Memory */
	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!resource) {
		dev_err(&pdev->dev, "failed to find IOMEM resource\n");
		err = -ENOENT;
		goto err1;
	}
	afm->map_base = resource->start;
	afm->map_size = resource->end - resource->start + 1;

	if (!request_mem_region(afm->map_base, afm->map_size, pdev->name)) {
		dev_err(&pdev->dev, "request memory region failed [0x%08x]\n",
			afm->map_base);
		err = -EBUSY;
		goto err1;
	}

	/* Map base address */
	afm->base = ioremap_nocache(afm->map_base, afm->map_size);
	if (!afm->base) {
		dev_err(&pdev->dev, "base ioremap failed [0x%08x]\n",
			afm->map_base);
		err = -ENXIO;
		goto err2;
	}

#ifdef CONFIG_STM_NAND_AFM_CACHED
	/* Setup cached mapping to the AFM data FIFO */
	afm->fifo_phys = (afm->map_base + EMINAND_AFM_DATA_FIFO);
#ifndef CONFIG_32BIT
	/* 29-bit uses 'Area 7' address.  [Should this be done in ioremap?] */
	afm->fifo_phys &= 0x1fffffff;
#endif
	afm->fifo_cached = ioremap_cache(afm->fifo_phys, 512);
	if (!afm->fifo_cached) {
		dev_err(&pdev->dev, "fifo ioremap failed [0x%08x]\n",
			afm->map_base + EMINAND_AFM_DATA_FIFO);
		err = -ENXIO;
		goto err3;
	}

	spin_lock_init(&afm->lock);
#endif

	/* Setup IRQ handler */
	resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (!resource) {
		dev_err(&pdev->dev, "failed to find IRQ resource\n");
		err = -ENOENT;
		goto err4;
	}

	afm->irq = resource->start;
	if (request_irq(afm->irq, afm_irq_handler, IRQF_DISABLED,
			dev_name(&pdev->dev), afm)) {
		dev_err(&pdev->dev, "irq request failed\n");
		err = -EBUSY;
		goto err4;
	}

	/* Initialise 'controller' structure */
	spin_lock_init(&afm->hwcontrol.lock);
	init_waitqueue_head(&afm->hwcontrol.wq);

	init_completion(&afm->rbn_completed);
	init_completion(&afm->seq_completed);
	afm->current_csn = -1;

	/* Stop AFM Controller, in case it's still running! */
	afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
	memset(afm->base + EMINAND_AFM_SEQUENCE_REG_1, 0, 32);

	/* Reset AFM Controller */
	afm_writereg((0x1 << 3), EMINAND_FLEXMODE_CONFIG);
	udelay(1);
	afm_writereg(0x00, EMINAND_FLEXMODE_CONFIG);

	/* Disable boot_not_flex */
	afm_writereg(0x00000000, EMINAND_BOOTBANK_CONFIG);

	/* Set Controller to AFM */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);

	/* Enable Interrupts: individual interrupts enabled when needed! */
	afm_writereg(0x0000007C, EMINAND_INTERRUPT_CLEAR);
	afm_writereg(0x00000001, EMINAND_INTERRUPT_EDGECONFIG);
	afm_writereg(AFM_IRQ, EMINAND_INTERRUPT_ENABLE);

	/* Success :-) */
	return afm;


	/* Error path */
 err4:
#ifdef CONFIG_STM_NAND_AFM_CACHED
	iounmap(afm->fifo_cached);
 err3:
#endif
	iounmap(afm->base);
 err2:
	release_mem_region(afm->map_base, afm->map_size);
 err1:
	kfree(afm);
 err0:
	return ERR_PTR(err);

}

static void __devexit afm_exit_controller(struct platform_device *pdev)
{
	struct stm_nand_afm_controller *afm = platform_get_drvdata(pdev);

	free_irq(afm->irq, afm);
#ifdef CONFIG_STM_NAND_AFM_CACHED
	iounmap(afm->fifo_cached);
#endif
	iounmap(afm->base);
	release_mem_region(afm->map_base, afm->map_size);
	kfree(afm);
}

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
static void afm_setup_eccparams(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd->priv;
	struct stm_nand_afm_device *data = chip->priv;

	/* Take a copy of ECC AFM params, as set up during nand_scan() */
	data->ecc_afm.ecc_ctrl = chip->ecc;
	data->ecc_afm.subpagesize = chip->subpagesize;
	data->ecc_afm.subpage_sft = mtd->subpage_sft;

	/* Set ECC BOOT params */
	data->ecc_boot.ecc_ctrl = data->ecc_afm.ecc_ctrl;
	data->ecc_boot.ecc_ctrl.write_page = afm_write_page_boot;
	data->ecc_boot.ecc_ctrl.read_page = afm_read_page_boot;
	data->ecc_boot.ecc_ctrl.size = 128;
	data->ecc_boot.ecc_ctrl.bytes = 4;

	if (mtd->oobsize == 16)
		data->ecc_boot.ecc_ctrl.layout = &boot_oob_16;
	else
		data->ecc_boot.ecc_ctrl.layout = &boot_oob_64;

	data->ecc_boot.ecc_ctrl.layout->oobavail = 0;
	data->ecc_boot.ecc_ctrl.steps = mtd->writesize /
		data->ecc_boot.ecc_ctrl.size;

	if (data->ecc_boot.ecc_ctrl.steps * data->ecc_boot.ecc_ctrl.size !=
	    mtd->writesize) {
		dev_err(data->dev, "invalid ECC parameters\n");
		BUG();
	}
	data->ecc_boot.ecc_ctrl.total = data->ecc_boot.ecc_ctrl.steps *
		data->ecc_boot.ecc_ctrl.bytes;

	/* Disable subpage writes */
	data->ecc_boot.subpage_sft = 0;
	data->ecc_boot.subpagesize = mtd->writesize;
}

/* Switch ECC parameters */
static void afm_set_eccparams(struct mtd_info *mtd, struct ecc_params *params)
{
	struct nand_chip *chip = mtd->priv;

	chip->ecc = params->ecc_ctrl;
	chip->subpagesize = params->subpagesize;

	mtd->oobavail = params->ecc_ctrl.layout->oobavail;
	mtd->subpage_sft = params->subpage_sft;
}

static void afm_select_eccparams(struct mtd_info *mtd, loff_t offs)
{
	struct nand_chip *chip = mtd->priv;
	struct stm_nand_afm_device *data = chip->priv;

	if (offs >= data->boot_start &&
	    offs < data->boot_end) {
		if (chip->ecc.layout != data->ecc_boot.ecc_ctrl.layout) {
			dev_dbg(data->dev, "switching to boot-mode ECC\n");
			afm_set_eccparams(mtd, &data->ecc_boot);
		}
	} else {
		if (chip->ecc.layout != data->ecc_afm.ecc_ctrl.layout) {
			dev_dbg(data->dev, "switching to AFM4 ECC\n");
			afm_set_eccparams(mtd, &data->ecc_afm);
		}
	}
}

/* Replicated from ../mtdpart.c: required here to get slave MTD offsets and
 * determine which ECC mode to use.
 */
struct mtd_part {
	struct mtd_info mtd;
	struct mtd_info *master;
	u_int32_t offset;
	int index;
	struct list_head list;
	int registered;
};

#define PART(x)  ((struct mtd_part *)(x))
#endif



/*
 * Internal helper-functions for MTD Interface callbacks
 */
static uint8_t *afm_transfer_oob(struct nand_chip *chip, uint8_t *oob,
				 struct mtd_oob_ops *ops, size_t len)
{
	switch (ops->mode) {

	case MTD_OOB_PLACE:
	case MTD_OOB_RAW:
		memcpy(oob, chip->oob_poi + ops->ooboffs, len);
		return oob + len;

	case MTD_OOB_AUTO: {
		struct nand_oobfree *free = chip->ecc.layout->oobfree;
		uint32_t boffs = 0, roffs = ops->ooboffs;
		size_t bytes = 0;

		for (; free->length && len; free++, len -= bytes) {
			/* Read request not from offset 0 ? */
			if (unlikely(roffs)) {
				if (roffs >= free->length) {
					roffs -= free->length;
					continue;
				}
				boffs = free->offset + roffs;
				bytes = min_t(size_t, len,
					      (free->length - roffs));
				roffs = 0;
			} else {
				bytes = min_t(size_t, len, free->length);
				boffs = free->offset;
			}
			memcpy(oob, chip->oob_poi + boffs, bytes);
			oob += bytes;
		}

		return oob;
	}
	default:
		BUG();
	}
	return NULL;
}

static int afm_do_read_oob(struct mtd_info *mtd, loff_t from,
			   struct mtd_oob_ops *ops)
{
	int page, realpage, chipnr, sndcmd = 1;
	struct nand_chip *chip = mtd->priv;
	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
	int readlen = ops->ooblen;
	int len;
	uint8_t *buf = ops->oobbuf;

	DEBUG(MTD_DEBUG_LEVEL3, "afm_do_read_oob: from = 0x%08Lx, len = %i\n",
	      (unsigned long long)from, readlen);


	if (ops->mode == MTD_OOB_AUTO)
		len = chip->ecc.layout->oobavail;
	else
		len = mtd->oobsize;

	if (unlikely(ops->ooboffs >= len)) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
			"Attempt to start read outside oob\n");
		return -EINVAL;
	}

	/* Do not allow reads past end of device */
	if (unlikely(from >= mtd->size ||
		     ops->ooboffs + readlen >
		     ((mtd->size >> chip->page_shift) -
		      (from >> chip->page_shift)) * len)) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
		      "Attempt read beyond end of device\n");
		return -EINVAL;
	}

	chipnr = (int)(from >> chip->chip_shift);
	chip->select_chip(mtd, chipnr);

	/* Shift to get page */
	realpage = (int)(from >> chip->page_shift);
	page = realpage & chip->pagemask;

	while (1) {
		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);

		len = min(len, readlen);
		buf = afm_transfer_oob(chip, buf, ops, len);

		if (!(chip->options & NAND_NO_READRDY)) {
			/*
			 * Apply delay or wait for ready/busy pin. Do this
			 * before the AUTOINCR check, so no problems arise if a
			 * chip which does auto increment is marked as
			 * NOAUTOINCR by the board driver.
			 */
			if (!chip->dev_ready)
				udelay(chip->chip_delay);
			else
				nand_wait_ready(mtd);
		}

		readlen -= len;
		if (!readlen)
			break;

		/* Increment page address */
		realpage++;

		page = realpage & chip->pagemask;
		/* Check, if we cross a chip boundary */
		if (!page) {
			chipnr++;
			chip->select_chip(mtd, -1);
			chip->select_chip(mtd, chipnr);
		}

		/* Check, if the chip supports auto page increment
		 * or if we have hit a block boundary.
		 */
		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
			sndcmd = 1;
	}

	ops->oobretlen = ops->ooblen;
	return 0;
}

static int afm_do_read_ops(struct mtd_info *mtd, loff_t from,
			   struct mtd_oob_ops *ops)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	int chipnr, page, realpage, col, bytes, aligned;
	struct nand_chip *chip = mtd->priv;
	struct mtd_ecc_stats stats;
	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
	int sndcmd = 1;
	int ret = 0;
	uint32_t readlen = ops->len;
	uint32_t oobreadlen = ops->ooblen;
	uint8_t *bufpoi, *oob, *buf;

	DEBUG(MTD_DEBUG_LEVEL3, "afm_do_read_ops: from = 0x%08Lx, len = %i\n",
	      (unsigned long long)from, readlen);

	stats = mtd->ecc_stats;

	chipnr = (int)(from >> chip->chip_shift);
	chip->select_chip(mtd, chipnr);

	realpage = (int)(from >> chip->page_shift);	/* actual page no.  */
	page = realpage & chip->pagemask;		/* within-chip page */

	col = (int)(from & (mtd->writesize - 1));	/* col within page  */

	buf = ops->datbuf;			/* pointer to data buf */
	oob = ops->oobbuf;			/* pointer to oob buf	*/

	while (1) {
		/* #bytes in next transfer */
		bytes = min(mtd->writesize - col, readlen);
		/* tranfer aligned to page? */
		aligned = (bytes == mtd->writesize);

		/* Add test for for alignment */
		if ((uint32_t)buf & 0x3)
			aligned = 0;

		/* Is the current page in the buffer ? */
		if (realpage != chip->pagebuf || oob) {
			bufpoi = aligned ? buf : chip->buffers->databuf;

			afm->page = page;
			afm->col = 0;
			/* Now read the page into the buffer, without ECC if
			 * MTD_OOB_RAW */
			if (unlikely(ops->mode == MTD_OOB_RAW))
				ret = chip->ecc.read_page_raw(mtd, chip,
							      bufpoi, page);
			else
				ret = chip->ecc.read_page(mtd, chip,
							  bufpoi, page);
			if (ret < 0)
				break;

			/* Transfer not aligned data */
			if (!aligned) {
				chip->pagebuf = realpage;
				memcpy(buf, chip->buffers->databuf + col,
				       bytes);
			}

			buf += bytes;

			/* Done page data, now look at OOB... */
			if (unlikely(oob)) {
				/* Raw mode does data:oob:data:oob */
				if (ops->mode != MTD_OOB_RAW) {
					int toread = min(oobreadlen,
						chip->ecc.layout->oobavail);
					if (toread) {
						oob = afm_transfer_oob(chip,
								       oob,
								       ops,
								       toread);
						oobreadlen -= toread;
					}
				} else
					buf = afm_transfer_oob(chip,
							       buf, ops,
							       mtd->oobsize);
			}
		} else {
			memcpy(buf, chip->buffers->databuf + col, bytes);
			buf += bytes;
		}

		readlen -= bytes;

		if (!readlen)
			break;

		/* For subsequent reads align to page boundary. */
		col = 0;
		/* Increment page address */
		realpage++;

		page = realpage & chip->pagemask;
		/* Check, if we cross a chip boundary */
		if (!page) {
			chipnr++;
			chip->select_chip(mtd, -1);
			chip->select_chip(mtd, chipnr);
		}

		/* Check, if the chip supports auto page increment
		 * or if we have hit a block boundary.
		 */
		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
			sndcmd = 1;
	}

	ops->retlen = ops->len - (size_t) readlen;
	if (oob)
		ops->oobretlen = ops->ooblen - oobreadlen;

	if (ret)
		return ret;

	if (mtd->ecc_stats.failed - stats.failed)
		return -EBADMSG;

	return  mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;

}

static int afm_check_wp(struct mtd_info *mtd)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	uint32_t status;

	/* Switch to Flex Mode */
	afm_writereg(0x00000001, EMINAND_FLEXMODE_CONFIG);

	/* Read status register */
	afm_writereg(FLX_CMD(NAND_CMD_STATUS), EMINAND_FLEX_COMMAND_REG);
	status = afm_readreg(EMINAND_FLEX_DATA);

	/* Switch back to AFM */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);

	return (status & NAND_STATUS_WP) ? 0 : 1;
}

static uint8_t *afm_fill_oob(struct nand_chip *chip, uint8_t *oob,
			     struct mtd_oob_ops *ops)
{
	size_t len = ops->ooblen;

	switch (ops->mode) {

	case MTD_OOB_PLACE:
	case MTD_OOB_RAW:
		memcpy(chip->oob_poi + ops->ooboffs, oob, len);
		return oob + len;

	case MTD_OOB_AUTO: {
		struct nand_oobfree *free = chip->ecc.layout->oobfree;
		uint32_t boffs = 0, woffs = ops->ooboffs;
		size_t bytes = 0;

		for (; free->length && len; free++, len -= bytes) {
			/* Write request not from offset 0 ? */
			if (unlikely(woffs)) {
				if (woffs >= free->length) {
					woffs -= free->length;
					continue;
				}
				boffs = free->offset + woffs;
				bytes = min_t(size_t, len,
					      (free->length - woffs));
				woffs = 0;
			} else {
				bytes = min_t(size_t, len, free->length);
				boffs = free->offset;
			}
			memcpy(chip->oob_poi + boffs, oob, bytes);
			oob += bytes;
		}
		return oob;
	}
	default:
		BUG();
	}
	return NULL;
}

static int afm_do_write_oob(struct mtd_info *mtd, loff_t to,
			    struct mtd_oob_ops *ops)
{
	int chipnr, page, status, len;
	struct nand_chip *chip = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
	      (unsigned int)to, (int)ops->ooblen);

	if (ops->mode == MTD_OOB_AUTO)
		len = chip->ecc.layout->oobavail;
	else
		len = mtd->oobsize;

	/* Do not allow write past end of page */
	if ((ops->ooboffs + ops->ooblen) > len) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
		      "Attempt to write past end of page\n");
		return -EINVAL;
	}

	if (unlikely(ops->ooboffs >= len)) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
			"Attempt to start write outside oob\n");
		return -EINVAL;
	}

	/* Do not allow reads past end of device */
	if (unlikely(to >= mtd->size ||
		     ops->ooboffs + ops->ooblen >
			((mtd->size >> chip->page_shift) -
			 (to >> chip->page_shift)) * len)) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
			"Attempt write beyond end of device\n");
		return -EINVAL;
	}

	chipnr = (int)(to >> chip->chip_shift);
	chip->select_chip(mtd, chipnr);

	/* Shift to get page */
	page = (int)(to >> chip->page_shift);

	/* Check, if it is write protected */
	if (afm_check_wp(mtd))
		return -EROFS;

	/* Invalidate the page cache, if we write to the cached page */
	if (page == chip->pagebuf)
		chip->pagebuf = -1;

	memset(chip->oob_poi, 0xff, mtd->oobsize);
	afm_fill_oob(chip, ops->oobbuf, ops);
	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
	memset(chip->oob_poi, 0xff, mtd->oobsize);

	if (status)
		return status;

	ops->oobretlen = ops->ooblen;

	return 0;
}

static int afm_write_page(struct mtd_info *mtd, struct nand_chip *chip,
			  const uint8_t *buf, int page, int cached, int raw)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	afm->page = page;
	afm->col = 0;

	if (unlikely(raw))
		chip->ecc.write_page_raw(mtd, chip, buf);
	else
		chip->ecc.write_page(mtd, chip, buf);

	/* Check status information */
	if (afm->status & NAND_STATUS_FAIL)
		return -EIO;

	return 0;
}

#define NOTALIGNED(x)	((x & (chip->subpagesize - 1)) != 0)
static int afm_do_write_ops(struct mtd_info *mtd, loff_t to,
			     struct mtd_oob_ops *ops)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	int chipnr, realpage, page, blockmask, column;
	struct nand_chip *chip = mtd->priv;
	uint32_t writelen = ops->len;
	uint8_t *oob = ops->oobbuf;
	uint8_t *buf = ops->datbuf;
	int ret, subpage;

	ops->retlen = 0;
	if (!writelen)
		return 0;

	/* reject writes, which are not page aligned */
	if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
		dev_err(afm->dev, "attempt to write not page aligned data\n");
		return -EINVAL;
	}

	column = to & (mtd->writesize - 1);
	subpage = column || (writelen & (mtd->writesize - 1));

	if (subpage && oob)
		return -EINVAL;

	chipnr = (int)(to >> chip->chip_shift);
	chip->select_chip(mtd, chipnr);

	/* Check, if it is write protected */
	if (afm_check_wp(mtd))
		return -EIO;

	realpage = (int)(to >> chip->page_shift);
	page = realpage & chip->pagemask;
	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;

	/* Invalidate the page cache, when we write to the cached page */
	if (to <= (chip->pagebuf << chip->page_shift) &&
	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
		chip->pagebuf = -1;

	/* If we're not given explicit OOB data, let it be 0xFF */
	if (likely(!oob))
		memset(chip->oob_poi, 0xff, mtd->oobsize);

	while (1) {
		int bytes = mtd->writesize;
		int cached = writelen > bytes && page != blockmask;
		uint8_t *wbuf = buf;

		/* Partial page write ? */
		/* TODO: change alignment constraints for DMA transfer! */
		if (unlikely(column || writelen < (mtd->writesize - 1) ||
			     ((uint32_t)wbuf & 31))) {
			cached = 0;
			bytes = min_t(int, bytes - column, (int) writelen);
			chip->pagebuf = -1;
			memset(chip->buffers->databuf, 0xff, mtd->writesize);
			memcpy(&chip->buffers->databuf[column], buf, bytes);
			wbuf = chip->buffers->databuf;
		}

		if (unlikely(oob))
			oob = afm_fill_oob(chip, oob, ops);

		ret = chip->write_page(mtd, chip, wbuf, page, cached,
				       (ops->mode == MTD_OOB_RAW));
		if (ret)
			break;

		writelen -= bytes;
		if (!writelen)
			break;

		column = 0;
		buf += bytes;
		realpage++;

		page = realpage & chip->pagemask;
		/* Check, if we cross a chip boundary */
		if (!page) {
			chipnr++;
			chip->select_chip(mtd, -1);
			chip->select_chip(mtd, chipnr);
		}
	}

	ops->retlen = ops->len - writelen;
	if (unlikely(oob))
		ops->oobretlen = ops->ooblen;
	return ret;
}


/*
 * MTD Interface callbacks (replacing equivalent functions in nand_base.c)
 */


/* MTD Interface - erase block(s) */
static int afm_erase(struct mtd_info *mtd, struct erase_info *instr)
{
	return nand_erase_nand(mtd, instr, 0);
}

/* MTD Interface - Check if block at offset is bad */
static int afm_block_isbad(struct mtd_info *mtd, loff_t offs)
{
	struct nand_chip *chip = mtd->priv;

	/* Check for invalid offset */
	if (offs > mtd->size)
		return -EINVAL;

	/* We should always have a memory-resident BBT */
	BUG_ON(!chip->bbt);

	return nand_isbad_bbt(mtd, offs, 0);
}

/* MTD Interface - Mark block at the given offset as bad */
static int afm_block_markbad(struct mtd_info *mtd, loff_t offs)
{
	struct nand_chip *chip = mtd->priv;
	uint8_t buf[16];
	int block, ret;

	/* Is is already marked bad? */
	ret = afm_block_isbad(mtd, offs);
	if (ret)
		return (ret > 0) ? 0 : ret;

	/* Update memory-resident BBT */
	BUG_ON(!chip->bbt);
	block = (int)(offs >> chip->bbt_erase_shift);
	chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);

	/* Update non-volatile marker... */
	if (chip->options & NAND_USE_FLASH_BBT) {
		/* Update flash-resident BBT */
		ret = nand_update_bbt(mtd, offs);
	} else {
		/*
		 * Write the bad-block marker to OOB.  We also need to wipe the
		 * first 'AFM' marker (just in case it survived the preceding
		 * failed ERASE) to prevent subsequent on-boot scans from
		 * recognising an AFM block, instead of a marked-bad block.
		 */
		memset(buf, 0, 16);
		nand_get_device(chip, mtd, FL_WRITING);
		offs += mtd->oobsize;
		chip->ops.mode = MTD_OOB_PLACE;
		chip->ops.len = 16;
		chip->ops.ooblen = 16;
		chip->ops.datbuf = NULL;
		chip->ops.oobbuf = buf;
		chip->ops.ooboffs = chip->badblockpos & ~0x01;

		ret = afm_do_write_oob(mtd, offs, &chip->ops);
		nand_release_device(mtd);
	}
	if (ret == 0)
		mtd->ecc_stats.badblocks++;

	return ret;
}

/* MTD Interface - Read chunk of page data */
static int afm_read(struct mtd_info *mtd, loff_t from, size_t len,
		    size_t *retlen, uint8_t *buf)
{
	struct nand_chip *chip = mtd->priv;
	int ret;

	/* Do not allow reads past end of device */
	if ((from + len) > mtd->size)
		return -EINVAL;
	if (!len)
		return 0;

	nand_get_device(chip, mtd, FL_READING);
	afm_select_eccparams(mtd, from);

	chip->ops.len = len;
	chip->ops.datbuf = buf;
	chip->ops.oobbuf = NULL;

	ret = afm_do_read_ops(mtd, from, &chip->ops);

	*retlen = chip->ops.retlen;

	nand_release_device(mtd);

	return ret;
}

/* MTD Interface - read page data and/or OOB */
static int afm_read_oob(struct mtd_info *mtd, loff_t from,
			struct mtd_oob_ops *ops)
{
	struct nand_chip *chip = mtd->priv;
	int ret = -ENOTSUPP;

	/* Do not allow reads past end of device */
	if (ops->datbuf && (from + ops->len) > mtd->size) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
		      "Attempt read beyond end of device\n");
		return -EINVAL;
	}

	nand_get_device(chip, mtd, FL_READING);
	afm_select_eccparams(mtd, from);

	switch (ops->mode) {
	case MTD_OOB_PLACE:
	case MTD_OOB_AUTO:
	case MTD_OOB_RAW:
		break;

	default:
		goto out;
	}

	if (!ops->datbuf)
		ret = afm_do_read_oob(mtd, from, ops);
	else
		ret = afm_do_read_ops(mtd, from, ops);

 out:
	nand_release_device(mtd);
	return ret;
}

/* MTD Interface - write page data */
static int afm_write(struct mtd_info *mtd, loff_t to, size_t len,
			  size_t *retlen, const uint8_t *buf)
{
	struct nand_chip *chip = mtd->priv;
	int ret;


	/* Do not allow reads past end of device */
	if ((to + len) > mtd->size)
		return -EINVAL;
	if (!len)
		return 0;

	nand_get_device(chip, mtd, FL_WRITING);
	afm_select_eccparams(mtd, to);

	chip->ops.len = len;
	chip->ops.datbuf = (uint8_t *)buf;
	chip->ops.oobbuf = NULL;

	ret = afm_do_write_ops(mtd, to, &chip->ops);

	*retlen = chip->ops.retlen;

	nand_release_device(mtd);

	return ret;
}

/* MTD Interface - write page data and/or OOB */
static int afm_write_oob(struct mtd_info *mtd, loff_t to,
			  struct mtd_oob_ops *ops)
{
	struct nand_chip *chip = mtd->priv;
	int ret = -ENOTSUPP;

	/* Do not allow writes past end of device */
	if (ops->datbuf && (to + ops->len) > mtd->size) {
		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
		      "Attempt read beyond end of device\n");
		return -EINVAL;
	}

	nand_get_device(chip, mtd, FL_WRITING);
	afm_select_eccparams(mtd, to);

	switch (ops->mode) {
	case MTD_OOB_PLACE:
	case MTD_OOB_AUTO:
	case MTD_OOB_RAW:
		break;

	default:
		goto out;
	}

	if (!ops->datbuf)
		ret = afm_do_write_oob(mtd, to, ops);
	else
		ret = afm_do_write_ops(mtd, to, ops);

 out:
	nand_release_device(mtd);
	return ret;
}

/* MTD Interface - Sync (just wait for chip ready, then release) */
static void afm_sync(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n");

	/* Grab the lock and see if the device is available */
	nand_get_device(chip, mtd, FL_SYNCING);
	/* Release it and go back */
	nand_release_device(mtd);
}

/* MTD Interface - Suspend (wait for chip ready, then suspend) */
static int afm_suspend(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd->priv;

	return nand_get_device(chip, mtd, FL_PM_SUSPENDED);
}

/* MTD Interface - Resume (release device) */
static void afm_resume(struct mtd_info *mtd)
{
	struct nand_chip *chip = mtd->priv;

	if (chip->state == FL_PM_SUSPENDED)
		nand_release_device(mtd);
	else
		printk(KERN_ERR "afm_resume() called for a chip which is not "
		       "in suspended state\n");
}

/*
 * AFM data transfer routines
 */

#ifdef CONFIG_STM_NAND_AFM_CACHED
static void afm_read_buf_cached(struct mtd_info *mtd, uint8_t *buf, int len)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	unsigned long irq_flags;

	int lenaligned = len & ~(L1_CACHE_BYTES-1);
	int lenleft = len & (L1_CACHE_BYTES-1);

	while (lenaligned > 0) {
		spin_lock_irqsave(&(afm->lock), irq_flags);
		invalidate_ioremap_region(afm->fifo_phys, afm->fifo_cached,
					  0, L1_CACHE_BYTES);
		memcpy_fromio(buf, afm->fifo_cached, L1_CACHE_BYTES);
		spin_unlock_irqrestore(&(afm->lock), irq_flags);

		buf += L1_CACHE_BYTES;
		lenaligned -= L1_CACHE_BYTES;
	}

#ifdef CONFIG_STM_L2_CACHE
	/* If L2 cache is enabled, we must ensure the cacheline is evicted prior
	 * to non-cached accesses..
	 */
	invalidate_ioremap_region(afm->fifo_phys, afm->fifo_cached,
				  0, L1_CACHE_BYTES);
#endif
	/* Mop up any remaining bytes */
	while (lenleft > 0) {
		*buf++ = readb(afm->base + EMINAND_AFM_DATA_FIFO);
		lenleft--;
	}
}
#else
/* Default read buffer command */
static void afm_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	readsl(afm->base + EMINAND_AFM_DATA_FIFO, buf, len/4);
}
#endif

/* Default write buffer command */
static void afm_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	writesl(afm->base + EMINAND_AFM_DATA_FIFO, buf, len/4);
}


/*
 * AFM low-level chip operations
 */

/* AFM: Block Erase */
static void afm_erase_cmd(struct mtd_info *mtd, int page)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	struct afm_prog	*prog;
	struct nand_chip *chip = mtd->priv;
	uint32_t reg;
	int ret;

	afm->status = 0;

	/* Initialise Seq interrupts */
	INIT_COMPLETION(afm->seq_completed);
	afm_enable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	/* Select AFM program */
	if ((mtd->writesize == 512 && chip->chipsize > (32 << 20)) ||
	    (mtd->writesize == 2048 && chip->chipsize > (128 << 20)))
		/* For 'large' chips, we need an extra address cycle */
		prog = &afm_prog_erase_ext;
	else
		prog = &afm_prog_erase;

	/* Set page address */
	prog->extra_reg	= page;

	/* Copy program to controller, and start sequence */
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/* Wait for sequence to finish */
	ret = wait_for_completion_timeout(&afm->seq_completed, 2*HZ);
	if (!ret) {
		dev_warn(afm->dev, "sequence timeout, force exit!\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm->status = NAND_STATUS_FAIL;
		afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
		return;
	}

	/* Get status */
	reg = afm_readreg(EMINAND_AFM_SEQUENCE_STATUS_REG);
	afm->status =  NAND_STATUS_READY | ((reg & 0x60) >> 5);

	/* Disable interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
}

static int afm_correct_ecc(struct mtd_info *mtd, unsigned char *buf,
			   unsigned char *read_ecc, unsigned char *calc_ecc)
{
	/* No error */
	if ((read_ecc[0] ^ calc_ecc[0]) == 0 &&
	    (read_ecc[1] ^ calc_ecc[1]) == 0 &&
	    (read_ecc[2] ^ calc_ecc[2]) == 0)
		return 0;

	/* Special test for freshly erased page */
	if (read_ecc[0] == 0xff && calc_ecc[0] == 0x00 &&
	    read_ecc[1] == 0xff && calc_ecc[1] == 0x00 &&
	    read_ecc[2] == 0xff && calc_ecc[2] == 0x00)
		return 0;

	/* Use nand_ecc.c:nand_correct_data() function */
	return nand_correct_data(mtd, buf, read_ecc, calc_ecc);

}

/* AFM: Read Page and OOB Data with ECC */
static int afm_read_page_ecc(struct mtd_info *mtd, struct nand_chip *chip,
			     uint8_t *buf, int page)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	int i, j;
	int eccsize = chip->ecc.size;
	int eccbytes = chip->ecc.bytes;
	int eccsteps = chip->ecc.steps;

	uint8_t *p = buf;
	uint8_t *ecc_code = chip->buffers->ecccode;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	uint8_t *e1, *e2, t;

	/* Read raw page+oob data */
	chip->ecc.read_page_raw(mtd, chip, buf, page);

	/* Recover ECC from OOB: H/W ECC + S/W LP16,17 from device */
	e1 = ecc_code;
	for (i = 0; i < chip->ecc.total; i++)
		e1[i] = chip->oob_poi[eccpos[i]];

	/* Generate linux-style 'ecc_code' and 'ecc_calc' */
	e1 = ecc_code;
	e2 = ecc_calc;
	p = buf;
	for (i = 0; i < eccsteps; i++) {
		uint32_t ecc_afm;
		uint8_t lp1617;
		uint32_t chkcode_offs;

		/* Get H/W ECC */
		chkcode_offs = (eccsteps == 1) ? (3 << 2) : (i << 2);
		ecc_afm = afm_readreg(EMINAND_AFM_ECC_CHECKCODE_REG_0 +
				      chkcode_offs);

		/* Extract ecc_code and ecc_calc */
		for (j = 0; j < 3; j++) {
			e2[j] = (unsigned char)(ecc_afm & 0xff);
			ecc_afm >>= 8;
		}

		/* Swap e[0] and e[1] */
		t = e1[0]; e1[0] = e1[1]; e1[1] = t;
		t = e2[0]; e2[0] = e2[1]; e2[1] = t;

		/* Generate S/W LP1617 ecc_calc */
		lp1617 = stm_afm_lp1617(p);

		/* Copy S/W LP1617 bits to standard location */
		e1[2] = (e1[2] & 0xfc) | (e1[6] & 0x3);
		e2[2] = (e2[2] & 0xfc) | (lp1617 & 0x3);

		/* Move on to next ECC block */
		e1 += eccbytes;
		e2 += eccbytes;
		p += eccsize;
	}

	/* Detect/Correct ECC errors */
	p = buf;
	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
		int stat;

		stat = afm_correct_ecc(mtd, p, &ecc_code[i], &ecc_calc[i]);

		if (stat == -1) {
			mtd->ecc_stats.failed++;
			printk(KERN_CONT "step %d, page %d]\n",
			       i / eccbytes, page);
		} else if (stat == 1) {
			printk(KERN_CONT "step %d, page = %d]\n",
			       i / eccbytes, page);
			mtd->ecc_stats.corrected++;
		}
	}

	return  0;
}

/* AFM: Read Raw Page and OOB Data */
static int afm_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
			     uint8_t *buf, int page)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	struct afm_prog	*prog;
	uint32_t reg;
	int ret;

	/* Select AFM program */
	prog = (mtd->writesize == 512) ?
		&afm_prog_read_raw_sp :
		&afm_prog_read_raw_lp;

	/* Initialise RBn interrupt */
	INIT_COMPLETION(afm->rbn_completed);
	afm_enable_interrupts(afm, AFM_IRQ_RBN);

	if (mtd->writesize == 512) {
		/* SmallPage: Reset ECC counters */
		reg = afm_readreg(EMINAND_FLEXMODE_CONFIG);
		reg |= (1 << 6);
		afm_writereg(reg, EMINAND_FLEXMODE_CONFIG);
	}

	/* Set page address */
	prog->addr_reg = afm->page << 8;

	/* Copy program to controller, and start sequence */
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/* Wait for data to become available */
	ret = wait_for_completion_timeout(&afm->rbn_completed, HZ/2);
	if (!ret) {
		dev_err(afm->dev, "RBn timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm_disable_interrupts(afm, AFM_IRQ_RBN);
		return 1;
	}
	/* Read page data and OOB (SmallPage: +48 bytes dummy data) */
	chip->read_buf(mtd, buf, mtd->writesize);
	chip->read_buf(mtd, chip->oob_poi, 64);

	/* Disable RBn interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_RBN);

	return 0;
}

/* AFM: Read OOB data */
static int afm_read_oob_chip(struct mtd_info *mtd, struct nand_chip *chip,
			     int page, int sndcmd)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct afm_prog	*prog;
	uint32_t reg;
	int ret;

	/* Select AFM program */
	prog = (mtd->writesize == 512) ?
		&afm_prog_read_oob_sp :
		&afm_prog_read_oob_lp;

	/* Initialise RBn interrupt */
	INIT_COMPLETION(afm->rbn_completed);
	afm_enable_interrupts(afm, AFM_IRQ_RBN);

	if (mtd->writesize == 512) {
		/* SmallPage: Reset ECC counters and set page address */
		reg = afm_readreg(EMINAND_FLEXMODE_CONFIG);
		reg |= (1 << 6);
		afm_writereg(reg, EMINAND_FLEXMODE_CONFIG);

		prog->addr_reg	= page << 8;
	} else {
		/* LargePage: Set page address */
		prog->addr_reg	= (page << 8) | (2048 >> 8);
	}

	/* Copy program to controller, and start sequence */
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/* Wait for data to become available */
	ret = wait_for_completion_timeout(&afm->rbn_completed, HZ/2);
	if (!ret) {
		dev_err(afm->dev, "RBn timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm_disable_interrupts(afm, AFM_IRQ_RBN);
		return 1;
	}

	/* Read OOB data to chip->oob_poi buffer */
	chip->read_buf(mtd, chip->oob_poi, 64);

	/* Disable RBn Interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_RBN);

	return 0;
}

/* AFM: Write Page and OOB Data, with ECC support [SmallPage] */
static void afm_write_page_ecc_sp(struct mtd_info *mtd,
				  struct nand_chip *chip,
				  const uint8_t *buf)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct afm_prog *prog = &afm_prog_write_ecc_sp_a;
	uint32_t ecc_afm;
	uint32_t reg;
	int ret;

	afm->status = 0;

	/* 1. Write page data to chip's page buffer */

	/*    Initialise Seq interrupt */
	INIT_COMPLETION(afm->seq_completed);
	afm_enable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	/*    Reset ECC counters */
	reg = afm_readreg(EMINAND_FLEXMODE_CONFIG);
	reg |= (1 << 6);
	afm_writereg(reg, EMINAND_FLEXMODE_CONFIG);

	/*    Set page address */
	prog->addr_reg	= afm->page << 8;

	/*    Copy program to controller, and start sequence */
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/*    Write page data */
	chip->write_buf(mtd, buf, mtd->writesize);

	/*    Wait for the sequence to terminate */
	ret = wait_for_completion_timeout(&afm->seq_completed, HZ/2);
	if (!ret) {
		dev_err(afm->dev, "seq timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm->status = NAND_STATUS_FAIL;
		afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
		return;
	}

	/*    Disable Seq interrupt */
	afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	/* 2. Write OOB data */
	/*    Populate OOB with ECC data  */
	ecc_afm = afm_readreg(EMINAND_AFM_ECC_CHECKCODE_REG_3);
	chip->oob_poi[0] = ecc_afm & 0xff; ecc_afm >>= 8;
	chip->oob_poi[1] = ecc_afm & 0xff; ecc_afm >>= 8;
	chip->oob_poi[2] = ecc_afm & 0xff;
	chip->oob_poi[3] = 'A';
	chip->oob_poi[4] = 'F';
	chip->oob_poi[5] = 'M';
	chip->oob_poi[6] = stm_afm_lp1617(buf);

	/*    Switch to FLEX mode for writing OOB */
	INIT_COMPLETION(afm->rbn_completed);
	afm_enable_interrupts(afm, AFM_IRQ_RBN);
	afm_writereg(0x00000001, EMINAND_FLEXMODE_CONFIG);

	/*    Write OOB data */
	afm_writereg(FLX_DATA_CFG_BEAT_4 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAWRITE_CONFIG);
	writesl(afm->base + EMINAND_FLEX_DATA, chip->oob_poi, 4);
	afm_writereg(FLX_DATA_CFG_BEAT_1 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAWRITE_CONFIG);

	/*    Issue page program command */
	afm_writereg(FLX_CMD(NAND_CMD_PAGEPROG), EMINAND_FLEX_COMMAND_REG);

	/*    Wait for page program operation to complete */
	ret = wait_for_completion_timeout(&afm->rbn_completed, HZ/2);
	if (!ret)
		dev_err(afm->dev, "RBn timeout, force exit\n");

	/*     Get status */
	afm_writereg(FLX_CMD(NAND_CMD_STATUS), EMINAND_FLEX_COMMAND_REG);
	afm->status = afm_readreg(EMINAND_FLEX_DATA);

	afm_disable_interrupts(afm, AFM_IRQ_RBN);

	/* 3. Switch back to AFM */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);
}


/* AFM: Write Page and OOB Data, with ECC support [LargePage]  */
static void afm_write_page_ecc_lp(struct mtd_info *mtd,
				  struct nand_chip *chip,
				  const uint8_t *buf)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	int eccsize = chip->ecc.size;
	int eccbytes = chip->ecc.bytes;
	int eccsteps = chip->ecc.steps;
	uint32_t *eccpos = chip->ecc.layout->eccpos;
	const uint8_t *p;

	struct afm_prog *prog = &afm_prog_write_ecc_lp;

	uint32_t reg;
	int ret;
	int i;

	afm->status = 0;

	/* Calc S/W ECC LP1617, insert into OOB area with 'AFM' signature */
	p = buf;
	for (i = 0; i < eccsteps; i++) {
		chip->oob_poi[eccpos[3+i*eccbytes]] = 'A';
		chip->oob_poi[eccpos[4+i*eccbytes]] = 'F';
		chip->oob_poi[eccpos[5+i*eccbytes]] = 'M';
		chip->oob_poi[eccpos[6+i*eccbytes]] = stm_afm_lp1617(p);
		p += eccsize;
	}

	/* Initialise Seq interrupt */
	INIT_COMPLETION(afm->seq_completed);
	afm_enable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	/* Reset ECC counters */
	reg = afm_readreg(EMINAND_FLEXMODE_CONFIG);
	reg |= (1 << 6);
	afm_writereg(reg, EMINAND_FLEXMODE_CONFIG);

	/* Set page address */
	prog->addr_reg	= afm->page << 8;

	/* Copy program to controller, and start sequence */
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/* Write page and oob data */
	chip->write_buf(mtd, buf, mtd->writesize);
	chip->write_buf(mtd, chip->oob_poi, 64);

	/* Wait for sequence to complete */
	ret = wait_for_completion_timeout(&afm->seq_completed, HZ/2);
	if (!ret) {
		dev_err(afm->dev, "seq timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm->status = NAND_STATUS_FAIL;
		afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
		return;
	}

	/* Get status */
	reg = afm_readreg(EMINAND_AFM_SEQUENCE_STATUS_REG);
	afm->status =  NAND_STATUS_READY | ((reg & 0x60) >> 5);

	/* Disable Seq interrupt */
	afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
}

/* AFM: Write Raw Page and OOB Data [LargePage] */
static void afm_write_page_raw_lp(struct mtd_info *mtd, struct nand_chip *chip,
				  const uint8_t *buf)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct afm_prog *prog = &afm_prog_write_raw_lp;
	uint32_t reg;
	int ret;

	afm->status = 0;

	/* Initialise Seq Interrupts */
	INIT_COMPLETION(afm->seq_completed);
	afm_enable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	/* Set page address */
	prog->addr_reg	= afm->page << 8;

	/* Copy program to controller, and start sequence */
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/* Write page and OOB data */
	chip->write_buf(mtd, buf, 2048);
	chip->write_buf(mtd, chip->oob_poi, 64);

	/* Wait for sequence to complete */
	ret = wait_for_completion_timeout(&afm->seq_completed, HZ/2);
	if (!ret) {
		dev_err(afm->dev, "seq timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm->status = NAND_STATUS_FAIL;
		afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
		return;
	}

	/* Get status */
	reg = afm_readreg(EMINAND_AFM_SEQUENCE_STATUS_REG);
	afm->status =  NAND_STATUS_READY | ((reg & 0x60) >> 5);

	/* Disable Seq Interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
}

/* AFM: Write Raw Page and OOB Data [SmallPage] */
static void afm_write_page_raw_sp(struct mtd_info *mtd, struct nand_chip *chip,
				  const uint8_t *buf)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct afm_prog *prog = &afm_prog_write_raw_sp_a;
	uint32_t reg;
	int ret;

	afm->status = 0;

	/* Enable sequence interrupts */
	INIT_COMPLETION(afm->seq_completed);
	afm_enable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	/* 1. Write page data to chip's page buffer */
	prog->addr_reg	= afm->page << 8;
	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	chip->write_buf(mtd, buf, mtd->writesize);

	ret = wait_for_completion_timeout(&afm->seq_completed, HZ/2);
	if (!ret) {
		dev_err(afm->dev, "seq timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm->status = NAND_STATUS_FAIL;
		afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
		return;
	}
	afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	reg = afm_readreg(EMINAND_AFM_SEQUENCE_STATUS_REG);
	if ((reg & 0x60) != 0) {
		dev_err(afm->dev, "error programming page data\n");
		afm->status = NAND_STATUS_FAIL;
		return;
	}

	/* 2. Switch to FLEX mode and write OOB data */
	INIT_COMPLETION(afm->rbn_completed);
	afm_enable_interrupts(afm, AFM_IRQ_RBN);
	afm_writereg(0x00000001, EMINAND_FLEXMODE_CONFIG);

	/*    Send OOB pointer operation */
	afm_writereg(FLX_CMD(NAND_CMD_READOOB),  EMINAND_FLEX_COMMAND_REG);

	/*    Send SEQIN command */
	afm_writereg(FLX_CMD(NAND_CMD_SEQIN), EMINAND_FLEX_COMMAND_REG);

	/*    Send page address */
	reg = afm->page << 8 | FLX_ADDR_REG_ADD8_VALID |
		FLX_ADDR_REG_CSN_STATUS;
	if (chip->chipsize > (32 << 20))
		reg |= FLX_ADDR_REG_BEAT_4;
	else
		reg |= FLX_ADDR_REG_BEAT_3;
	afm_writereg(reg, EMINAND_FLEX_ADDRESS_REG);

	/*    Send OOB data */
	afm_writereg(FLX_DATA_CFG_BEAT_4 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAWRITE_CONFIG);
	writesl(afm->base + EMINAND_FLEX_DATA, chip->oob_poi, 4);
	afm_writereg(FLX_DATA_CFG_BEAT_1 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAWRITE_CONFIG);

	/*    Send page program command */
	afm_writereg(FLX_CMD(NAND_CMD_PAGEPROG),  EMINAND_FLEX_COMMAND_REG);

	/* Wait for page program operation to complete */
	ret = wait_for_completion_timeout(&afm->rbn_completed, HZ/2);
	if (!ret)
		dev_err(afm->dev, "RBn timeout!\n");

	/*     Get status */
	afm_writereg(FLX_CMD(NAND_CMD_STATUS), EMINAND_FLEX_COMMAND_REG);
	afm->status = afm_readreg(EMINAND_FLEX_DATA);

	afm_disable_interrupts(afm, AFM_IRQ_RBN);

	/* 3. Switch back to AFM */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);
}

/* AFM: Write OOB Data [LargePage] */
static int afm_write_oob_chip_lp(struct mtd_info *mtd, struct nand_chip *chip,
				 int page)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct afm_prog *prog = &afm_prog_write_oob_lp;
	uint32_t reg;
	int ret;

	afm->status = 0;

	/* Enable sequence interrupts */
	INIT_COMPLETION(afm->seq_completed);
	afm_enable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	prog->addr_reg	= (page << 8) | (2048 >> 8);

	memcpy_toio(afm->base + EMINAND_AFM_SEQUENCE_REG_1, prog, 32);

	/* Write OOB */
	chip->write_buf(mtd, chip->oob_poi, 64);

	/* Wait for sequence to complete */
	ret = wait_for_completion_timeout(&afm->seq_completed, HZ);
	if (!ret) {
		dev_err(afm->dev, "seq timeout, force exit\n");
		afm_writereg(0x00000000, EMINAND_AFM_SEQUENCE_CONFIG_REG);
		afm->status = NAND_STATUS_FAIL;
		afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);
		return 1;
	}

	/* Get status */
	reg = afm_readreg(EMINAND_AFM_SEQUENCE_STATUS_REG);
	afm->status =  NAND_STATUS_READY | ((reg & 0x60) >> 5);

	/* Disable sequence interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_SEQ_DREQ);

	return afm->status & NAND_STATUS_FAIL ? -EIO : 0;
}

/* AFM: Write OOB Data [SmallPage] */
static int afm_write_oob_chip_sp(struct mtd_info *mtd, struct nand_chip *chip,
				 int page)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	int ret;
	uint32_t status;
	uint32_t reg;

	afm->status = 0;

	/* Initialise interrupts */
	INIT_COMPLETION(afm->rbn_completed);
	afm_enable_interrupts(afm, AFM_IRQ_RBN);

	/* Switch to Flex Mode */
	afm_writereg(0x00000001, EMINAND_FLEXMODE_CONFIG);

	/* Pointer Operation */
	afm_writereg(FLX_CMD(NAND_CMD_READOOB), EMINAND_FLEX_COMMAND_REG);

	/* Send SEQIN command */
	afm_writereg(FLX_CMD(NAND_CMD_SEQIN), EMINAND_FLEX_COMMAND_REG);

	/* Send Col/Page Addr */
	reg = (page << 8 |
	       FLX_ADDR_REG_RBN |
	       FLX_ADDR_REG_ADD8_VALID |
	       FLX_ADDR_REG_CSN_STATUS);

	if (chip->chipsize > (32 << 20))
		/* Need extra address cycle for 'large' devices */
		reg |= FLX_ADDR_REG_BEAT_4;
	else
		reg |= FLX_ADDR_REG_BEAT_3;
	afm_writereg(reg, EMINAND_FLEX_ADDRESS_REG);

	/* Write OOB data */
	afm_writereg(FLX_DATA_CFG_BEAT_4 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAWRITE_CONFIG);
	writesl(afm->base + EMINAND_FLEX_DATA, chip->oob_poi, 4);
	afm_writereg(FLX_DATA_CFG_BEAT_1 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAWRITE_CONFIG);

	/* Issue Page Program command */
	afm_writereg(FLX_CMD(NAND_CMD_PAGEPROG), EMINAND_FLEX_COMMAND_REG);

	/* Wait for page program operation to complete */
	ret = wait_for_completion_timeout(&afm->rbn_completed, HZ/2);
	if (!ret)
		dev_err(afm->dev, "RBn timeout\n");

	/* Get status */
	afm_writereg(FLX_CMD(NAND_CMD_STATUS), EMINAND_FLEX_COMMAND_REG);
	status = afm_readreg(EMINAND_FLEX_DATA);

	afm->status = 0xff & status;

	/* Switch back to AFM */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);

	/* Disable RBn interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_RBN);

	return status & NAND_STATUS_FAIL ? -EIO : 0;
}

/* Read the device electronic signature */
static int afm_read_sig(struct mtd_info *mtd,
		       int *maf_id, int *dev_id,
		       uint8_t *cellinfo, uint8_t *extid)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	uint32_t ret, reg;

	/* Enable RBn interrupts */
	INIT_COMPLETION(afm->rbn_completed);
	afm_enable_interrupts(afm, AFM_IRQ_RBN);

	/* Switch to Flex Mode */
	afm_writereg(0x00000001, EMINAND_FLEXMODE_CONFIG);

	/* Issue NAND reset */
	afm_writereg(FLX_CMD(NAND_CMD_RESET), EMINAND_FLEX_COMMAND_REG);

	/* Wait for reset to complete */
	ret = wait_for_completion_timeout(&afm->rbn_completed, HZ/2);
	if (!ret)
		dev_err(afm->dev, "RBn timeout\n");

	/* Read electronic signature */
	afm_writereg(FLX_CMD(NAND_CMD_READID), EMINAND_FLEX_COMMAND_REG);

	reg = (0x00 |
	       FLX_ADDR_REG_BEAT_1 |
	       FLX_ADDR_REG_RBN |
	       FLX_ADDR_REG_CSN_STATUS);
	afm_writereg(reg, EMINAND_FLEX_ADDRESS_REG);

	afm_writereg(FLX_DATA_CFG_BEAT_4 | FLX_DATA_CFG_CSN_STATUS,
		     EMINAND_FLEX_DATAREAD_CONFIG);
	reg = afm_readreg(EMINAND_FLEX_DATA);
	afm_writereg(FLX_DATA_CFG_BEAT_1 | FLX_DATA_CFG_CSN_STATUS,
		      EMINAND_FLEX_DATAREAD_CONFIG);

	/* Extract manufacturer and device ID */
	*maf_id = reg & 0xff;
	*dev_id = (reg >> 8) & 0xff;

	/* Newer devices have all the information in additional id bytes */
	*cellinfo = (reg >> 16) & 0xff;
	*extid = (reg >> 24) & 0xff;

	/* Switch back to AFM Mode */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);

	/* Disable RBn interrupts */
	afm_disable_interrupts(afm, AFM_IRQ_RBN);

	return 0;
}

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
/* For boot-mode, we use software ECC with AFM raw read/write commands */
static int boot_calc_ecc(struct mtd_info *mtd, const unsigned char *buf,
			 unsigned char *ecc)
{
	stm_ecc_gen(buf, ecc, ECC_128);
	ecc[3] = 'B';

	return 0;
}

static int boot_correct_ecc(struct mtd_info *mtd, unsigned char *buf,
			    unsigned char *read_ecc, unsigned char *calc_ecc)
{
	int status;

	status = stm_ecc_correct(buf, read_ecc, calc_ecc, ECC_128);

	/* convert to MTD-compatible status */
	if (status == E_NO_CHK)
		return 0;
	if (status == E_D1_CHK || status == E_C1_CHK)
		return 1;

	return -1;
}

static int afm_read_page_boot(struct mtd_info *mtd, struct nand_chip *chip,
			      uint8_t *buf, int page)
{
	int i, eccsize = chip->ecc.size;
	int eccbytes = chip->ecc.bytes;
	int eccsteps = chip->ecc.steps;
	uint8_t *p = buf;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	uint8_t *ecc_code = chip->buffers->ecccode;
	uint32_t *eccpos = chip->ecc.layout->eccpos;

	chip->ecc.read_page_raw(mtd, chip, buf, page);

	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
		boot_calc_ecc(mtd, p, &ecc_calc[i]);

	for (i = 0; i < chip->ecc.total; i++)
		ecc_code[i] = chip->oob_poi[eccpos[i]];

	eccsteps = chip->ecc.steps;
	p = buf;

	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
		int stat;

		stat = boot_correct_ecc(mtd, p, &ecc_code[i], &ecc_calc[i]);
		if (stat == -1) {
			printk(KERN_CONT "step %d, page %d]\n",
			       i / eccbytes, page);
			mtd->ecc_stats.failed++;
		} else if (stat == 1) {
			printk(KERN_CONT "step %d, page = %d]\n",
			       i / eccbytes, page);
			mtd->ecc_stats.corrected += stat;
		}
	}
	return 0;
}

static void afm_write_page_boot(struct mtd_info *mtd,
				struct nand_chip *chip,
				const uint8_t *buf)

{
	int i, eccsize = chip->ecc.size;
	int eccbytes = chip->ecc.bytes;
	int eccsteps = chip->ecc.steps;
	uint8_t *ecc_calc = chip->buffers->ecccalc;
	const uint8_t *p = buf;
	uint32_t *eccpos = chip->ecc.layout->eccpos;

	/* Software ecc calculation */
	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
		boot_calc_ecc(mtd, p, &ecc_calc[i]);

	for (i = 0; i < chip->ecc.total; i++)
		chip->oob_poi[eccpos[i]] = ecc_calc[i];

	chip->ecc.write_page_raw(mtd, chip, buf);

}
#endif /* CONFIG_STM_NAND_AFM_BOOTMODESUPPORT */


/* AFM : Select Chip
 * For now we only support 1 chip per 'stm_nand_afm_device' so chipnr will be 0
 * for select, -1 for deselect.
 */
static void afm_select_chip(struct mtd_info *mtd, int chipnr)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct nand_chip *chip = mtd->priv;
	struct stm_nand_afm_device *data = chip->priv;

	/* Deselect, do nothing */
	if (chipnr == -1) {
		return;
	} else if (chipnr == 0) {
		/* If same chip as last time, no need to change anything */
		if (data->csn == afm->current_csn)
			return;

		/* Set CSn on AFM controller */
		afm->current_csn = data->csn;
		afm_writereg(0x1 << data->csn, EMINAND_MUXCONTROL_REG);

		/* Set up timing parameters */
		afm_set_timings(afm, data->timing_data);
	} else {
		dev_err(afm->dev, "attempt to select chipnr = %d\n", chipnr);
	}

	return;
}

/* The low-level AFM chip operations wait internally, and set the status field.
 * All that is left to do here is return the status */
static int afm_wait(struct mtd_info *mtd, struct nand_chip *chip)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);

	return afm->status;
}

/*
 * afm_command() and afm_read_byte() are treated as special cases here.  We need
 * to support nand_base.c:nand_erase_nand() because this is called directly by
 * nand_bbt.c (why is it not a callback?).  However, nand_erase_nand() calls
 * nand_check_wp() which in turn calls chip->cmdfunc() and chip->read_byte() in
 * order to check the status register.  Since nand_check_wp() is the only
 * function that uses these callbacks, we can implement specialised versions
 * such that afm_command() is empty, and afm_read_byte() queries and returns the
 * status register.
 *
 * Need to track updates to nand_base.c to ensure these assumptions remain valid
 * in the future!
 */
static void afm_command(struct mtd_info *mtd, unsigned int command,
			 int column, int page_addr)
{

}

static uint8_t afm_read_byte(struct mtd_info *mtd)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	uint32_t reg;

	/* Switch to Flex Mode */
	afm_writereg(0x00000001, EMINAND_FLEXMODE_CONFIG);

	/* Write STATUS command (do not set FLX_CMD_REG_RBN!) */
	reg = (NAND_CMD_STATUS | FLX_CMD_REG_BEAT_1 | FLX_CMD_REG_CSN_STATUS);
	afm_writereg(reg, EMINAND_FLEX_COMMAND_REG);

	reg = afm_readreg(EMINAND_FLEX_DATA);

	/* Switch back to AFM */
	afm_writereg(0x00000002, EMINAND_FLEXMODE_CONFIG);

	return reg & 0xff;
}

static int afm_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
	printk(KERN_ERR NAME ": %s Unsupported callback", __func__);
	BUG();

	return 0;
}

static u16 afm_read_word(struct mtd_info *mtd)
{
	printk(KERN_ERR NAME ": %s Unsupported callback", __func__);
	BUG();

	return 0xff;
}

/*
 * AFM scan/probe NAND routines
 */

/* Set AFM generic call-backs (not chip-specific) */
static void afm_set_defaults(struct nand_chip *chip, int busw)
{
	chip->chip_delay = 0;
	chip->cmdfunc = afm_command;
	chip->waitfunc = afm_wait;
	chip->select_chip = afm_select_chip;
	chip->read_byte = afm_read_byte;
	chip->read_word = afm_read_word;
	chip->block_bad = NULL;
	chip->block_markbad = NULL;
	chip->write_buf = afm_write_buf;
	chip->verify_buf = afm_verify_buf;
	chip->scan_bbt = nand_default_bbt;
#ifdef CONFIG_STM_NAND_AFM_CACHED
	chip->read_buf = afm_read_buf_cached;
#else
	chip->read_buf = afm_read_buf;
#endif
}


/* Determine the NAND device paramters
 * [cf nand_base.c:nand_get_flash_type()] */
static struct nand_flash_dev *afm_get_flash_type(struct mtd_info *mtd,
						 struct nand_chip *chip,
						 int busw, int *maf_id)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct nand_flash_dev *type = NULL;
	int i, dev_id, maf_idx;
	uint8_t cellinfo;
	uint8_t extid;

	/* Select the device */
	chip->select_chip(mtd, 0);

	/* Read the electronic signature */
	afm_read_sig(mtd, maf_id, &dev_id, &cellinfo, &extid);

	/* Lookup device */
	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
		if (dev_id == nand_flash_ids[i].id) {
			type =  &nand_flash_ids[i];
			break;
		}
	}

	if (!type)
		return ERR_PTR(-ENODEV);

	if (!mtd->name)
		mtd->name = type->name;

	chip->chipsize = type->chipsize << 20;

	if (!type->pagesize) {
		/* New devices use the extended chip info... */
		chip->cellinfo = cellinfo;

		/* Calc pagesize */
		mtd->writesize = 1024 << (extid & 0x3);
		extid >>= 2;

		/* Calc oobsize */
		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
		extid >>= 2;

		/* Calc blocksize. Blocksize is multiples of 64KiB */
		mtd->erasesize = (64 * 1024) << (extid & 0x03);
		extid >>= 2;

		/* Get buswidth information */
		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
	} else {
		/* Old devices have chip data hardcoded in device id table */
		mtd->erasesize = type->erasesize;
		mtd->writesize = type->pagesize;
		mtd->oobsize = mtd->writesize / 32;
		busw = type->options & NAND_BUSWIDTH_16;
	}

	afm_generic_config(afm, chip->options & NAND_BUSWIDTH_16,
			   mtd->writesize, chip->chipsize);

	/* Try to identify manufacturer */
	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
		if (nand_manuf_ids[maf_idx].id == *maf_id)
			break;
	}

	/*
	 * Check, if buswidth is correct. Hardware drivers should set
	 * chip correct !
	 */
	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
		dev_info(afm->dev, "NAND device: Manufacturer ID:"
			 " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
			 dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
		dev_info(afm->dev, "NAND bus width %d instead %d bit\n",
			 (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
			 busw ? 16 : 8);
		return ERR_PTR(-EINVAL);
	}

	/* Calculate the address shift from the page size */
	chip->page_shift = ffs(mtd->writesize) - 1;

	/* Convert chipsize to number of pages per chip -1. */
	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;

	chip->bbt_erase_shift = ffs(mtd->erasesize) - 1;
	chip->phys_erase_shift = chip->bbt_erase_shift;

	chip->chip_shift = ffs(chip->chipsize) - 1;

	/* Set the bad block position [AAC: Now obsolete?] */
	chip->badblockpos = mtd->writesize > 512 ?
		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;

	/* Get chip options, preserve non chip based options */
	chip->options &= ~NAND_CHIPOPTIONS_MSK;
	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;

	/*
	 * Set chip as a default. Board drivers can override it, if necessary
	 */
	chip->options |= NAND_NO_AUTOINCR;

	/* Check if chip is a not a samsung device. Do not clear the
	 * options for chips which are not having an extended id.
	 */
	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;


	chip->erase_cmd = afm_erase_cmd;

	dev_info(afm->dev, "NAND device: Manufacturer ID:"
		 " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
		 nand_manuf_ids[maf_idx].name, type->name);

	return type;
}


/* Scan device, part 1 : Determine device paramters */
int afm_scan_ident(struct mtd_info *mtd, int maxchips)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	int busw, maf_id;
	struct nand_chip *chip = mtd->priv;
	struct nand_flash_dev *type = NULL;

	if (maxchips != 1) {
		dev_err(afm->dev, "Only chip per MTD device allowed\n");
		return 1;
	}

	busw = chip->options & NAND_BUSWIDTH_16;
	afm_set_defaults(chip, busw);

	type = afm_get_flash_type(mtd, chip, busw, &maf_id);

	if (IS_ERR(type)) {
		dev_err(afm->dev, "No NAND device found\n");
		chip->select_chip(mtd, -1);
		return PTR_ERR(type);
	}

	/* Not dealing with multichip support just yet! */
	chip->numchips = 1;
	mtd->size = chip->chipsize;

	return 0;

}

/* Scan device, part 2: Configure AFM according to device paramters */
static int afm_scan_tail(struct mtd_info *mtd)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	int i;
	struct nand_chip *chip = mtd->priv;

	int ret;

	if (!(chip->options & NAND_OWN_BUFFERS))
		chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
	if (!chip->buffers)
		return -ENOMEM;

	/* Set the internal oob buffer location, just after the page data */
	chip->oob_poi = chip->buffers->databuf + mtd->writesize;

	if (mtd->writesize == 512 && mtd->oobsize == 16) {
		chip->ecc.layout = &afm_oob_16;
		chip->badblock_pattern = &bbt_scan_sp;
	} else if (mtd->writesize == 2048 && mtd->oobsize == 64) {
		chip->ecc.layout = &afm_oob_64;
		chip->badblock_pattern = &bbt_scan_lp;
	} else {
		dev_err(afm->dev, "Unsupported chip type "
			"[pagesize = %d, oobsize = %d]\n",
			mtd->writesize, mtd->oobsize);
		return 1;
	}

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
	/* Handle boot-mode ECC when scanning for bad blocks */
	chip->badblock_pattern->options |= NAND_BBT_SCANSTMBOOTECC;
#endif

	/* Set ECC parameters and call-backs */
	chip->ecc.mode = NAND_ECC_HW;
	chip->ecc.size = 512;
	chip->ecc.bytes = 7;
	chip->write_page = afm_write_page;
	chip->ecc.read_page = afm_read_page_ecc;
	chip->ecc.read_page_raw = afm_read_page_raw;
	chip->ecc.read_oob = afm_read_oob_chip;
	if (mtd->writesize == 512) {
		chip->ecc.write_page = afm_write_page_ecc_sp;
		chip->ecc.write_page_raw = afm_write_page_raw_sp;
		chip->ecc.write_oob = afm_write_oob_chip_sp;
	} else {
		chip->ecc.write_page = afm_write_page_ecc_lp;
		chip->ecc.write_page_raw = afm_write_page_raw_lp;
		chip->ecc.write_oob = afm_write_oob_chip_lp;
	}
	chip->ecc.steps = mtd->writesize / chip->ecc.size;
	if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
		dev_err(afm->dev, "Invalid ecc parameters\n");
		return 1;
	}
	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
	chip->ecc.calculate = NULL;	/* Hard-coded in call-backs */
	chip->ecc.correct = NULL;

	/* Count the number of OOB bytes available for client data */
	chip->ecc.layout->oobavail = 0;
	for (i = 0; chip->ecc.layout->oobfree[i].length
		     && i < MTD_MAX_OOBFREE_ENTRIES; i++)
		chip->ecc.layout->oobavail +=
			chip->ecc.layout->oobfree[i].length;
	mtd->oobavail = chip->ecc.layout->oobavail;

	/* Disable subpage writes for now */
	mtd->subpage_sft = 0;
	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;

	/* Initialize state */
	chip->state = FL_READY;

	/* Invalidate the pagebuffer reference */
	chip->pagebuf = -1;

	/* Fill in remaining MTD driver data */
	mtd->type = MTD_NANDFLASH;
	mtd->flags = MTD_CAP_NANDFLASH;
	mtd->erase = afm_erase;  /* uses chip->erase_cmd */
	mtd->point = NULL;
	mtd->unpoint = NULL;
	mtd->read = afm_read;
	mtd->write = afm_write;
	mtd->read_oob = afm_read_oob;
	mtd->write_oob = afm_write_oob;
	mtd->sync = afm_sync;
	mtd->lock = NULL;
	mtd->unlock = NULL;
	mtd->suspend = afm_suspend;
	mtd->resume = afm_resume;
	mtd->block_isbad = afm_block_isbad;
	mtd->block_markbad = afm_block_markbad;

	/* Propagate ecc.layout to mtd_info */
	mtd->ecclayout = chip->ecc.layout;

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
	/* Setup ECC params, for AFM and BOOT operation */
	afm_setup_eccparams(mtd);
#endif

	/* Check, if we should skip the bad block table scan */
	if (chip->options & NAND_SKIP_BBTSCAN)
		return 0;

	/* Build bad block table */
	ret = chip->scan_bbt(mtd);

	return ret;
}

/* Scan for the NAND device */
int afm_scan(struct mtd_info *mtd, int maxchips)
{
	int ret;

	ret = afm_scan_ident(mtd, maxchips);
	if (!ret)
		ret = afm_scan_tail(mtd);


	return ret;
}

#ifdef CONFIG_STM_NAND_AFM_PBLBOOTBOUNDARY
/* The NAND_BLOCK_ZERO_REMAP_REG is not implemented on current versions of the
 * NAND controller.  We must therefore scan for the logical block 0 pattern.
 */
static int check_block_zero_pattern(uint8_t *buf)
{
	uint8_t i;
	uint8_t *b;

	for (i = 0, b = buf + 128; i < 64; i++, b++)
		if (*b != i)
			return 1;

	return 0;
}

static int pbl_boot_boundary(struct mtd_info *mtd, uint32_t *_boundary)
{
	struct stm_nand_afm_controller *afm = mtd_to_afm(mtd);
	struct nand_chip *chip = mtd->priv;
	struct stm_nand_afm_device *data = chip->priv;
	uint8_t *buf = chip->buffers->databuf;
	int block;
	int block_zero_phys = -1;
	int pages_per_block;
	uint32_t boundary_log = 0;
	uint32_t boundary_phys;

	/* Select boot-mode ECC */
	afm_select_eccparams(mtd, data->boot_start);

	/* Find logical block zero */
	pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
	afm->col = 0;
	afm->page = 0;
	for (block = 0; block < 512; block++) {
		if (nand_isbad_bbt(mtd, block << chip->phys_erase_shift, 0)
		    == 0) {
			afm_read_page_boot(mtd, chip, buf, afm->page);

			if (check_block_zero_pattern(buf) == 0) {
				block_zero_phys = block;
				boundary_log = *((uint32_t *)(buf + 0x0034));
				break;
			}
		}
		afm->page += pages_per_block;
	}

	/* Return if we didn't find logical block zero */
	if (block_zero_phys == -1)
		return 1;

	/* For bootmode_boundary, map logical offset to physical offset */
	boundary_phys = block_zero_phys << chip->phys_erase_shift;
	while (boundary_log >= mtd->erasesize) {
		boundary_phys += mtd->erasesize;
		if (!afm_block_isbad(mtd, boundary_phys))
			boundary_log -= mtd->erasesize;
	}
	boundary_phys += boundary_log;		/* Add residual offset */

	dev_info(afm->dev, "boot-mode boundary = 0x%08x (physical)\n",
		 boundary_phys);

	*_boundary = boundary_phys;

	return 0;
}
#endif


static struct stm_nand_afm_device * __init
afm_init_bank(struct stm_nand_afm_controller *afm,
	      struct stm_nand_bank_data *bank,
	      const char *name)
{
	struct stm_nand_afm_device *data;
	int err;

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
	struct mtd_info *slave;
	struct mtd_part *part;
	char *boot_part_name;
#ifdef CONFIG_STM_NAND_AFM_PBLBOOTBOUNDARY
	uint32_t boundary;
#endif
#endif

	/* Allocate memory for the driver structure (and zero it) */
	data = kzalloc(sizeof(struct stm_nand_afm_device), GFP_KERNEL);
	if (!data) {
		dev_err(afm->dev, "failed to allocate device structure\n");
		err = -ENOMEM;
		goto err1;
	}

	data->chip.priv = data;
	data->mtd.priv = &data->chip;
	data->mtd.owner = THIS_MODULE;
	data->dev = afm->dev;

	/* Use hwcontrol structure to manage access to AFM Controller */
	data->chip.controller = &afm->hwcontrol;
	data->chip.state = FL_READY;

	/* Assign more sensible name (default is string from nand_ids.c!) */
	data->mtd.name = name;
	data->csn = bank->csn;

	data->timing_data = bank->timing_data;

	data->chip.options = bank->options;
	data->chip.options |= NAND_NO_AUTOINCR;

	/* Scan to find existance of device */
	if (afm_scan(&data->mtd, 1)) {
		dev_err(afm->dev, "device scan failed\n");
		err = -ENXIO;
		goto err2;
	}

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
	/* Set name of boot partition */
	boot_part_name = nbootpart ? nbootpart :
		CONFIG_STM_NAND_AFM_BOOTPARTITION;
	dev_info(afm->dev, "using boot partition name [%s] (from %s)\n",
		 boot_part_name, nbootpart ? "command line" : "kernel config");
#endif

#ifdef CONFIG_MTD_PARTITIONS
	/* Try probing for MTD partitions */
	data->nr_parts = parse_mtd_partitions(&data->mtd,
					      part_probes,
					      &data->parts, 0);

	/* Try platform data */
	if (!data->nr_parts && bank->partitions) {
		data->parts = bank->partitions;
		data->nr_parts = bank->nr_partitions;
	}

	/* Add any partitions that were found */
	if (data->nr_parts) {
		err = add_mtd_partitions(&data->mtd, data->parts,
					 data->nr_parts);
		if (err)
			goto err3;

#ifdef CONFIG_STM_NAND_AFM_BOOTMODESUPPORT
		/* Update boot-mode slave partition */
		slave = get_mtd_partition_slave(&data->mtd, boot_part_name);
		if (slave) {
			dev_info(afm->dev, "found boot-mode partition "
				 "updating ECC paramters\n");

			part = PART(slave);
			data->boot_start = part->offset;
			data->boot_end = part->offset + slave->size;

#ifdef CONFIG_STM_NAND_AFM_PBLBOOTBOUNDARY
			/* Update 'boot_end' with value in PBL image */
			if (pbl_boot_boundary(&data->mtd,
					      &boundary) != 0) {
				dev_info(afm->dev, "failed to get boot-mode "
					 "boundary from PBL\n");
			} else {
				if (boundary < data->boot_start ||
				    boundary > data->boot_end) {
					dev_info(afm->dev,
						 "PBL boot-mode "
						 "boundary lies outside "
						 "boot partition\n");
				} else {
					dev_info(afm->dev,
						 "Updating boot-mode "
						 "ECC boundary from PBL");
					data->boot_end = boundary;
				}
			}
#endif
			slave->oobavail =
				data->ecc_boot.ecc_ctrl.layout->oobavail;
			slave->subpage_sft =
				data->ecc_boot.subpage_sft;
			slave->ecclayout =
				data->ecc_boot.ecc_ctrl.layout;

			dev_info(afm->dev, "boot-mode ECC: 0x%08x->0x%08x\n",
				 (unsigned int)data->boot_start,
				 (unsigned int)data->boot_end);

		} else {
			dev_info(afm->dev, "failed to find boot "
				 "partition [%s]\n", boot_part_name);
		}
#endif
	} else
#endif

		err = add_mtd_device(&data->mtd);

	/* Success! */
	if (!err)
		return data;

#ifdef CONFIG_MTD_PARTITIONS
 err3:
	if (data->parts && data->parts != bank->partitions)
		kfree(data->parts);
#endif
 err2:
	kfree(data);
 err1:
	return ERR_PTR(err);
}

/*
 * stm-nand-afm device probe
 */
static int __init stm_afm_probe(struct platform_device *pdev)
{
	struct stm_plat_nand_flex_data *pdata = pdev->dev.platform_data;
	struct stm_nand_bank_data *bank;
	struct stm_nand_afm_controller *afm;
	int n;
	int res;

	afm = afm_init_controller(pdev);
	if (IS_ERR(afm)) {
		dev_err(&pdev->dev, "failed to initialise NAND Controller.\n");
		res = PTR_ERR(afm);
		return res;
	}

	bank = pdata->banks;
	for (n = 0; n < pdata->nr_banks; n++) {
		afm->devices[n] = afm_init_bank(afm, bank,
						dev_name(&pdev->dev));
		bank++;
	}

	platform_set_drvdata(pdev, afm);

	return 0;
}


static int __devexit stm_afm_remove(struct platform_device *pdev)
{
	struct stm_plat_nand_flex_data *pdata = pdev->dev.platform_data;
	struct stm_nand_afm_controller *afm = platform_get_drvdata(pdev);
	int n;

	for (n = 0; n < pdata->nr_banks; n++) {
		struct stm_nand_afm_device *data = afm->devices[n];
		nand_release(&data->mtd);

#ifdef CONFIG_MTD_PARTITIONS
		if (data->parts && data->parts != pdata->banks[n].partitions)
			kfree(data->parts);
#endif
		kfree(data);
	}

	afm_exit_controller(pdev);

	platform_set_drvdata(pdev, NULL);

	return 0;
}

static struct platform_driver stm_afm_nand_driver = {
	.probe		= stm_afm_probe,
	.remove		= stm_afm_remove,
	.driver		= {
		.name	= NAME,
		.owner	= THIS_MODULE,
	},
};

static int __init stm_afm_nand_init(void)
{
	return platform_driver_register(&stm_afm_nand_driver);
}

static void __exit stm_afm_nand_exit(void)
{
	platform_driver_unregister(&stm_afm_nand_driver);
}

module_init(stm_afm_nand_init);
module_exit(stm_afm_nand_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Angus Clark");
MODULE_DESCRIPTION("STM AFM-based NAND Flash driver");