18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Freescale Enhanced Local Bus Controller NAND driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright © 2006-2007, 2010 Freescale Semiconductor
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Authors: Nick Spence <nick.spence@freescale.com>,
78c2ecf20Sopenharmony_ci *          Scott Wood <scottwood@freescale.com>
88c2ecf20Sopenharmony_ci *          Jack Lan <jack.lan@freescale.com>
98c2ecf20Sopenharmony_ci *          Roy Zang <tie-fei.zang@freescale.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/string.h>
168c2ecf20Sopenharmony_ci#include <linux/ioport.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
248c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
258c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h>
268c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <asm/io.h>
298c2ecf20Sopenharmony_ci#include <asm/fsl_lbc.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define MAX_BANKS 8
328c2ecf20Sopenharmony_ci#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
338c2ecf20Sopenharmony_ci#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* mtd information per set */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct fsl_elbc_mtd {
388c2ecf20Sopenharmony_ci	struct nand_chip chip;
398c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	struct device *dev;
428c2ecf20Sopenharmony_ci	int bank;               /* Chip select bank number           */
438c2ecf20Sopenharmony_ci	u8 __iomem *vbase;      /* Chip select base virtual address  */
448c2ecf20Sopenharmony_ci	int page_size;          /* NAND page size (0=512, 1=2048)    */
458c2ecf20Sopenharmony_ci	unsigned int fmr;       /* FCM Flash Mode Register value     */
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* Freescale eLBC FCM controller information */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct fsl_elbc_fcm_ctrl {
518c2ecf20Sopenharmony_ci	struct nand_controller controller;
528c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *chips[MAX_BANKS];
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	u8 __iomem *addr;        /* Address of assigned FCM buffer        */
558c2ecf20Sopenharmony_ci	unsigned int page;       /* Last page written to / read from      */
568c2ecf20Sopenharmony_ci	unsigned int read_bytes; /* Number of bytes read during command   */
578c2ecf20Sopenharmony_ci	unsigned int column;     /* Saved column from SEQIN               */
588c2ecf20Sopenharmony_ci	unsigned int index;      /* Pointer to next byte to 'read'        */
598c2ecf20Sopenharmony_ci	unsigned int status;     /* status read from LTESR after last op  */
608c2ecf20Sopenharmony_ci	unsigned int mdr;        /* UPM/FCM Data Register value           */
618c2ecf20Sopenharmony_ci	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
628c2ecf20Sopenharmony_ci	unsigned int oob;        /* Non zero if operating on OOB data     */
638c2ecf20Sopenharmony_ci	unsigned int counter;	 /* counter for the initializations	  */
648c2ecf20Sopenharmony_ci	unsigned int max_bitflips;  /* Saved during READ0 cmd		  */
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/* These map to the positions used by the FCM hardware ECC generator */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section,
708c2ecf20Sopenharmony_ci				  struct mtd_oob_region *oobregion)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
738c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (section >= chip->ecc.steps)
768c2ecf20Sopenharmony_ci		return -ERANGE;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	oobregion->offset = (16 * section) + 6;
798c2ecf20Sopenharmony_ci	if (priv->fmr & FMR_ECCM)
808c2ecf20Sopenharmony_ci		oobregion->offset += 2;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	oobregion->length = chip->ecc.bytes;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section,
888c2ecf20Sopenharmony_ci				   struct mtd_oob_region *oobregion)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
918c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (section > chip->ecc.steps)
948c2ecf20Sopenharmony_ci		return -ERANGE;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (!section) {
978c2ecf20Sopenharmony_ci		oobregion->offset = 0;
988c2ecf20Sopenharmony_ci		if (mtd->writesize > 512)
998c2ecf20Sopenharmony_ci			oobregion->offset++;
1008c2ecf20Sopenharmony_ci		oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5;
1018c2ecf20Sopenharmony_ci	} else {
1028c2ecf20Sopenharmony_ci		oobregion->offset = (16 * section) -
1038c2ecf20Sopenharmony_ci				    ((priv->fmr & FMR_ECCM) ? 5 : 7);
1048c2ecf20Sopenharmony_ci		if (section < chip->ecc.steps)
1058c2ecf20Sopenharmony_ci			oobregion->length = 13;
1068c2ecf20Sopenharmony_ci		else
1078c2ecf20Sopenharmony_ci			oobregion->length = mtd->oobsize - oobregion->offset;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = {
1148c2ecf20Sopenharmony_ci	.ecc = fsl_elbc_ooblayout_ecc,
1158c2ecf20Sopenharmony_ci	.free = fsl_elbc_ooblayout_free,
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt,
1208c2ecf20Sopenharmony_ci * interfere with ECC positions, that's why we implement our own descriptors.
1218c2ecf20Sopenharmony_ci * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0.
1228c2ecf20Sopenharmony_ci */
1238c2ecf20Sopenharmony_cistatic u8 bbt_pattern[] = {'B', 'b', 't', '0' };
1248c2ecf20Sopenharmony_cistatic u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = {
1278c2ecf20Sopenharmony_ci	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
1288c2ecf20Sopenharmony_ci		   NAND_BBT_2BIT | NAND_BBT_VERSION,
1298c2ecf20Sopenharmony_ci	.offs =	11,
1308c2ecf20Sopenharmony_ci	.len = 4,
1318c2ecf20Sopenharmony_ci	.veroffs = 15,
1328c2ecf20Sopenharmony_ci	.maxblocks = 4,
1338c2ecf20Sopenharmony_ci	.pattern = bbt_pattern,
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = {
1378c2ecf20Sopenharmony_ci	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
1388c2ecf20Sopenharmony_ci		   NAND_BBT_2BIT | NAND_BBT_VERSION,
1398c2ecf20Sopenharmony_ci	.offs =	11,
1408c2ecf20Sopenharmony_ci	.len = 4,
1418c2ecf20Sopenharmony_ci	.veroffs = 15,
1428c2ecf20Sopenharmony_ci	.maxblocks = 4,
1438c2ecf20Sopenharmony_ci	.pattern = mirror_pattern,
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/*=================================*/
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * Set up the FCM hardware block and page address fields, and the fcm
1508c2ecf20Sopenharmony_ci * structure addr field to point to the correct FCM buffer in memory
1518c2ecf20Sopenharmony_ci */
1528c2ecf20Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
1558c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
1568c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
1578c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
1588c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
1598c2ecf20Sopenharmony_ci	int buf_num;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->page = page_addr;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (priv->page_size) {
1648c2ecf20Sopenharmony_ci		/*
1658c2ecf20Sopenharmony_ci		 * large page size chip : FPAR[PI] save the lowest 6 bits,
1668c2ecf20Sopenharmony_ci		 *                        FBAR[BLK] save the other bits.
1678c2ecf20Sopenharmony_ci		 */
1688c2ecf20Sopenharmony_ci		out_be32(&lbc->fbar, page_addr >> 6);
1698c2ecf20Sopenharmony_ci		out_be32(&lbc->fpar,
1708c2ecf20Sopenharmony_ci		         ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
1718c2ecf20Sopenharmony_ci		         (oob ? FPAR_LP_MS : 0) | column);
1728c2ecf20Sopenharmony_ci		buf_num = (page_addr & 1) << 2;
1738c2ecf20Sopenharmony_ci	} else {
1748c2ecf20Sopenharmony_ci		/*
1758c2ecf20Sopenharmony_ci		 * small page size chip : FPAR[PI] save the lowest 5 bits,
1768c2ecf20Sopenharmony_ci		 *                        FBAR[BLK] save the other bits.
1778c2ecf20Sopenharmony_ci		 */
1788c2ecf20Sopenharmony_ci		out_be32(&lbc->fbar, page_addr >> 5);
1798c2ecf20Sopenharmony_ci		out_be32(&lbc->fpar,
1808c2ecf20Sopenharmony_ci		         ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
1818c2ecf20Sopenharmony_ci		         (oob ? FPAR_SP_MS : 0) | column);
1828c2ecf20Sopenharmony_ci		buf_num = page_addr & 7;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
1868c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->index = column;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* for OOB data point to the second half of the buffer */
1898c2ecf20Sopenharmony_ci	if (oob)
1908c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	dev_vdbg(priv->dev, "set_addr: bank=%d, "
1938c2ecf20Sopenharmony_ci			    "elbc_fcm_ctrl->addr=0x%p (0x%p), "
1948c2ecf20Sopenharmony_ci	                    "index %x, pes %d ps %d\n",
1958c2ecf20Sopenharmony_ci		 buf_num, elbc_fcm_ctrl->addr, priv->vbase,
1968c2ecf20Sopenharmony_ci		 elbc_fcm_ctrl->index,
1978c2ecf20Sopenharmony_ci	         chip->phys_erase_shift, chip->page_shift);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/*
2018c2ecf20Sopenharmony_ci * execute FCM command and wait for it to complete
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_cistatic int fsl_elbc_run_command(struct mtd_info *mtd)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
2068c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
2078c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
2088c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
2098c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	/* Setup the FMR[OP] to execute without write protection */
2128c2ecf20Sopenharmony_ci	out_be32(&lbc->fmr, priv->fmr | 3);
2138c2ecf20Sopenharmony_ci	if (elbc_fcm_ctrl->use_mdr)
2148c2ecf20Sopenharmony_ci		out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	dev_vdbg(priv->dev,
2178c2ecf20Sopenharmony_ci	         "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
2188c2ecf20Sopenharmony_ci	         in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
2198c2ecf20Sopenharmony_ci	dev_vdbg(priv->dev,
2208c2ecf20Sopenharmony_ci	         "fsl_elbc_run_command: fbar=%08x fpar=%08x "
2218c2ecf20Sopenharmony_ci	         "fbcr=%08x bank=%d\n",
2228c2ecf20Sopenharmony_ci	         in_be32(&lbc->fbar), in_be32(&lbc->fpar),
2238c2ecf20Sopenharmony_ci	         in_be32(&lbc->fbcr), priv->bank);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ctrl->irq_status = 0;
2268c2ecf20Sopenharmony_ci	/* execute special operation */
2278c2ecf20Sopenharmony_ci	out_be32(&lbc->lsor, priv->bank);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/* wait for FCM complete flag or timeout */
2308c2ecf20Sopenharmony_ci	wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
2318c2ecf20Sopenharmony_ci	                   FCM_TIMEOUT_MSECS * HZ/1000);
2328c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->status = ctrl->irq_status;
2338c2ecf20Sopenharmony_ci	/* store mdr value in case it was needed */
2348c2ecf20Sopenharmony_ci	if (elbc_fcm_ctrl->use_mdr)
2358c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->use_mdr = 0;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (elbc_fcm_ctrl->status != LTESR_CC) {
2408c2ecf20Sopenharmony_ci		dev_info(priv->dev,
2418c2ecf20Sopenharmony_ci		         "command failed: fir %x fcr %x status %x mdr %x\n",
2428c2ecf20Sopenharmony_ci		         in_be32(&lbc->fir), in_be32(&lbc->fcr),
2438c2ecf20Sopenharmony_ci			 elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
2448c2ecf20Sopenharmony_ci		return -EIO;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
2488c2ecf20Sopenharmony_ci		return 0;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->max_bitflips = 0;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) {
2538c2ecf20Sopenharmony_ci		uint32_t lteccr = in_be32(&lbc->lteccr);
2548c2ecf20Sopenharmony_ci		/*
2558c2ecf20Sopenharmony_ci		 * if command was a full page read and the ELBC
2568c2ecf20Sopenharmony_ci		 * has the LTECCR register, then bits 12-15 (ppc order) of
2578c2ecf20Sopenharmony_ci		 * LTECCR indicates which 512 byte sub-pages had fixed errors.
2588c2ecf20Sopenharmony_ci		 * bits 28-31 are uncorrectable errors, marked elsewhere.
2598c2ecf20Sopenharmony_ci		 * for small page nand only 1 bit is used.
2608c2ecf20Sopenharmony_ci		 * if the ELBC doesn't have the lteccr register it reads 0
2618c2ecf20Sopenharmony_ci		 * FIXME: 4 bits can be corrected on NANDs with 2k pages, so
2628c2ecf20Sopenharmony_ci		 * count the number of sub-pages with bitflips and update
2638c2ecf20Sopenharmony_ci		 * ecc_stats.corrected accordingly.
2648c2ecf20Sopenharmony_ci		 */
2658c2ecf20Sopenharmony_ci		if (lteccr & 0x000F000F)
2668c2ecf20Sopenharmony_ci			out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */
2678c2ecf20Sopenharmony_ci		if (lteccr & 0x000F0000) {
2688c2ecf20Sopenharmony_ci			mtd->ecc_stats.corrected++;
2698c2ecf20Sopenharmony_ci			elbc_fcm_ctrl->max_bitflips = 1;
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic void fsl_elbc_do_read(struct nand_chip *chip, int oob)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
2798c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
2808c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (priv->page_size) {
2838c2ecf20Sopenharmony_ci		out_be32(&lbc->fir,
2848c2ecf20Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
2858c2ecf20Sopenharmony_ci		         (FIR_OP_CA  << FIR_OP1_SHIFT) |
2868c2ecf20Sopenharmony_ci		         (FIR_OP_PA  << FIR_OP2_SHIFT) |
2878c2ecf20Sopenharmony_ci		         (FIR_OP_CM1 << FIR_OP3_SHIFT) |
2888c2ecf20Sopenharmony_ci		         (FIR_OP_RBW << FIR_OP4_SHIFT));
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci		out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
2918c2ecf20Sopenharmony_ci		                    (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
2928c2ecf20Sopenharmony_ci	} else {
2938c2ecf20Sopenharmony_ci		out_be32(&lbc->fir,
2948c2ecf20Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
2958c2ecf20Sopenharmony_ci		         (FIR_OP_CA  << FIR_OP1_SHIFT) |
2968c2ecf20Sopenharmony_ci		         (FIR_OP_PA  << FIR_OP2_SHIFT) |
2978c2ecf20Sopenharmony_ci		         (FIR_OP_RBW << FIR_OP3_SHIFT));
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		if (oob)
3008c2ecf20Sopenharmony_ci			out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT);
3018c2ecf20Sopenharmony_ci		else
3028c2ecf20Sopenharmony_ci			out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci/* cmdfunc send commands to the FCM */
3078c2ecf20Sopenharmony_cistatic void fsl_elbc_cmdfunc(struct nand_chip *chip, unsigned int command,
3088c2ecf20Sopenharmony_ci                             int column, int page_addr)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
3118c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
3128c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
3138c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
3148c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->use_mdr = 0;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* clear the read buffer */
3198c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->read_bytes = 0;
3208c2ecf20Sopenharmony_ci	if (command != NAND_CMD_PAGEPROG)
3218c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->index = 0;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	switch (command) {
3248c2ecf20Sopenharmony_ci	/* READ0 and READ1 read the entire buffer to use hardware ECC. */
3258c2ecf20Sopenharmony_ci	case NAND_CMD_READ1:
3268c2ecf20Sopenharmony_ci		column += 256;
3278c2ecf20Sopenharmony_ci		fallthrough;
3288c2ecf20Sopenharmony_ci	case NAND_CMD_READ0:
3298c2ecf20Sopenharmony_ci		dev_dbg(priv->dev,
3308c2ecf20Sopenharmony_ci		        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
3318c2ecf20Sopenharmony_ci		        " 0x%x, column: 0x%x.\n", page_addr, column);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
3358c2ecf20Sopenharmony_ci		set_addr(mtd, 0, page_addr, 0);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
3388c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->index += column;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		fsl_elbc_do_read(chip, 0);
3418c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* RNDOUT moves the pointer inside the page */
3458c2ecf20Sopenharmony_ci	case NAND_CMD_RNDOUT:
3468c2ecf20Sopenharmony_ci		dev_dbg(priv->dev,
3478c2ecf20Sopenharmony_ci			"fsl_elbc_cmdfunc: NAND_CMD_RNDOUT, column: 0x%x.\n",
3488c2ecf20Sopenharmony_ci			column);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->index = column;
3518c2ecf20Sopenharmony_ci		return;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* READOOB reads only the OOB because no ECC is performed. */
3548c2ecf20Sopenharmony_ci	case NAND_CMD_READOOB:
3558c2ecf20Sopenharmony_ci		dev_vdbg(priv->dev,
3568c2ecf20Sopenharmony_ci		         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
3578c2ecf20Sopenharmony_ci			 " 0x%x, column: 0x%x.\n", page_addr, column);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		out_be32(&lbc->fbcr, mtd->oobsize - column);
3608c2ecf20Sopenharmony_ci		set_addr(mtd, column, page_addr, 1);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		fsl_elbc_do_read(chip, 1);
3658c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
3668c2ecf20Sopenharmony_ci		return;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	case NAND_CMD_READID:
3698c2ecf20Sopenharmony_ci	case NAND_CMD_PARAM:
3708c2ecf20Sopenharmony_ci		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
3738c2ecf20Sopenharmony_ci		                    (FIR_OP_UA  << FIR_OP1_SHIFT) |
3748c2ecf20Sopenharmony_ci		                    (FIR_OP_RBW << FIR_OP2_SHIFT));
3758c2ecf20Sopenharmony_ci		out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT);
3768c2ecf20Sopenharmony_ci		/*
3778c2ecf20Sopenharmony_ci		 * although currently it's 8 bytes for READID, we always read
3788c2ecf20Sopenharmony_ci		 * the maximum 256 bytes(for PARAM)
3798c2ecf20Sopenharmony_ci		 */
3808c2ecf20Sopenharmony_ci		out_be32(&lbc->fbcr, 256);
3818c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = 256;
3828c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->use_mdr = 1;
3838c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->mdr = column;
3848c2ecf20Sopenharmony_ci		set_addr(mtd, 0, 0, 0);
3858c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
3868c2ecf20Sopenharmony_ci		return;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* ERASE1 stores the block and page address */
3898c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE1:
3908c2ecf20Sopenharmony_ci		dev_vdbg(priv->dev,
3918c2ecf20Sopenharmony_ci		         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
3928c2ecf20Sopenharmony_ci		         "page_addr: 0x%x.\n", page_addr);
3938c2ecf20Sopenharmony_ci		set_addr(mtd, 0, page_addr, 0);
3948c2ecf20Sopenharmony_ci		return;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* ERASE2 uses the block and page address from ERASE1 */
3978c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE2:
3988c2ecf20Sopenharmony_ci		dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		out_be32(&lbc->fir,
4018c2ecf20Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
4028c2ecf20Sopenharmony_ci		         (FIR_OP_PA  << FIR_OP1_SHIFT) |
4038c2ecf20Sopenharmony_ci		         (FIR_OP_CM2 << FIR_OP2_SHIFT) |
4048c2ecf20Sopenharmony_ci		         (FIR_OP_CW1 << FIR_OP3_SHIFT) |
4058c2ecf20Sopenharmony_ci		         (FIR_OP_RS  << FIR_OP4_SHIFT));
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		out_be32(&lbc->fcr,
4088c2ecf20Sopenharmony_ci		         (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
4098c2ecf20Sopenharmony_ci		         (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
4108c2ecf20Sopenharmony_ci		         (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		out_be32(&lbc->fbcr, 0);
4138c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = 0;
4148c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->use_mdr = 1;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
4178c2ecf20Sopenharmony_ci		return;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* SEQIN sets up the addr buffer and all registers except the length */
4208c2ecf20Sopenharmony_ci	case NAND_CMD_SEQIN: {
4218c2ecf20Sopenharmony_ci		__be32 fcr;
4228c2ecf20Sopenharmony_ci		dev_vdbg(priv->dev,
4238c2ecf20Sopenharmony_ci			 "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
4248c2ecf20Sopenharmony_ci		         "page_addr: 0x%x, column: 0x%x.\n",
4258c2ecf20Sopenharmony_ci		         page_addr, column);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->column = column;
4288c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->use_mdr = 1;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		if (column >= mtd->writesize) {
4318c2ecf20Sopenharmony_ci			/* OOB area */
4328c2ecf20Sopenharmony_ci			column -= mtd->writesize;
4338c2ecf20Sopenharmony_ci			elbc_fcm_ctrl->oob = 1;
4348c2ecf20Sopenharmony_ci		} else {
4358c2ecf20Sopenharmony_ci			WARN_ON(column != 0);
4368c2ecf20Sopenharmony_ci			elbc_fcm_ctrl->oob = 0;
4378c2ecf20Sopenharmony_ci		}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		fcr = (NAND_CMD_STATUS   << FCR_CMD1_SHIFT) |
4408c2ecf20Sopenharmony_ci		      (NAND_CMD_SEQIN    << FCR_CMD2_SHIFT) |
4418c2ecf20Sopenharmony_ci		      (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		if (priv->page_size) {
4448c2ecf20Sopenharmony_ci			out_be32(&lbc->fir,
4458c2ecf20Sopenharmony_ci			         (FIR_OP_CM2 << FIR_OP0_SHIFT) |
4468c2ecf20Sopenharmony_ci			         (FIR_OP_CA  << FIR_OP1_SHIFT) |
4478c2ecf20Sopenharmony_ci			         (FIR_OP_PA  << FIR_OP2_SHIFT) |
4488c2ecf20Sopenharmony_ci			         (FIR_OP_WB  << FIR_OP3_SHIFT) |
4498c2ecf20Sopenharmony_ci			         (FIR_OP_CM3 << FIR_OP4_SHIFT) |
4508c2ecf20Sopenharmony_ci			         (FIR_OP_CW1 << FIR_OP5_SHIFT) |
4518c2ecf20Sopenharmony_ci			         (FIR_OP_RS  << FIR_OP6_SHIFT));
4528c2ecf20Sopenharmony_ci		} else {
4538c2ecf20Sopenharmony_ci			out_be32(&lbc->fir,
4548c2ecf20Sopenharmony_ci			         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
4558c2ecf20Sopenharmony_ci			         (FIR_OP_CM2 << FIR_OP1_SHIFT) |
4568c2ecf20Sopenharmony_ci			         (FIR_OP_CA  << FIR_OP2_SHIFT) |
4578c2ecf20Sopenharmony_ci			         (FIR_OP_PA  << FIR_OP3_SHIFT) |
4588c2ecf20Sopenharmony_ci			         (FIR_OP_WB  << FIR_OP4_SHIFT) |
4598c2ecf20Sopenharmony_ci			         (FIR_OP_CM3 << FIR_OP5_SHIFT) |
4608c2ecf20Sopenharmony_ci			         (FIR_OP_CW1 << FIR_OP6_SHIFT) |
4618c2ecf20Sopenharmony_ci			         (FIR_OP_RS  << FIR_OP7_SHIFT));
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci			if (elbc_fcm_ctrl->oob)
4648c2ecf20Sopenharmony_ci				/* OOB area --> READOOB */
4658c2ecf20Sopenharmony_ci				fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
4668c2ecf20Sopenharmony_ci			else
4678c2ecf20Sopenharmony_ci				/* First 256 bytes --> READ0 */
4688c2ecf20Sopenharmony_ci				fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
4698c2ecf20Sopenharmony_ci		}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		out_be32(&lbc->fcr, fcr);
4728c2ecf20Sopenharmony_ci		set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
4738c2ecf20Sopenharmony_ci		return;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
4778c2ecf20Sopenharmony_ci	case NAND_CMD_PAGEPROG: {
4788c2ecf20Sopenharmony_ci		dev_vdbg(priv->dev,
4798c2ecf20Sopenharmony_ci		         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
4808c2ecf20Sopenharmony_ci			 "writing %d bytes.\n", elbc_fcm_ctrl->index);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci		/* if the write did not start at 0 or is not a full page
4838c2ecf20Sopenharmony_ci		 * then set the exact length, otherwise use a full page
4848c2ecf20Sopenharmony_ci		 * write so the HW generates the ECC.
4858c2ecf20Sopenharmony_ci		 */
4868c2ecf20Sopenharmony_ci		if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
4878c2ecf20Sopenharmony_ci		    elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize)
4888c2ecf20Sopenharmony_ci			out_be32(&lbc->fbcr,
4898c2ecf20Sopenharmony_ci				elbc_fcm_ctrl->index - elbc_fcm_ctrl->column);
4908c2ecf20Sopenharmony_ci		else
4918c2ecf20Sopenharmony_ci			out_be32(&lbc->fbcr, 0);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
4948c2ecf20Sopenharmony_ci		return;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* CMD_STATUS must read the status byte while CEB is active */
4988c2ecf20Sopenharmony_ci	/* Note - it does not wait for the ready line */
4998c2ecf20Sopenharmony_ci	case NAND_CMD_STATUS:
5008c2ecf20Sopenharmony_ci		out_be32(&lbc->fir,
5018c2ecf20Sopenharmony_ci		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
5028c2ecf20Sopenharmony_ci		         (FIR_OP_RBW << FIR_OP1_SHIFT));
5038c2ecf20Sopenharmony_ci		out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
5048c2ecf20Sopenharmony_ci		out_be32(&lbc->fbcr, 1);
5058c2ecf20Sopenharmony_ci		set_addr(mtd, 0, 0, 0);
5068c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->read_bytes = 1;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		/* The chip always seems to report that it is
5118c2ecf20Sopenharmony_ci		 * write-protected, even when it is not.
5128c2ecf20Sopenharmony_ci		 */
5138c2ecf20Sopenharmony_ci		setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
5148c2ecf20Sopenharmony_ci		return;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* RESET without waiting for the ready line */
5178c2ecf20Sopenharmony_ci	case NAND_CMD_RESET:
5188c2ecf20Sopenharmony_ci		dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
5198c2ecf20Sopenharmony_ci		out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
5208c2ecf20Sopenharmony_ci		out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
5218c2ecf20Sopenharmony_ci		fsl_elbc_run_command(mtd);
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	default:
5258c2ecf20Sopenharmony_ci		dev_err(priv->dev,
5268c2ecf20Sopenharmony_ci		        "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
5278c2ecf20Sopenharmony_ci		        command);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void fsl_elbc_select_chip(struct nand_chip *chip, int cs)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	/* The hardware does not seem to support multiple
5348c2ecf20Sopenharmony_ci	 * chips per bank.
5358c2ecf20Sopenharmony_ci	 */
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci/*
5398c2ecf20Sopenharmony_ci * Write buf to the FCM Controller Data Buffer
5408c2ecf20Sopenharmony_ci */
5418c2ecf20Sopenharmony_cistatic void fsl_elbc_write_buf(struct nand_chip *chip, const u8 *buf, int len)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
5448c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
5458c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
5468c2ecf20Sopenharmony_ci	unsigned int bufsize = mtd->writesize + mtd->oobsize;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if (len <= 0) {
5498c2ecf20Sopenharmony_ci		dev_err(priv->dev, "write_buf of %d bytes", len);
5508c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->status = 0;
5518c2ecf20Sopenharmony_ci		return;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
5558c2ecf20Sopenharmony_ci		dev_err(priv->dev,
5568c2ecf20Sopenharmony_ci		        "write_buf beyond end of buffer "
5578c2ecf20Sopenharmony_ci		        "(%d requested, %u available)\n",
5588c2ecf20Sopenharmony_ci			len, bufsize - elbc_fcm_ctrl->index);
5598c2ecf20Sopenharmony_ci		len = bufsize - elbc_fcm_ctrl->index;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
5638c2ecf20Sopenharmony_ci	/*
5648c2ecf20Sopenharmony_ci	 * This is workaround for the weird elbc hangs during nand write,
5658c2ecf20Sopenharmony_ci	 * Scott Wood says: "...perhaps difference in how long it takes a
5668c2ecf20Sopenharmony_ci	 * write to make it through the localbus compared to a write to IMMR
5678c2ecf20Sopenharmony_ci	 * is causing problems, and sync isn't helping for some reason."
5688c2ecf20Sopenharmony_ci	 * Reading back the last byte helps though.
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->index += len;
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci/*
5768c2ecf20Sopenharmony_ci * read a byte from either the FCM hardware buffer if it has any data left
5778c2ecf20Sopenharmony_ci * otherwise issue a command to read a single byte.
5788c2ecf20Sopenharmony_ci */
5798c2ecf20Sopenharmony_cistatic u8 fsl_elbc_read_byte(struct nand_chip *chip)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
5828c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	/* If there are still bytes in the FCM, then use the next byte. */
5858c2ecf20Sopenharmony_ci	if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
5868c2ecf20Sopenharmony_ci		return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	dev_err(priv->dev, "read_byte beyond end of buffer\n");
5898c2ecf20Sopenharmony_ci	return ERR_BYTE;
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/*
5938c2ecf20Sopenharmony_ci * Read from the FCM Controller Data Buffer
5948c2ecf20Sopenharmony_ci */
5958c2ecf20Sopenharmony_cistatic void fsl_elbc_read_buf(struct nand_chip *chip, u8 *buf, int len)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
5988c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
5998c2ecf20Sopenharmony_ci	int avail;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (len < 0)
6028c2ecf20Sopenharmony_ci		return;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	avail = min((unsigned int)len,
6058c2ecf20Sopenharmony_ci			elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
6068c2ecf20Sopenharmony_ci	memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
6078c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->index += avail;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (len > avail)
6108c2ecf20Sopenharmony_ci		dev_err(priv->dev,
6118c2ecf20Sopenharmony_ci		        "read_buf beyond end of buffer "
6128c2ecf20Sopenharmony_ci		        "(%d requested, %d available)\n",
6138c2ecf20Sopenharmony_ci		        len, avail);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci/* This function is called after Program and Erase Operations to
6178c2ecf20Sopenharmony_ci * check for success or failure.
6188c2ecf20Sopenharmony_ci */
6198c2ecf20Sopenharmony_cistatic int fsl_elbc_wait(struct nand_chip *chip)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
6228c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (elbc_fcm_ctrl->status != LTESR_CC)
6258c2ecf20Sopenharmony_ci		return NAND_STATUS_FAIL;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* The chip always seems to report that it is
6288c2ecf20Sopenharmony_ci	 * write-protected, even when it is not.
6298c2ecf20Sopenharmony_ci	 */
6308c2ecf20Sopenharmony_ci	return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic int fsl_elbc_read_page(struct nand_chip *chip, uint8_t *buf,
6348c2ecf20Sopenharmony_ci			      int oob_required, int page)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6378c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
6388c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
6398c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
6428c2ecf20Sopenharmony_ci	if (oob_required)
6438c2ecf20Sopenharmony_ci		fsl_elbc_read_buf(chip, chip->oob_poi, mtd->oobsize);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (fsl_elbc_wait(chip) & NAND_STATUS_FAIL)
6468c2ecf20Sopenharmony_ci		mtd->ecc_stats.failed++;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return elbc_fcm_ctrl->max_bitflips;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in
6528c2ecf20Sopenharmony_ci * waitfunc.
6538c2ecf20Sopenharmony_ci */
6548c2ecf20Sopenharmony_cistatic int fsl_elbc_write_page(struct nand_chip *chip, const uint8_t *buf,
6558c2ecf20Sopenharmony_ci			       int oob_required, int page)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
6608c2ecf20Sopenharmony_ci	fsl_elbc_write_buf(chip, chip->oob_poi, mtd->oobsize);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in
6668c2ecf20Sopenharmony_ci * waitfunc.
6678c2ecf20Sopenharmony_ci */
6688c2ecf20Sopenharmony_cistatic int fsl_elbc_write_subpage(struct nand_chip *chip, uint32_t offset,
6698c2ecf20Sopenharmony_ci				  uint32_t data_len, const uint8_t *buf,
6708c2ecf20Sopenharmony_ci				  int oob_required, int page)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
6758c2ecf20Sopenharmony_ci	fsl_elbc_write_buf(chip, buf, mtd->writesize);
6768c2ecf20Sopenharmony_ci	fsl_elbc_write_buf(chip, chip->oob_poi, mtd->oobsize);
6778c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
6838c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
6848c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
6858c2ecf20Sopenharmony_ci	struct nand_chip *chip = &priv->chip;
6868c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* Fill in fsl_elbc_mtd structure */
6918c2ecf20Sopenharmony_ci	mtd->dev.parent = priv->dev;
6928c2ecf20Sopenharmony_ci	nand_set_flash_node(chip, priv->dev->of_node);
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	/* set timeout to maximum */
6958c2ecf20Sopenharmony_ci	priv->fmr = 15 << FMR_CWTO_SHIFT;
6968c2ecf20Sopenharmony_ci	if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS)
6978c2ecf20Sopenharmony_ci		priv->fmr |= FMR_ECCM;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	/* fill in nand_chip structure */
7008c2ecf20Sopenharmony_ci	/* set up function call table */
7018c2ecf20Sopenharmony_ci	chip->legacy.read_byte = fsl_elbc_read_byte;
7028c2ecf20Sopenharmony_ci	chip->legacy.write_buf = fsl_elbc_write_buf;
7038c2ecf20Sopenharmony_ci	chip->legacy.read_buf = fsl_elbc_read_buf;
7048c2ecf20Sopenharmony_ci	chip->legacy.select_chip = fsl_elbc_select_chip;
7058c2ecf20Sopenharmony_ci	chip->legacy.cmdfunc = fsl_elbc_cmdfunc;
7068c2ecf20Sopenharmony_ci	chip->legacy.waitfunc = fsl_elbc_wait;
7078c2ecf20Sopenharmony_ci	chip->legacy.set_features = nand_get_set_features_notsupp;
7088c2ecf20Sopenharmony_ci	chip->legacy.get_features = nand_get_set_features_notsupp;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	chip->bbt_td = &bbt_main_descr;
7118c2ecf20Sopenharmony_ci	chip->bbt_md = &bbt_mirror_descr;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	/* set up nand options */
7148c2ecf20Sopenharmony_ci	chip->bbt_options = NAND_BBT_USE_FLASH;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	chip->controller = &elbc_fcm_ctrl->controller;
7178c2ecf20Sopenharmony_ci	nand_set_controller_data(chip, priv);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	return 0;
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic int fsl_elbc_attach_chip(struct nand_chip *chip)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7258c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = nand_get_controller_data(chip);
7268c2ecf20Sopenharmony_ci	struct fsl_lbc_ctrl *ctrl = priv->ctrl;
7278c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
7288c2ecf20Sopenharmony_ci	unsigned int al;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/*
7318c2ecf20Sopenharmony_ci	 * if ECC was not chosen in DT, decide whether to use HW or SW ECC from
7328c2ecf20Sopenharmony_ci	 * CS Base Register
7338c2ecf20Sopenharmony_ci	 */
7348c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) {
7358c2ecf20Sopenharmony_ci		/* If CS Base Register selects full hardware ECC then use it */
7368c2ecf20Sopenharmony_ci		if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
7378c2ecf20Sopenharmony_ci		    BR_DECC_CHK_GEN) {
7388c2ecf20Sopenharmony_ci			chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
7398c2ecf20Sopenharmony_ci		} else {
7408c2ecf20Sopenharmony_ci			/* otherwise fall back to default software ECC */
7418c2ecf20Sopenharmony_ci			chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
7428c2ecf20Sopenharmony_ci			chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	switch (chip->ecc.engine_type) {
7478c2ecf20Sopenharmony_ci	/* if HW ECC was chosen, setup ecc and oob layout */
7488c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_HOST:
7498c2ecf20Sopenharmony_ci		chip->ecc.read_page = fsl_elbc_read_page;
7508c2ecf20Sopenharmony_ci		chip->ecc.write_page = fsl_elbc_write_page;
7518c2ecf20Sopenharmony_ci		chip->ecc.write_subpage = fsl_elbc_write_subpage;
7528c2ecf20Sopenharmony_ci		mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops);
7538c2ecf20Sopenharmony_ci		chip->ecc.size = 512;
7548c2ecf20Sopenharmony_ci		chip->ecc.bytes = 3;
7558c2ecf20Sopenharmony_ci		chip->ecc.strength = 1;
7568c2ecf20Sopenharmony_ci		break;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	/* if none or SW ECC was chosen, we do not need to set anything here */
7598c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_NONE:
7608c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_SOFT:
7618c2ecf20Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_DIE:
7628c2ecf20Sopenharmony_ci		break;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	default:
7658c2ecf20Sopenharmony_ci		return -EINVAL;
7668c2ecf20Sopenharmony_ci	}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* calculate FMR Address Length field */
7698c2ecf20Sopenharmony_ci	al = 0;
7708c2ecf20Sopenharmony_ci	if (chip->pagemask & 0xffff0000)
7718c2ecf20Sopenharmony_ci		al++;
7728c2ecf20Sopenharmony_ci	if (chip->pagemask & 0xff000000)
7738c2ecf20Sopenharmony_ci		al++;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	priv->fmr |= al << FMR_AL_SHIFT;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
7788c2ecf20Sopenharmony_ci	        nanddev_ntargets(&chip->base));
7798c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
7808c2ecf20Sopenharmony_ci	        nanddev_target_size(&chip->base));
7818c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
7828c2ecf20Sopenharmony_ci	        chip->pagemask);
7838c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->legacy.chip_delay = %d\n",
7848c2ecf20Sopenharmony_ci	        chip->legacy.chip_delay);
7858c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
7868c2ecf20Sopenharmony_ci	        chip->badblockpos);
7878c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
7888c2ecf20Sopenharmony_ci	        chip->chip_shift);
7898c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
7908c2ecf20Sopenharmony_ci	        chip->page_shift);
7918c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
7928c2ecf20Sopenharmony_ci	        chip->phys_erase_shift);
7938c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.engine_type = %d\n",
7948c2ecf20Sopenharmony_ci		chip->ecc.engine_type);
7958c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
7968c2ecf20Sopenharmony_ci	        chip->ecc.steps);
7978c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
7988c2ecf20Sopenharmony_ci	        chip->ecc.bytes);
7998c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
8008c2ecf20Sopenharmony_ci	        chip->ecc.total);
8018c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n",
8028c2ecf20Sopenharmony_ci		mtd->ooblayout);
8038c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
8048c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
8058c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
8068c2ecf20Sopenharmony_ci	        mtd->erasesize);
8078c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
8088c2ecf20Sopenharmony_ci	        mtd->writesize);
8098c2ecf20Sopenharmony_ci	dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
8108c2ecf20Sopenharmony_ci	        mtd->oobsize);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* adjust Option Register and ECC to match Flash page size */
8138c2ecf20Sopenharmony_ci	if (mtd->writesize == 512) {
8148c2ecf20Sopenharmony_ci		priv->page_size = 0;
8158c2ecf20Sopenharmony_ci		clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
8168c2ecf20Sopenharmony_ci	} else if (mtd->writesize == 2048) {
8178c2ecf20Sopenharmony_ci		priv->page_size = 1;
8188c2ecf20Sopenharmony_ci		setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
8198c2ecf20Sopenharmony_ci	} else {
8208c2ecf20Sopenharmony_ci		dev_err(priv->dev,
8218c2ecf20Sopenharmony_ci		        "fsl_elbc_init: page size %d is not supported\n",
8228c2ecf20Sopenharmony_ci		        mtd->writesize);
8238c2ecf20Sopenharmony_ci		return -ENOTSUPP;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	return 0;
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic const struct nand_controller_ops fsl_elbc_controller_ops = {
8308c2ecf20Sopenharmony_ci	.attach_chip = fsl_elbc_attach_chip,
8318c2ecf20Sopenharmony_ci};
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_cistatic int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
8368c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&priv->chip);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	kfree(mtd->name);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	if (priv->vbase)
8418c2ecf20Sopenharmony_ci		iounmap(priv->vbase);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->chips[priv->bank] = NULL;
8448c2ecf20Sopenharmony_ci	kfree(priv);
8458c2ecf20Sopenharmony_ci	return 0;
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fsl_elbc_nand_mutex);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic int fsl_elbc_nand_probe(struct platform_device *pdev)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	struct fsl_lbc_regs __iomem *lbc;
8538c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv;
8548c2ecf20Sopenharmony_ci	struct resource res;
8558c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
8568c2ecf20Sopenharmony_ci	static const char *part_probe_types[]
8578c2ecf20Sopenharmony_ci		= { "cmdlinepart", "RedBoot", "ofpart", NULL };
8588c2ecf20Sopenharmony_ci	int ret;
8598c2ecf20Sopenharmony_ci	int bank;
8608c2ecf20Sopenharmony_ci	struct device *dev;
8618c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
8628c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
8658c2ecf20Sopenharmony_ci		return -ENODEV;
8668c2ecf20Sopenharmony_ci	lbc = fsl_lbc_ctrl_dev->regs;
8678c2ecf20Sopenharmony_ci	dev = fsl_lbc_ctrl_dev->dev;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/* get, allocate and map the memory resource */
8708c2ecf20Sopenharmony_ci	ret = of_address_to_resource(node, 0, &res);
8718c2ecf20Sopenharmony_ci	if (ret) {
8728c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get resource\n");
8738c2ecf20Sopenharmony_ci		return ret;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* find which chip select it is connected to */
8778c2ecf20Sopenharmony_ci	for (bank = 0; bank < MAX_BANKS; bank++)
8788c2ecf20Sopenharmony_ci		if ((in_be32(&lbc->bank[bank].br) & BR_V) &&
8798c2ecf20Sopenharmony_ci		    (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
8808c2ecf20Sopenharmony_ci		    (in_be32(&lbc->bank[bank].br) &
8818c2ecf20Sopenharmony_ci		     in_be32(&lbc->bank[bank].or) & BR_BA)
8828c2ecf20Sopenharmony_ci		     == fsl_lbc_addr(res.start))
8838c2ecf20Sopenharmony_ci			break;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (bank >= MAX_BANKS) {
8868c2ecf20Sopenharmony_ci		dev_err(dev, "address did not match any chip selects\n");
8878c2ecf20Sopenharmony_ci		return -ENODEV;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
8918c2ecf20Sopenharmony_ci	if (!priv)
8928c2ecf20Sopenharmony_ci		return -ENOMEM;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	mutex_lock(&fsl_elbc_nand_mutex);
8958c2ecf20Sopenharmony_ci	if (!fsl_lbc_ctrl_dev->nand) {
8968c2ecf20Sopenharmony_ci		elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
8978c2ecf20Sopenharmony_ci		if (!elbc_fcm_ctrl) {
8988c2ecf20Sopenharmony_ci			mutex_unlock(&fsl_elbc_nand_mutex);
8998c2ecf20Sopenharmony_ci			ret = -ENOMEM;
9008c2ecf20Sopenharmony_ci			goto err;
9018c2ecf20Sopenharmony_ci		}
9028c2ecf20Sopenharmony_ci		elbc_fcm_ctrl->counter++;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		nand_controller_init(&elbc_fcm_ctrl->controller);
9058c2ecf20Sopenharmony_ci		fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
9068c2ecf20Sopenharmony_ci	} else {
9078c2ecf20Sopenharmony_ci		elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
9088c2ecf20Sopenharmony_ci	}
9098c2ecf20Sopenharmony_ci	mutex_unlock(&fsl_elbc_nand_mutex);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->chips[bank] = priv;
9128c2ecf20Sopenharmony_ci	priv->bank = bank;
9138c2ecf20Sopenharmony_ci	priv->ctrl = fsl_lbc_ctrl_dev;
9148c2ecf20Sopenharmony_ci	priv->dev = &pdev->dev;
9158c2ecf20Sopenharmony_ci	dev_set_drvdata(priv->dev, priv);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	priv->vbase = ioremap(res.start, resource_size(&res));
9188c2ecf20Sopenharmony_ci	if (!priv->vbase) {
9198c2ecf20Sopenharmony_ci		dev_err(dev, "failed to map chip region\n");
9208c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9218c2ecf20Sopenharmony_ci		goto err;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	mtd = nand_to_mtd(&priv->chip);
9258c2ecf20Sopenharmony_ci	mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
9268c2ecf20Sopenharmony_ci	if (!nand_to_mtd(&priv->chip)->name) {
9278c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9288c2ecf20Sopenharmony_ci		goto err;
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	ret = fsl_elbc_chip_init(priv);
9328c2ecf20Sopenharmony_ci	if (ret)
9338c2ecf20Sopenharmony_ci		goto err;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	priv->chip.controller->ops = &fsl_elbc_controller_ops;
9368c2ecf20Sopenharmony_ci	ret = nand_scan(&priv->chip, 1);
9378c2ecf20Sopenharmony_ci	if (ret)
9388c2ecf20Sopenharmony_ci		goto err;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	/* First look for RedBoot table or partitions on the command
9418c2ecf20Sopenharmony_ci	 * line, these take precedence over device tree information */
9428c2ecf20Sopenharmony_ci	ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0);
9438c2ecf20Sopenharmony_ci	if (ret)
9448c2ecf20Sopenharmony_ci		goto cleanup_nand;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	pr_info("eLBC NAND device at 0x%llx, bank %d\n",
9478c2ecf20Sopenharmony_ci		(unsigned long long)res.start, priv->bank);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	return 0;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cicleanup_nand:
9528c2ecf20Sopenharmony_ci	nand_cleanup(&priv->chip);
9538c2ecf20Sopenharmony_cierr:
9548c2ecf20Sopenharmony_ci	fsl_elbc_chip_remove(priv);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	return ret;
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_cistatic int fsl_elbc_nand_remove(struct platform_device *pdev)
9608c2ecf20Sopenharmony_ci{
9618c2ecf20Sopenharmony_ci	struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
9628c2ecf20Sopenharmony_ci	struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev);
9638c2ecf20Sopenharmony_ci	struct nand_chip *chip = &priv->chip;
9648c2ecf20Sopenharmony_ci	int ret;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
9678c2ecf20Sopenharmony_ci	WARN_ON(ret);
9688c2ecf20Sopenharmony_ci	nand_cleanup(chip);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	fsl_elbc_chip_remove(priv);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	mutex_lock(&fsl_elbc_nand_mutex);
9738c2ecf20Sopenharmony_ci	elbc_fcm_ctrl->counter--;
9748c2ecf20Sopenharmony_ci	if (!elbc_fcm_ctrl->counter) {
9758c2ecf20Sopenharmony_ci		fsl_lbc_ctrl_dev->nand = NULL;
9768c2ecf20Sopenharmony_ci		kfree(elbc_fcm_ctrl);
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci	mutex_unlock(&fsl_elbc_nand_mutex);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	return 0;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_elbc_nand_match[] = {
9858c2ecf20Sopenharmony_ci	{ .compatible = "fsl,elbc-fcm-nand", },
9868c2ecf20Sopenharmony_ci	{}
9878c2ecf20Sopenharmony_ci};
9888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_elbc_nand_match);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_cistatic struct platform_driver fsl_elbc_nand_driver = {
9918c2ecf20Sopenharmony_ci	.driver = {
9928c2ecf20Sopenharmony_ci		.name = "fsl,elbc-fcm-nand",
9938c2ecf20Sopenharmony_ci		.of_match_table = fsl_elbc_nand_match,
9948c2ecf20Sopenharmony_ci	},
9958c2ecf20Sopenharmony_ci	.probe = fsl_elbc_nand_probe,
9968c2ecf20Sopenharmony_ci	.remove = fsl_elbc_nand_remove,
9978c2ecf20Sopenharmony_ci};
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cimodule_platform_driver(fsl_elbc_nand_driver);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
10028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale");
10038c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver");
1004