162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Freescale Enhanced Local Bus Controller NAND driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright © 2006-2007, 2010 Freescale Semiconductor
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Authors: Nick Spence <nick.spence@freescale.com>,
762306a36Sopenharmony_ci *          Scott Wood <scottwood@freescale.com>
862306a36Sopenharmony_ci *          Jack Lan <jack.lan@freescale.com>
962306a36Sopenharmony_ci *          Roy Zang <tie-fei.zang@freescale.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/ioport.h>
1762306a36Sopenharmony_ci#include <linux/of_address.h>
1862306a36Sopenharmony_ci#include <linux/of_platform.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/interrupt.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2462306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
2562306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <asm/io.h>
2862306a36Sopenharmony_ci#include <asm/fsl_lbc.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define MAX_BANKS 8
3162306a36Sopenharmony_ci#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
3262306a36Sopenharmony_ci#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* mtd information per set */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct fsl_elbc_mtd {
3762306a36Sopenharmony_ci	struct nand_chip chip;
3862306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	struct device *dev;
4162306a36Sopenharmony_ci	int bank;               /* Chip select bank number           */
4262306a36Sopenharmony_ci	u8 __iomem *vbase;      /* Chip select base virtual address  */
4362306a36Sopenharmony_ci	int page_size;          /* NAND page size (0=512, 1=2048)    */
4462306a36Sopenharmony_ci	unsigned int fmr;       /* FCM Flash Mode Register value     */
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Freescale eLBC FCM controller information */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct fsl_elbc_fcm_ctrl {
5062306a36Sopenharmony_ci	struct nand_controller controller;
5162306a36Sopenharmony_ci	struct fsl_elbc_mtd *chips[MAX_BANKS];
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	u8 __iomem *addr;        /* Address of assigned FCM buffer        */
5462306a36Sopenharmony_ci	unsigned int page;       /* Last page written to / read from      */
5562306a36Sopenharmony_ci	unsigned int read_bytes; /* Number of bytes read during command   */
5662306a36Sopenharmony_ci	unsigned int column;     /* Saved column from SEQIN               */
5762306a36Sopenharmony_ci	unsigned int index;      /* Pointer to next byte to 'read'        */
5862306a36Sopenharmony_ci	unsigned int status;     /* status read from LTESR after last op  */
5962306a36Sopenharmony_ci	unsigned int mdr;        /* UPM/FCM Data Register value           */
6062306a36Sopenharmony_ci	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
6162306a36Sopenharmony_ci	unsigned int oob;        /* Non zero if operating on OOB data     */
6262306a36Sopenharmony_ci	unsigned int counter;	 /* counter for the initializations	  */
6362306a36Sopenharmony_ci	unsigned int max_bitflips;  /* Saved during READ0 cmd		  */
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* These map to the positions used by the FCM hardware ECC generator */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
6962306a36Sopenharmony_ci				  struct mtd_oob_region *oobregion)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
7262306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
7562306a36Sopenharmony_ci		return -ERANGE;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	oobregion->offset = (16 * section) + 6;
7862306a36Sopenharmony_ci	if (priv->fmr & FMR_ECCM)
7962306a36Sopenharmony_ci		oobregion->offset += 2;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	oobregion->length = chip->ecc.bytes;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section,
8762306a36Sopenharmony_ci				   struct mtd_oob_region *oobregion)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
9062306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (section > chip->ecc.steps)
9362306a36Sopenharmony_ci		return -ERANGE;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (!section) {
9662306a36Sopenharmony_ci		oobregion->offset = 0;
9762306a36Sopenharmony_ci		if (mtd->writesize > 512)
9862306a36Sopenharmony_ci			oobregion->offset++;
9962306a36Sopenharmony_ci		oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5;
10062306a36Sopenharmony_ci	} else {
10162306a36Sopenharmony_ci		oobregion->offset = (16 * section) -
10262306a36Sopenharmony_ci				    ((priv->fmr & FMR_ECCM) ? 5 : 7);
10362306a36Sopenharmony_ci		if (section < chip->ecc.steps)
10462306a36Sopenharmony_ci			oobregion->length = 13;
10562306a36Sopenharmony_ci		else
10662306a36Sopenharmony_ci			oobregion->length = mtd->oobsize - oobregion->offset;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = {
11362306a36Sopenharmony_ci	.ecc = fsl_elbc_ooblayout_ecc,
11462306a36Sopenharmony_ci	.free = fsl_elbc_ooblayout_free,
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
11962306a36Sopenharmony_ci * interfere with ECC positions, that's why we implement our own descriptors.
12062306a36Sopenharmony_ci * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic u8 bbt_pattern[] = {'B', 'b', 't', '0' };
12362306a36Sopenharmony_cistatic u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = {
12662306a36Sopenharmony_ci	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
12762306a36Sopenharmony_ci		   NAND_BBT_2BIT | NAND_BBT_VERSION,
12862306a36Sopenharmony_ci	.offs =	11,
12962306a36Sopenharmony_ci	.len = 4,
13062306a36Sopenharmony_ci	.veroffs = 15,
13162306a36Sopenharmony_ci	.maxblocks = 4,
13262306a36Sopenharmony_ci	.pattern = bbt_pattern,
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = {
13662306a36Sopenharmony_ci	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
13762306a36Sopenharmony_ci		   NAND_BBT_2BIT | NAND_BBT_VERSION,
13862306a36Sopenharmony_ci	.offs =	11,
13962306a36Sopenharmony_ci	.len = 4,
14062306a36Sopenharmony_ci	.veroffs = 15,
14162306a36Sopenharmony_ci	.maxblocks = 4,
14262306a36Sopenharmony_ci	.pattern = mirror_pattern,
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*=================================*/
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * Set up the FCM hardware block and page address fields, and the fcm
14962306a36Sopenharmony_ci * structure addr field to point to the correct FCM buffer in memory
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
15462306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
15562306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
15662306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
15762306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
15862306a36Sopenharmony_ci	int buf_num;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	elbc_fcm_ctrl->page = page_addr;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (priv->page_size) {
16362306a36Sopenharmony_ci		/*
16462306a36Sopenharmony_ci		 * large page size chip : FPAR[PI] save the lowest 6 bits,
16562306a36Sopenharmony_ci		 *                        FBAR[BLK] save the other bits.
16662306a36Sopenharmony_ci		 */
16762306a36Sopenharmony_ci		out_be32(&lbc->fbar, page_addr >> 6);
16862306a36Sopenharmony_ci		out_be32(&lbc->fpar,
16962306a36Sopenharmony_ci		         ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
17062306a36Sopenharmony_ci		         (oob ? FPAR_LP_MS : 0) | column);
17162306a36Sopenharmony_ci		buf_num = (page_addr & 1) << 2;
17262306a36Sopenharmony_ci	} else {
17362306a36Sopenharmony_ci		/*
17462306a36Sopenharmony_ci		 * small page size chip : FPAR[PI] save the lowest 5 bits,
17562306a36Sopenharmony_ci		 *                        FBAR[BLK] save the other bits.
17662306a36Sopenharmony_ci		 */
17762306a36Sopenharmony_ci		out_be32(&lbc->fbar, page_addr >> 5);
17862306a36Sopenharmony_ci		out_be32(&lbc->fpar,
17962306a36Sopenharmony_ci		         ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
18062306a36Sopenharmony_ci		         (oob ? FPAR_SP_MS : 0) | column);
18162306a36Sopenharmony_ci		buf_num = page_addr & 7;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
18562306a36Sopenharmony_ci	elbc_fcm_ctrl->index = column;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* for OOB data point to the second half of the buffer */
18862306a36Sopenharmony_ci	if (oob)
18962306a36Sopenharmony_ci		elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dev_vdbg(priv->dev, "set_addr: bank=%d, "
19262306a36Sopenharmony_ci			    "elbc_fcm_ctrl->addr=0x%p (0x%p), "
19362306a36Sopenharmony_ci	                    "index %x, pes %d ps %d\n",
19462306a36Sopenharmony_ci		 buf_num, elbc_fcm_ctrl->addr, priv->vbase,
19562306a36Sopenharmony_ci		 elbc_fcm_ctrl->index,
19662306a36Sopenharmony_ci	         chip->phys_erase_shift, chip->page_shift);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/*
20062306a36Sopenharmony_ci * execute FCM command and wait for it to complete
20162306a36Sopenharmony_ci */
20262306a36Sopenharmony_cistatic int fsl_elbc_run_command(struct mtd_info *mtd)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
20562306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
20662306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
20762306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
20862306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Setup the FMR[OP] to execute without write protection */
21162306a36Sopenharmony_ci	out_be32(&lbc->fmr, priv->fmr | 3);
21262306a36Sopenharmony_ci	if (elbc_fcm_ctrl->use_mdr)
21362306a36Sopenharmony_ci		out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	dev_vdbg(priv->dev,
21662306a36Sopenharmony_ci	         "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
21762306a36Sopenharmony_ci	         in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
21862306a36Sopenharmony_ci	dev_vdbg(priv->dev,
21962306a36Sopenharmony_ci	         "fsl_elbc_run_command: fbar=%08x fpar=%08x "
22062306a36Sopenharmony_ci	         "fbcr=%08x bank=%d\n",
22162306a36Sopenharmony_ci	         in_be32(&lbc->fbar), in_be32(&lbc->fpar),
22262306a36Sopenharmony_ci	         in_be32(&lbc->fbcr), priv->bank);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ctrl->irq_status = 0;
22562306a36Sopenharmony_ci	/* execute special operation */
22662306a36Sopenharmony_ci	out_be32(&lbc->lsor, priv->bank);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* wait for FCM complete flag or timeout */
22962306a36Sopenharmony_ci	wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
23062306a36Sopenharmony_ci	                   FCM_TIMEOUT_MSECS * HZ/1000);
23162306a36Sopenharmony_ci	elbc_fcm_ctrl->status = ctrl->irq_status;
23262306a36Sopenharmony_ci	/* store mdr value in case it was needed */
23362306a36Sopenharmony_ci	if (elbc_fcm_ctrl->use_mdr)
23462306a36Sopenharmony_ci		elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	elbc_fcm_ctrl->use_mdr = 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (elbc_fcm_ctrl->status != LTESR_CC) {
23962306a36Sopenharmony_ci		dev_info(priv->dev,
24062306a36Sopenharmony_ci		         "command failed: fir %x fcr %x status %x mdr %x\n",
24162306a36Sopenharmony_ci		         in_be32(&lbc->fir), in_be32(&lbc->fcr),
24262306a36Sopenharmony_ci			 elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
24362306a36Sopenharmony_ci		return -EIO;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
24762306a36Sopenharmony_ci		return 0;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	elbc_fcm_ctrl->max_bitflips = 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
25262306a36Sopenharmony_ci		uint32_t lteccr = in_be32(&lbc->lteccr);
25362306a36Sopenharmony_ci		/*
25462306a36Sopenharmony_ci		 * if command was a full page read and the ELBC
25562306a36Sopenharmony_ci		 * has the LTECCR register, then bits 12-15 (ppc order) of
25662306a36Sopenharmony_ci		 * LTECCR indicates which 512 byte sub-pages had fixed errors.
25762306a36Sopenharmony_ci		 * bits 28-31 are uncorrectable errors, marked elsewhere.
25862306a36Sopenharmony_ci		 * for small page nand only 1 bit is used.
25962306a36Sopenharmony_ci		 * if the ELBC doesn't have the lteccr register it reads 0
26062306a36Sopenharmony_ci		 * FIXME: 4 bits can be corrected on NANDs with 2k pages, so
26162306a36Sopenharmony_ci		 * count the number of sub-pages with bitflips and update
26262306a36Sopenharmony_ci		 * ecc_stats.corrected accordingly.
26362306a36Sopenharmony_ci		 */
26462306a36Sopenharmony_ci		if (lteccr & 0x000F000F)
26562306a36Sopenharmony_ci			out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
26662306a36Sopenharmony_ci		if (lteccr & 0x000F0000) {
26762306a36Sopenharmony_ci			mtd->ecc_stats.corrected++;
26862306a36Sopenharmony_ci			elbc_fcm_ctrl->max_bitflips = 1;
26962306a36Sopenharmony_ci		}
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return 0;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic void fsl_elbc_do_read(struct nand_chip *chip, int oob)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
27862306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
27962306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (priv->page_size) {
28262306a36Sopenharmony_ci		out_be32(&lbc->fir,
28362306a36Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
28462306a36Sopenharmony_ci		         (FIR_OP_CA  << FIR_OP1_SHIFT) |
28562306a36Sopenharmony_ci		         (FIR_OP_PA  << FIR_OP2_SHIFT) |
28662306a36Sopenharmony_ci		         (FIR_OP_CM1 << FIR_OP3_SHIFT) |
28762306a36Sopenharmony_ci		         (FIR_OP_RBW << FIR_OP4_SHIFT));
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
29062306a36Sopenharmony_ci		                    (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
29162306a36Sopenharmony_ci	} else {
29262306a36Sopenharmony_ci		out_be32(&lbc->fir,
29362306a36Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
29462306a36Sopenharmony_ci		         (FIR_OP_CA  << FIR_OP1_SHIFT) |
29562306a36Sopenharmony_ci		         (FIR_OP_PA  << FIR_OP2_SHIFT) |
29662306a36Sopenharmony_ci		         (FIR_OP_RBW << FIR_OP3_SHIFT));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		if (oob)
29962306a36Sopenharmony_ci			out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT);
30062306a36Sopenharmony_ci		else
30162306a36Sopenharmony_ci			out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/* cmdfunc send commands to the FCM */
30662306a36Sopenharmony_cistatic void fsl_elbc_cmdfunc(struct nand_chip *chip, unsigned int command,
30762306a36Sopenharmony_ci                             int column, int page_addr)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
31062306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
31162306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
31262306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
31362306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	elbc_fcm_ctrl->use_mdr = 0;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* clear the read buffer */
31862306a36Sopenharmony_ci	elbc_fcm_ctrl->read_bytes = 0;
31962306a36Sopenharmony_ci	if (command != NAND_CMD_PAGEPROG)
32062306a36Sopenharmony_ci		elbc_fcm_ctrl->index = 0;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	switch (command) {
32362306a36Sopenharmony_ci	/* READ0 and READ1 read the entire buffer to use hardware ECC. */
32462306a36Sopenharmony_ci	case NAND_CMD_READ1:
32562306a36Sopenharmony_ci		column += 256;
32662306a36Sopenharmony_ci		fallthrough;
32762306a36Sopenharmony_ci	case NAND_CMD_READ0:
32862306a36Sopenharmony_ci		dev_dbg(priv->dev,
32962306a36Sopenharmony_ci		        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
33062306a36Sopenharmony_ci		        " 0x%x, column: 0x%x.\n", page_addr, column);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
33462306a36Sopenharmony_ci		set_addr(mtd, 0, page_addr, 0);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
33762306a36Sopenharmony_ci		elbc_fcm_ctrl->index += column;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		fsl_elbc_do_read(chip, 0);
34062306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
34162306a36Sopenharmony_ci		return;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* RNDOUT moves the pointer inside the page */
34462306a36Sopenharmony_ci	case NAND_CMD_RNDOUT:
34562306a36Sopenharmony_ci		dev_dbg(priv->dev,
34662306a36Sopenharmony_ci			"fsl_elbc_cmdfunc: NAND_CMD_RNDOUT, column: 0x%x.\n",
34762306a36Sopenharmony_ci			column);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci		elbc_fcm_ctrl->index = column;
35062306a36Sopenharmony_ci		return;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* READOOB reads only the OOB because no ECC is performed. */
35362306a36Sopenharmony_ci	case NAND_CMD_READOOB:
35462306a36Sopenharmony_ci		dev_vdbg(priv->dev,
35562306a36Sopenharmony_ci		         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
35662306a36Sopenharmony_ci			 " 0x%x, column: 0x%x.\n", page_addr, column);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci		out_be32(&lbc->fbcr, mtd->oobsize - column);
35962306a36Sopenharmony_ci		set_addr(mtd, column, page_addr, 1);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		fsl_elbc_do_read(chip, 1);
36462306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
36562306a36Sopenharmony_ci		return;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	case NAND_CMD_READID:
36862306a36Sopenharmony_ci	case NAND_CMD_PARAM:
36962306a36Sopenharmony_ci		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
37262306a36Sopenharmony_ci		                    (FIR_OP_UA  << FIR_OP1_SHIFT) |
37362306a36Sopenharmony_ci		                    (FIR_OP_RBW << FIR_OP2_SHIFT));
37462306a36Sopenharmony_ci		out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
37562306a36Sopenharmony_ci		/*
37662306a36Sopenharmony_ci		 * although currently it's 8 bytes for READID, we always read
37762306a36Sopenharmony_ci		 * the maximum 256 bytes(for PARAM)
37862306a36Sopenharmony_ci		 */
37962306a36Sopenharmony_ci		out_be32(&lbc->fbcr, 256);
38062306a36Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = 256;
38162306a36Sopenharmony_ci		elbc_fcm_ctrl->use_mdr = 1;
38262306a36Sopenharmony_ci		elbc_fcm_ctrl->mdr = column;
38362306a36Sopenharmony_ci		set_addr(mtd, 0, 0, 0);
38462306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
38562306a36Sopenharmony_ci		return;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* ERASE1 stores the block and page address */
38862306a36Sopenharmony_ci	case NAND_CMD_ERASE1:
38962306a36Sopenharmony_ci		dev_vdbg(priv->dev,
39062306a36Sopenharmony_ci		         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
39162306a36Sopenharmony_ci		         "page_addr: 0x%x.\n", page_addr);
39262306a36Sopenharmony_ci		set_addr(mtd, 0, page_addr, 0);
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* ERASE2 uses the block and page address from ERASE1 */
39662306a36Sopenharmony_ci	case NAND_CMD_ERASE2:
39762306a36Sopenharmony_ci		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		out_be32(&lbc->fir,
40062306a36Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
40162306a36Sopenharmony_ci		         (FIR_OP_PA  << FIR_OP1_SHIFT) |
40262306a36Sopenharmony_ci		         (FIR_OP_CM2 << FIR_OP2_SHIFT) |
40362306a36Sopenharmony_ci		         (FIR_OP_CW1 << FIR_OP3_SHIFT) |
40462306a36Sopenharmony_ci		         (FIR_OP_RS  << FIR_OP4_SHIFT));
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci		out_be32(&lbc->fcr,
40762306a36Sopenharmony_ci		         (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
40862306a36Sopenharmony_ci		         (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
40962306a36Sopenharmony_ci		         (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		out_be32(&lbc->fbcr, 0);
41262306a36Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = 0;
41362306a36Sopenharmony_ci		elbc_fcm_ctrl->use_mdr = 1;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
41662306a36Sopenharmony_ci		return;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	/* SEQIN sets up the addr buffer and all registers except the length */
41962306a36Sopenharmony_ci	case NAND_CMD_SEQIN: {
42062306a36Sopenharmony_ci		__be32 fcr;
42162306a36Sopenharmony_ci		dev_vdbg(priv->dev,
42262306a36Sopenharmony_ci			 "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
42362306a36Sopenharmony_ci		         "page_addr: 0x%x, column: 0x%x.\n",
42462306a36Sopenharmony_ci		         page_addr, column);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		elbc_fcm_ctrl->column = column;
42762306a36Sopenharmony_ci		elbc_fcm_ctrl->use_mdr = 1;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		if (column >= mtd->writesize) {
43062306a36Sopenharmony_ci			/* OOB area */
43162306a36Sopenharmony_ci			column -= mtd->writesize;
43262306a36Sopenharmony_ci			elbc_fcm_ctrl->oob = 1;
43362306a36Sopenharmony_ci		} else {
43462306a36Sopenharmony_ci			WARN_ON(column != 0);
43562306a36Sopenharmony_ci			elbc_fcm_ctrl->oob = 0;
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
43962306a36Sopenharmony_ci		      (NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
44062306a36Sopenharmony_ci		      (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		if (priv->page_size) {
44362306a36Sopenharmony_ci			out_be32(&lbc->fir,
44462306a36Sopenharmony_ci			         (FIR_OP_CM2 << FIR_OP0_SHIFT) |
44562306a36Sopenharmony_ci			         (FIR_OP_CA  << FIR_OP1_SHIFT) |
44662306a36Sopenharmony_ci			         (FIR_OP_PA  << FIR_OP2_SHIFT) |
44762306a36Sopenharmony_ci			         (FIR_OP_WB  << FIR_OP3_SHIFT) |
44862306a36Sopenharmony_ci			         (FIR_OP_CM3 << FIR_OP4_SHIFT) |
44962306a36Sopenharmony_ci			         (FIR_OP_CW1 << FIR_OP5_SHIFT) |
45062306a36Sopenharmony_ci			         (FIR_OP_RS  << FIR_OP6_SHIFT));
45162306a36Sopenharmony_ci		} else {
45262306a36Sopenharmony_ci			out_be32(&lbc->fir,
45362306a36Sopenharmony_ci			         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
45462306a36Sopenharmony_ci			         (FIR_OP_CM2 << FIR_OP1_SHIFT) |
45562306a36Sopenharmony_ci			         (FIR_OP_CA  << FIR_OP2_SHIFT) |
45662306a36Sopenharmony_ci			         (FIR_OP_PA  << FIR_OP3_SHIFT) |
45762306a36Sopenharmony_ci			         (FIR_OP_WB  << FIR_OP4_SHIFT) |
45862306a36Sopenharmony_ci			         (FIR_OP_CM3 << FIR_OP5_SHIFT) |
45962306a36Sopenharmony_ci			         (FIR_OP_CW1 << FIR_OP6_SHIFT) |
46062306a36Sopenharmony_ci			         (FIR_OP_RS  << FIR_OP7_SHIFT));
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci			if (elbc_fcm_ctrl->oob)
46362306a36Sopenharmony_ci				/* OOB area --> READOOB */
46462306a36Sopenharmony_ci				fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
46562306a36Sopenharmony_ci			else
46662306a36Sopenharmony_ci				/* First 256 bytes --> READ0 */
46762306a36Sopenharmony_ci				fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
46862306a36Sopenharmony_ci		}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		out_be32(&lbc->fcr, fcr);
47162306a36Sopenharmony_ci		set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
47262306a36Sopenharmony_ci		return;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
47662306a36Sopenharmony_ci	case NAND_CMD_PAGEPROG: {
47762306a36Sopenharmony_ci		dev_vdbg(priv->dev,
47862306a36Sopenharmony_ci		         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
47962306a36Sopenharmony_ci			 "writing %d bytes.\n", elbc_fcm_ctrl->index);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		/* if the write did not start at 0 or is not a full page
48262306a36Sopenharmony_ci		 * then set the exact length, otherwise use a full page
48362306a36Sopenharmony_ci		 * write so the HW generates the ECC.
48462306a36Sopenharmony_ci		 */
48562306a36Sopenharmony_ci		if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
48662306a36Sopenharmony_ci		    elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize)
48762306a36Sopenharmony_ci			out_be32(&lbc->fbcr,
48862306a36Sopenharmony_ci				elbc_fcm_ctrl->index - elbc_fcm_ctrl->column);
48962306a36Sopenharmony_ci		else
49062306a36Sopenharmony_ci			out_be32(&lbc->fbcr, 0);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
49362306a36Sopenharmony_ci		return;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* CMD_STATUS must read the status byte while CEB is active */
49762306a36Sopenharmony_ci	/* Note - it does not wait for the ready line */
49862306a36Sopenharmony_ci	case NAND_CMD_STATUS:
49962306a36Sopenharmony_ci		out_be32(&lbc->fir,
50062306a36Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
50162306a36Sopenharmony_ci		         (FIR_OP_RBW << FIR_OP1_SHIFT));
50262306a36Sopenharmony_ci		out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
50362306a36Sopenharmony_ci		out_be32(&lbc->fbcr, 1);
50462306a36Sopenharmony_ci		set_addr(mtd, 0, 0, 0);
50562306a36Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = 1;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci		/* The chip always seems to report that it is
51062306a36Sopenharmony_ci		 * write-protected, even when it is not.
51162306a36Sopenharmony_ci		 */
51262306a36Sopenharmony_ci		setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
51362306a36Sopenharmony_ci		return;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	/* RESET without waiting for the ready line */
51662306a36Sopenharmony_ci	case NAND_CMD_RESET:
51762306a36Sopenharmony_ci		dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
51862306a36Sopenharmony_ci		out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
51962306a36Sopenharmony_ci		out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
52062306a36Sopenharmony_ci		fsl_elbc_run_command(mtd);
52162306a36Sopenharmony_ci		return;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	default:
52462306a36Sopenharmony_ci		dev_err(priv->dev,
52562306a36Sopenharmony_ci		        "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
52662306a36Sopenharmony_ci		        command);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic void fsl_elbc_select_chip(struct nand_chip *chip, int cs)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	/* The hardware does not seem to support multiple
53362306a36Sopenharmony_ci	 * chips per bank.
53462306a36Sopenharmony_ci	 */
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/*
53862306a36Sopenharmony_ci * Write buf to the FCM Controller Data Buffer
53962306a36Sopenharmony_ci */
54062306a36Sopenharmony_cistatic void fsl_elbc_write_buf(struct nand_chip *chip, const u8 *buf, int len)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
54362306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
54462306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
54562306a36Sopenharmony_ci	unsigned int bufsize = mtd->writesize + mtd->oobsize;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (len <= 0) {
54862306a36Sopenharmony_ci		dev_err(priv->dev, "write_buf of %d bytes", len);
54962306a36Sopenharmony_ci		elbc_fcm_ctrl->status = 0;
55062306a36Sopenharmony_ci		return;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
55462306a36Sopenharmony_ci		dev_err(priv->dev,
55562306a36Sopenharmony_ci		        "write_buf beyond end of buffer "
55662306a36Sopenharmony_ci		        "(%d requested, %u available)\n",
55762306a36Sopenharmony_ci			len, bufsize - elbc_fcm_ctrl->index);
55862306a36Sopenharmony_ci		len = bufsize - elbc_fcm_ctrl->index;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
56262306a36Sopenharmony_ci	/*
56362306a36Sopenharmony_ci	 * This is workaround for the weird elbc hangs during nand write,
56462306a36Sopenharmony_ci	 * Scott Wood says: "...perhaps difference in how long it takes a
56562306a36Sopenharmony_ci	 * write to make it through the localbus compared to a write to IMMR
56662306a36Sopenharmony_ci	 * is causing problems, and sync isn't helping for some reason."
56762306a36Sopenharmony_ci	 * Reading back the last byte helps though.
56862306a36Sopenharmony_ci	 */
56962306a36Sopenharmony_ci	in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	elbc_fcm_ctrl->index += len;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci/*
57562306a36Sopenharmony_ci * read a byte from either the FCM hardware buffer if it has any data left
57662306a36Sopenharmony_ci * otherwise issue a command to read a single byte.
57762306a36Sopenharmony_ci */
57862306a36Sopenharmony_cistatic u8 fsl_elbc_read_byte(struct nand_chip *chip)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
58162306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/* If there are still bytes in the FCM, then use the next byte. */
58462306a36Sopenharmony_ci	if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
58562306a36Sopenharmony_ci		return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	dev_err(priv->dev, "read_byte beyond end of buffer\n");
58862306a36Sopenharmony_ci	return ERR_BYTE;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/*
59262306a36Sopenharmony_ci * Read from the FCM Controller Data Buffer
59362306a36Sopenharmony_ci */
59462306a36Sopenharmony_cistatic void fsl_elbc_read_buf(struct nand_chip *chip, u8 *buf, int len)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
59762306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
59862306a36Sopenharmony_ci	int avail;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (len < 0)
60162306a36Sopenharmony_ci		return;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	avail = min((unsigned int)len,
60462306a36Sopenharmony_ci			elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
60562306a36Sopenharmony_ci	memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
60662306a36Sopenharmony_ci	elbc_fcm_ctrl->index += avail;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (len > avail)
60962306a36Sopenharmony_ci		dev_err(priv->dev,
61062306a36Sopenharmony_ci		        "read_buf beyond end of buffer "
61162306a36Sopenharmony_ci		        "(%d requested, %d available)\n",
61262306a36Sopenharmony_ci		        len, avail);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci/* This function is called after Program and Erase Operations to
61662306a36Sopenharmony_ci * check for success or failure.
61762306a36Sopenharmony_ci */
61862306a36Sopenharmony_cistatic int fsl_elbc_wait(struct nand_chip *chip)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
62162306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (elbc_fcm_ctrl->status != LTESR_CC)
62462306a36Sopenharmony_ci		return NAND_STATUS_FAIL;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	/* The chip always seems to report that it is
62762306a36Sopenharmony_ci	 * write-protected, even when it is not.
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic int fsl_elbc_read_page(struct nand_chip *chip, uint8_t *buf,
63362306a36Sopenharmony_ci			      int oob_required, int page)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
63662306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
63762306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
63862306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
64162306a36Sopenharmony_ci	if (oob_required)
64262306a36Sopenharmony_ci		fsl_elbc_read_buf(chip, chip->oob_poi, mtd->oobsize);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (fsl_elbc_wait(chip) & NAND_STATUS_FAIL)
64562306a36Sopenharmony_ci		mtd->ecc_stats.failed++;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	return elbc_fcm_ctrl->max_bitflips;
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in
65162306a36Sopenharmony_ci * waitfunc.
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic int fsl_elbc_write_page(struct nand_chip *chip, const uint8_t *buf,
65462306a36Sopenharmony_ci			       int oob_required, int page)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
65962306a36Sopenharmony_ci	fsl_elbc_write_buf(chip, chip->oob_poi, mtd->oobsize);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return nand_prog_page_end_op(chip);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in
66562306a36Sopenharmony_ci * waitfunc.
66662306a36Sopenharmony_ci */
66762306a36Sopenharmony_cistatic int fsl_elbc_write_subpage(struct nand_chip *chip, uint32_t offset,
66862306a36Sopenharmony_ci				  uint32_t data_len, const uint8_t *buf,
66962306a36Sopenharmony_ci				  int oob_required, int page)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
67462306a36Sopenharmony_ci	fsl_elbc_write_buf(chip, buf, mtd->writesize);
67562306a36Sopenharmony_ci	fsl_elbc_write_buf(chip, chip->oob_poi, mtd->oobsize);
67662306a36Sopenharmony_ci	return nand_prog_page_end_op(chip);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
68262306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
68362306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
68462306a36Sopenharmony_ci	struct nand_chip *chip = &priv->chip;
68562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	/* Fill in fsl_elbc_mtd structure */
69062306a36Sopenharmony_ci	mtd->dev.parent = priv->dev;
69162306a36Sopenharmony_ci	nand_set_flash_node(chip, priv->dev->of_node);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/* set timeout to maximum */
69462306a36Sopenharmony_ci	priv->fmr = 15 << FMR_CWTO_SHIFT;
69562306a36Sopenharmony_ci	if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS)
69662306a36Sopenharmony_ci		priv->fmr |= FMR_ECCM;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* fill in nand_chip structure */
69962306a36Sopenharmony_ci	/* set up function call table */
70062306a36Sopenharmony_ci	chip->legacy.read_byte = fsl_elbc_read_byte;
70162306a36Sopenharmony_ci	chip->legacy.write_buf = fsl_elbc_write_buf;
70262306a36Sopenharmony_ci	chip->legacy.read_buf = fsl_elbc_read_buf;
70362306a36Sopenharmony_ci	chip->legacy.select_chip = fsl_elbc_select_chip;
70462306a36Sopenharmony_ci	chip->legacy.cmdfunc = fsl_elbc_cmdfunc;
70562306a36Sopenharmony_ci	chip->legacy.waitfunc = fsl_elbc_wait;
70662306a36Sopenharmony_ci	chip->legacy.set_features = nand_get_set_features_notsupp;
70762306a36Sopenharmony_ci	chip->legacy.get_features = nand_get_set_features_notsupp;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	chip->bbt_td = &bbt_main_descr;
71062306a36Sopenharmony_ci	chip->bbt_md = &bbt_mirror_descr;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* set up nand options */
71362306a36Sopenharmony_ci	chip->bbt_options = NAND_BBT_USE_FLASH;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	chip->controller = &elbc_fcm_ctrl->controller;
71662306a36Sopenharmony_ci	nand_set_controller_data(chip, priv);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int fsl_elbc_attach_chip(struct nand_chip *chip)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
72462306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
72562306a36Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
72662306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
72762306a36Sopenharmony_ci	unsigned int al;
72862306a36Sopenharmony_ci	u32 br;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/*
73162306a36Sopenharmony_ci	 * if ECC was not chosen in DT, decide whether to use HW or SW ECC from
73262306a36Sopenharmony_ci	 * CS Base Register
73362306a36Sopenharmony_ci	 */
73462306a36Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) {
73562306a36Sopenharmony_ci		/* If CS Base Register selects full hardware ECC then use it */
73662306a36Sopenharmony_ci		if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
73762306a36Sopenharmony_ci		    BR_DECC_CHK_GEN) {
73862306a36Sopenharmony_ci			chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
73962306a36Sopenharmony_ci		} else {
74062306a36Sopenharmony_ci			/* otherwise fall back to default software ECC */
74162306a36Sopenharmony_ci			chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
74262306a36Sopenharmony_ci			chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	switch (chip->ecc.engine_type) {
74762306a36Sopenharmony_ci	/* if HW ECC was chosen, setup ecc and oob layout */
74862306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_HOST:
74962306a36Sopenharmony_ci		chip->ecc.read_page = fsl_elbc_read_page;
75062306a36Sopenharmony_ci		chip->ecc.write_page = fsl_elbc_write_page;
75162306a36Sopenharmony_ci		chip->ecc.write_subpage = fsl_elbc_write_subpage;
75262306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
75362306a36Sopenharmony_ci		chip->ecc.size = 512;
75462306a36Sopenharmony_ci		chip->ecc.bytes = 3;
75562306a36Sopenharmony_ci		chip->ecc.strength = 1;
75662306a36Sopenharmony_ci		break;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	/* if none or SW ECC was chosen, we do not need to set anything here */
75962306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_NONE:
76062306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_SOFT:
76162306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_DIE:
76262306a36Sopenharmony_ci		break;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	default:
76562306a36Sopenharmony_ci		return -EINVAL;
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/* enable/disable HW ECC checking and generating based on if HW ECC was chosen */
76962306a36Sopenharmony_ci	br = in_be32(&lbc->bank[priv->bank].br) & ~BR_DECC;
77062306a36Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
77162306a36Sopenharmony_ci		out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_CHK_GEN);
77262306a36Sopenharmony_ci	else
77362306a36Sopenharmony_ci		out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_OFF);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* calculate FMR Address Length field */
77662306a36Sopenharmony_ci	al = 0;
77762306a36Sopenharmony_ci	if (chip->pagemask & 0xffff0000)
77862306a36Sopenharmony_ci		al++;
77962306a36Sopenharmony_ci	if (chip->pagemask & 0xff000000)
78062306a36Sopenharmony_ci		al++;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	priv->fmr |= al << FMR_AL_SHIFT;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
78562306a36Sopenharmony_ci	        nanddev_ntargets(&chip->base));
78662306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
78762306a36Sopenharmony_ci	        nanddev_target_size(&chip->base));
78862306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
78962306a36Sopenharmony_ci	        chip->pagemask);
79062306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->legacy.chip_delay = %d\n",
79162306a36Sopenharmony_ci	        chip->legacy.chip_delay);
79262306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
79362306a36Sopenharmony_ci	        chip->badblockpos);
79462306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
79562306a36Sopenharmony_ci	        chip->chip_shift);
79662306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
79762306a36Sopenharmony_ci	        chip->page_shift);
79862306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
79962306a36Sopenharmony_ci	        chip->phys_erase_shift);
80062306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.engine_type = %d\n",
80162306a36Sopenharmony_ci		chip->ecc.engine_type);
80262306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
80362306a36Sopenharmony_ci	        chip->ecc.steps);
80462306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
80562306a36Sopenharmony_ci	        chip->ecc.bytes);
80662306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
80762306a36Sopenharmony_ci	        chip->ecc.total);
80862306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
80962306a36Sopenharmony_ci		mtd->ooblayout);
81062306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
81162306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
81262306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
81362306a36Sopenharmony_ci	        mtd->erasesize);
81462306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
81562306a36Sopenharmony_ci	        mtd->writesize);
81662306a36Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
81762306a36Sopenharmony_ci	        mtd->oobsize);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	/* adjust Option Register and ECC to match Flash page size */
82062306a36Sopenharmony_ci	if (mtd->writesize == 512) {
82162306a36Sopenharmony_ci		priv->page_size = 0;
82262306a36Sopenharmony_ci		clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
82362306a36Sopenharmony_ci	} else if (mtd->writesize == 2048) {
82462306a36Sopenharmony_ci		priv->page_size = 1;
82562306a36Sopenharmony_ci		setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
82662306a36Sopenharmony_ci	} else {
82762306a36Sopenharmony_ci		dev_err(priv->dev,
82862306a36Sopenharmony_ci		        "fsl_elbc_init: page size %d is not supported\n",
82962306a36Sopenharmony_ci		        mtd->writesize);
83062306a36Sopenharmony_ci		return -ENOTSUPP;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return 0;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic const struct nand_controller_ops fsl_elbc_controller_ops = {
83762306a36Sopenharmony_ci	.attach_chip = fsl_elbc_attach_chip,
83862306a36Sopenharmony_ci};
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
84362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&priv->chip);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	kfree(mtd->name);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	if (priv->vbase)
84862306a36Sopenharmony_ci		iounmap(priv->vbase);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	elbc_fcm_ctrl->chips[priv->bank] = NULL;
85162306a36Sopenharmony_ci	kfree(priv);
85262306a36Sopenharmony_ci	return 0;
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic DEFINE_MUTEX(fsl_elbc_nand_mutex);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int fsl_elbc_nand_probe(struct platform_device *pdev)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc;
86062306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv;
86162306a36Sopenharmony_ci	struct resource res;
86262306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
86362306a36Sopenharmony_ci	static const char *part_probe_types[]
86462306a36Sopenharmony_ci		= { "cmdlinepart", "RedBoot", "ofpart", NULL };
86562306a36Sopenharmony_ci	int ret;
86662306a36Sopenharmony_ci	int bank;
86762306a36Sopenharmony_ci	struct device *dev;
86862306a36Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
86962306a36Sopenharmony_ci	struct mtd_info *mtd;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
87262306a36Sopenharmony_ci		return -ENODEV;
87362306a36Sopenharmony_ci	lbc = fsl_lbc_ctrl_dev->regs;
87462306a36Sopenharmony_ci	dev = fsl_lbc_ctrl_dev->dev;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	/* get, allocate and map the memory resource */
87762306a36Sopenharmony_ci	ret = of_address_to_resource(node, 0, &res);
87862306a36Sopenharmony_ci	if (ret) {
87962306a36Sopenharmony_ci		dev_err(dev, "failed to get resource\n");
88062306a36Sopenharmony_ci		return ret;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* find which chip select it is connected to */
88462306a36Sopenharmony_ci	for (bank = 0; bank < MAX_BANKS; bank++)
88562306a36Sopenharmony_ci		if ((in_be32(&lbc->bank[bank].br) & BR_V) &&
88662306a36Sopenharmony_ci		    (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
88762306a36Sopenharmony_ci		    (in_be32(&lbc->bank[bank].br) &
88862306a36Sopenharmony_ci		     in_be32(&lbc->bank[bank].or) & BR_BA)
88962306a36Sopenharmony_ci		     == fsl_lbc_addr(res.start))
89062306a36Sopenharmony_ci			break;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (bank >= MAX_BANKS) {
89362306a36Sopenharmony_ci		dev_err(dev, "address did not match any chip selects\n");
89462306a36Sopenharmony_ci		return -ENODEV;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
89862306a36Sopenharmony_ci	if (!priv)
89962306a36Sopenharmony_ci		return -ENOMEM;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	mutex_lock(&fsl_elbc_nand_mutex);
90262306a36Sopenharmony_ci	if (!fsl_lbc_ctrl_dev->nand) {
90362306a36Sopenharmony_ci		elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
90462306a36Sopenharmony_ci		if (!elbc_fcm_ctrl) {
90562306a36Sopenharmony_ci			mutex_unlock(&fsl_elbc_nand_mutex);
90662306a36Sopenharmony_ci			ret = -ENOMEM;
90762306a36Sopenharmony_ci			goto err;
90862306a36Sopenharmony_ci		}
90962306a36Sopenharmony_ci		elbc_fcm_ctrl->counter++;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		nand_controller_init(&elbc_fcm_ctrl->controller);
91262306a36Sopenharmony_ci		fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
91362306a36Sopenharmony_ci	} else {
91462306a36Sopenharmony_ci		elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci	mutex_unlock(&fsl_elbc_nand_mutex);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	elbc_fcm_ctrl->chips[bank] = priv;
91962306a36Sopenharmony_ci	priv->bank = bank;
92062306a36Sopenharmony_ci	priv->ctrl = fsl_lbc_ctrl_dev;
92162306a36Sopenharmony_ci	priv->dev = &pdev->dev;
92262306a36Sopenharmony_ci	dev_set_drvdata(priv->dev, priv);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	priv->vbase = ioremap(res.start, resource_size(&res));
92562306a36Sopenharmony_ci	if (!priv->vbase) {
92662306a36Sopenharmony_ci		dev_err(dev, "failed to map chip region\n");
92762306a36Sopenharmony_ci		ret = -ENOMEM;
92862306a36Sopenharmony_ci		goto err;
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	mtd = nand_to_mtd(&priv->chip);
93262306a36Sopenharmony_ci	mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
93362306a36Sopenharmony_ci	if (!nand_to_mtd(&priv->chip)->name) {
93462306a36Sopenharmony_ci		ret = -ENOMEM;
93562306a36Sopenharmony_ci		goto err;
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	ret = fsl_elbc_chip_init(priv);
93962306a36Sopenharmony_ci	if (ret)
94062306a36Sopenharmony_ci		goto err;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	priv->chip.controller->ops = &fsl_elbc_controller_ops;
94362306a36Sopenharmony_ci	ret = nand_scan(&priv->chip, 1);
94462306a36Sopenharmony_ci	if (ret)
94562306a36Sopenharmony_ci		goto err;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	/* First look for RedBoot table or partitions on the command
94862306a36Sopenharmony_ci	 * line, these take precedence over device tree information */
94962306a36Sopenharmony_ci	ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
95062306a36Sopenharmony_ci	if (ret)
95162306a36Sopenharmony_ci		goto cleanup_nand;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	pr_info("eLBC NAND device at 0x%llx, bank %d\n",
95462306a36Sopenharmony_ci		(unsigned long long)res.start, priv->bank);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cicleanup_nand:
95962306a36Sopenharmony_ci	nand_cleanup(&priv->chip);
96062306a36Sopenharmony_cierr:
96162306a36Sopenharmony_ci	fsl_elbc_chip_remove(priv);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	return ret;
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic void fsl_elbc_nand_remove(struct platform_device *pdev)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
96962306a36Sopenharmony_ci	struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
97062306a36Sopenharmony_ci	struct nand_chip *chip = &priv->chip;
97162306a36Sopenharmony_ci	int ret;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
97462306a36Sopenharmony_ci	WARN_ON(ret);
97562306a36Sopenharmony_ci	nand_cleanup(chip);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	fsl_elbc_chip_remove(priv);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	mutex_lock(&fsl_elbc_nand_mutex);
98062306a36Sopenharmony_ci	elbc_fcm_ctrl->counter--;
98162306a36Sopenharmony_ci	if (!elbc_fcm_ctrl->counter) {
98262306a36Sopenharmony_ci		fsl_lbc_ctrl_dev->nand = NULL;
98362306a36Sopenharmony_ci		kfree(elbc_fcm_ctrl);
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci	mutex_unlock(&fsl_elbc_nand_mutex);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_cistatic const struct of_device_id fsl_elbc_nand_match[] = {
99062306a36Sopenharmony_ci	{ .compatible = "fsl,elbc-fcm-nand", },
99162306a36Sopenharmony_ci	{}
99262306a36Sopenharmony_ci};
99362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_elbc_nand_match);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cistatic struct platform_driver fsl_elbc_nand_driver = {
99662306a36Sopenharmony_ci	.driver = {
99762306a36Sopenharmony_ci		.name = "fsl,elbc-fcm-nand",
99862306a36Sopenharmony_ci		.of_match_table = fsl_elbc_nand_match,
99962306a36Sopenharmony_ci	},
100062306a36Sopenharmony_ci	.probe = fsl_elbc_nand_probe,
100162306a36Sopenharmony_ci	.remove_new = fsl_elbc_nand_remove,
100262306a36Sopenharmony_ci};
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cimodule_platform_driver(fsl_elbc_nand_driver);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
100762306a36Sopenharmony_ciMODULE_AUTHOR("Freescale");
100862306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver");
1009