162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for NAND MLC Controller in LPC32xx
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Roland Stigge <stigge@antcom.de>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright © 2011 WORK Microwave GmbH
862306a36Sopenharmony_ci * Copyright © 2011, 2012 Roland Stigge
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * NAND Flash Controller Operation:
1162306a36Sopenharmony_ci * - Read: Auto Decode
1262306a36Sopenharmony_ci * - Write: Auto Encode
1362306a36Sopenharmony_ci * - Tested Page Sizes: 2048, 4096
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/module.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2062306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
2162306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
2262306a36Sopenharmony_ci#include <linux/clk.h>
2362306a36Sopenharmony_ci#include <linux/err.h>
2462306a36Sopenharmony_ci#include <linux/delay.h>
2562306a36Sopenharmony_ci#include <linux/completion.h>
2662306a36Sopenharmony_ci#include <linux/interrupt.h>
2762306a36Sopenharmony_ci#include <linux/of.h>
2862306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
2962306a36Sopenharmony_ci#include <linux/mtd/lpc32xx_mlc.h>
3062306a36Sopenharmony_ci#include <linux/io.h>
3162306a36Sopenharmony_ci#include <linux/mm.h>
3262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3362306a36Sopenharmony_ci#include <linux/dmaengine.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRV_NAME "lpc32xx_mlc"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/**********************************************************************
3862306a36Sopenharmony_ci* MLC NAND controller register offsets
3962306a36Sopenharmony_ci**********************************************************************/
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define MLC_BUFF(x)			(x + 0x00000)
4262306a36Sopenharmony_ci#define MLC_DATA(x)			(x + 0x08000)
4362306a36Sopenharmony_ci#define MLC_CMD(x)			(x + 0x10000)
4462306a36Sopenharmony_ci#define MLC_ADDR(x)			(x + 0x10004)
4562306a36Sopenharmony_ci#define MLC_ECC_ENC_REG(x)		(x + 0x10008)
4662306a36Sopenharmony_ci#define MLC_ECC_DEC_REG(x)		(x + 0x1000C)
4762306a36Sopenharmony_ci#define MLC_ECC_AUTO_ENC_REG(x)		(x + 0x10010)
4862306a36Sopenharmony_ci#define MLC_ECC_AUTO_DEC_REG(x)		(x + 0x10014)
4962306a36Sopenharmony_ci#define MLC_RPR(x)			(x + 0x10018)
5062306a36Sopenharmony_ci#define MLC_WPR(x)			(x + 0x1001C)
5162306a36Sopenharmony_ci#define MLC_RUBP(x)			(x + 0x10020)
5262306a36Sopenharmony_ci#define MLC_ROBP(x)			(x + 0x10024)
5362306a36Sopenharmony_ci#define MLC_SW_WP_ADD_LOW(x)		(x + 0x10028)
5462306a36Sopenharmony_ci#define MLC_SW_WP_ADD_HIG(x)		(x + 0x1002C)
5562306a36Sopenharmony_ci#define MLC_ICR(x)			(x + 0x10030)
5662306a36Sopenharmony_ci#define MLC_TIME_REG(x)			(x + 0x10034)
5762306a36Sopenharmony_ci#define MLC_IRQ_MR(x)			(x + 0x10038)
5862306a36Sopenharmony_ci#define MLC_IRQ_SR(x)			(x + 0x1003C)
5962306a36Sopenharmony_ci#define MLC_LOCK_PR(x)			(x + 0x10044)
6062306a36Sopenharmony_ci#define MLC_ISR(x)			(x + 0x10048)
6162306a36Sopenharmony_ci#define MLC_CEH(x)			(x + 0x1004C)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/**********************************************************************
6462306a36Sopenharmony_ci* MLC_CMD bit definitions
6562306a36Sopenharmony_ci**********************************************************************/
6662306a36Sopenharmony_ci#define MLCCMD_RESET			0xFF
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/**********************************************************************
6962306a36Sopenharmony_ci* MLC_ICR bit definitions
7062306a36Sopenharmony_ci**********************************************************************/
7162306a36Sopenharmony_ci#define MLCICR_WPROT			(1 << 3)
7262306a36Sopenharmony_ci#define MLCICR_LARGEBLOCK		(1 << 2)
7362306a36Sopenharmony_ci#define MLCICR_LONGADDR			(1 << 1)
7462306a36Sopenharmony_ci#define MLCICR_16BIT			(1 << 0)  /* unsupported by LPC32x0! */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/**********************************************************************
7762306a36Sopenharmony_ci* MLC_TIME_REG bit definitions
7862306a36Sopenharmony_ci**********************************************************************/
7962306a36Sopenharmony_ci#define MLCTIMEREG_TCEA_DELAY(n)	(((n) & 0x03) << 24)
8062306a36Sopenharmony_ci#define MLCTIMEREG_BUSY_DELAY(n)	(((n) & 0x1F) << 19)
8162306a36Sopenharmony_ci#define MLCTIMEREG_NAND_TA(n)		(((n) & 0x07) << 16)
8262306a36Sopenharmony_ci#define MLCTIMEREG_RD_HIGH(n)		(((n) & 0x0F) << 12)
8362306a36Sopenharmony_ci#define MLCTIMEREG_RD_LOW(n)		(((n) & 0x0F) << 8)
8462306a36Sopenharmony_ci#define MLCTIMEREG_WR_HIGH(n)		(((n) & 0x0F) << 4)
8562306a36Sopenharmony_ci#define MLCTIMEREG_WR_LOW(n)		(((n) & 0x0F) << 0)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/**********************************************************************
8862306a36Sopenharmony_ci* MLC_IRQ_MR and MLC_IRQ_SR bit definitions
8962306a36Sopenharmony_ci**********************************************************************/
9062306a36Sopenharmony_ci#define MLCIRQ_NAND_READY		(1 << 5)
9162306a36Sopenharmony_ci#define MLCIRQ_CONTROLLER_READY		(1 << 4)
9262306a36Sopenharmony_ci#define MLCIRQ_DECODE_FAILURE		(1 << 3)
9362306a36Sopenharmony_ci#define MLCIRQ_DECODE_ERROR		(1 << 2)
9462306a36Sopenharmony_ci#define MLCIRQ_ECC_READY		(1 << 1)
9562306a36Sopenharmony_ci#define MLCIRQ_WRPROT_FAULT		(1 << 0)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/**********************************************************************
9862306a36Sopenharmony_ci* MLC_LOCK_PR bit definitions
9962306a36Sopenharmony_ci**********************************************************************/
10062306a36Sopenharmony_ci#define MLCLOCKPR_MAGIC			0xA25E
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/**********************************************************************
10362306a36Sopenharmony_ci* MLC_ISR bit definitions
10462306a36Sopenharmony_ci**********************************************************************/
10562306a36Sopenharmony_ci#define MLCISR_DECODER_FAILURE		(1 << 6)
10662306a36Sopenharmony_ci#define MLCISR_ERRORS			((1 << 4) | (1 << 5))
10762306a36Sopenharmony_ci#define MLCISR_ERRORS_DETECTED		(1 << 3)
10862306a36Sopenharmony_ci#define MLCISR_ECC_READY		(1 << 2)
10962306a36Sopenharmony_ci#define MLCISR_CONTROLLER_READY		(1 << 1)
11062306a36Sopenharmony_ci#define MLCISR_NAND_READY		(1 << 0)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/**********************************************************************
11362306a36Sopenharmony_ci* MLC_CEH bit definitions
11462306a36Sopenharmony_ci**********************************************************************/
11562306a36Sopenharmony_ci#define MLCCEH_NORMAL			(1 << 0)
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct lpc32xx_nand_cfg_mlc {
11862306a36Sopenharmony_ci	uint32_t tcea_delay;
11962306a36Sopenharmony_ci	uint32_t busy_delay;
12062306a36Sopenharmony_ci	uint32_t nand_ta;
12162306a36Sopenharmony_ci	uint32_t rd_high;
12262306a36Sopenharmony_ci	uint32_t rd_low;
12362306a36Sopenharmony_ci	uint32_t wr_high;
12462306a36Sopenharmony_ci	uint32_t wr_low;
12562306a36Sopenharmony_ci	struct mtd_partition *parts;
12662306a36Sopenharmony_ci	unsigned num_parts;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section,
13062306a36Sopenharmony_ci				 struct mtd_oob_region *oobregion)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
13562306a36Sopenharmony_ci		return -ERANGE;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes;
13862306a36Sopenharmony_ci	oobregion->length = nand_chip->ecc.bytes;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section,
14462306a36Sopenharmony_ci				  struct mtd_oob_region *oobregion)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
14962306a36Sopenharmony_ci		return -ERANGE;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	oobregion->offset = 16 * section;
15262306a36Sopenharmony_ci	oobregion->length = 16 - nand_chip->ecc.bytes;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = {
15862306a36Sopenharmony_ci	.ecc = lpc32xx_ooblayout_ecc,
15962306a36Sopenharmony_ci	.free = lpc32xx_ooblayout_free,
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct nand_bbt_descr lpc32xx_nand_bbt = {
16362306a36Sopenharmony_ci	.options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB |
16462306a36Sopenharmony_ci		   NAND_BBT_WRITE,
16562306a36Sopenharmony_ci	.pages = { 524224, 0, 0, 0, 0, 0, 0, 0 },
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic struct nand_bbt_descr lpc32xx_nand_bbt_mirror = {
16962306a36Sopenharmony_ci	.options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB |
17062306a36Sopenharmony_ci		   NAND_BBT_WRITE,
17162306a36Sopenharmony_ci	.pages = { 524160, 0, 0, 0, 0, 0, 0, 0 },
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistruct lpc32xx_nand_host {
17562306a36Sopenharmony_ci	struct platform_device	*pdev;
17662306a36Sopenharmony_ci	struct nand_chip	nand_chip;
17762306a36Sopenharmony_ci	struct lpc32xx_mlc_platform_data *pdata;
17862306a36Sopenharmony_ci	struct clk		*clk;
17962306a36Sopenharmony_ci	struct gpio_desc	*wp_gpio;
18062306a36Sopenharmony_ci	void __iomem		*io_base;
18162306a36Sopenharmony_ci	int			irq;
18262306a36Sopenharmony_ci	struct lpc32xx_nand_cfg_mlc	*ncfg;
18362306a36Sopenharmony_ci	struct completion       comp_nand;
18462306a36Sopenharmony_ci	struct completion       comp_controller;
18562306a36Sopenharmony_ci	uint32_t llptr;
18662306a36Sopenharmony_ci	/*
18762306a36Sopenharmony_ci	 * Physical addresses of ECC buffer, DMA data buffers, OOB data buffer
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	dma_addr_t		oob_buf_phy;
19062306a36Sopenharmony_ci	/*
19162306a36Sopenharmony_ci	 * Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer
19262306a36Sopenharmony_ci	 */
19362306a36Sopenharmony_ci	uint8_t			*oob_buf;
19462306a36Sopenharmony_ci	/* Physical address of DMA base address */
19562306a36Sopenharmony_ci	dma_addr_t		io_base_phy;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	struct completion	comp_dma;
19862306a36Sopenharmony_ci	struct dma_chan		*dma_chan;
19962306a36Sopenharmony_ci	struct dma_slave_config	dma_slave_config;
20062306a36Sopenharmony_ci	struct scatterlist	sgl;
20162306a36Sopenharmony_ci	uint8_t			*dma_buf;
20262306a36Sopenharmony_ci	uint8_t			*dummy_buf;
20362306a36Sopenharmony_ci	int			mlcsubpages; /* number of 512bytes-subpages */
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/*
20762306a36Sopenharmony_ci * Activate/Deactivate DMA Operation:
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * Using the PL080 DMA Controller for transferring the 512 byte subpages
21062306a36Sopenharmony_ci * instead of doing readl() / writel() in a loop slows it down significantly.
21162306a36Sopenharmony_ci * Measurements via getnstimeofday() upon 512 byte subpage reads reveal:
21262306a36Sopenharmony_ci *
21362306a36Sopenharmony_ci * - readl() of 128 x 32 bits in a loop: ~20us
21462306a36Sopenharmony_ci * - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us
21562306a36Sopenharmony_ci * - DMA read of 512 bytes (32 bit, no bursts): ~100us
21662306a36Sopenharmony_ci *
21762306a36Sopenharmony_ci * This applies to the transfer itself. In the DMA case: only the
21862306a36Sopenharmony_ci * wait_for_completion() (DMA setup _not_ included).
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * Note that the 512 bytes subpage transfer is done directly from/to a
22162306a36Sopenharmony_ci * FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a
22262306a36Sopenharmony_ci * 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND
22362306a36Sopenharmony_ci * controller transferring data between its internal buffer to/from the NAND
22462306a36Sopenharmony_ci * chip.)
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * Therefore, using the PL080 DMA is disabled by default, for now.
22762306a36Sopenharmony_ci *
22862306a36Sopenharmony_ci */
22962306a36Sopenharmony_cistatic int use_dma;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic void lpc32xx_nand_setup(struct lpc32xx_nand_host *host)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	uint32_t clkrate, tmp;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Reset MLC controller */
23662306a36Sopenharmony_ci	writel(MLCCMD_RESET, MLC_CMD(host->io_base));
23762306a36Sopenharmony_ci	udelay(1000);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Get base clock for MLC block */
24062306a36Sopenharmony_ci	clkrate = clk_get_rate(host->clk);
24162306a36Sopenharmony_ci	if (clkrate == 0)
24262306a36Sopenharmony_ci		clkrate = 104000000;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Unlock MLC_ICR
24562306a36Sopenharmony_ci	 * (among others, will be locked again automatically) */
24662306a36Sopenharmony_ci	writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base));
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Configure MLC Controller: Large Block, 5 Byte Address */
24962306a36Sopenharmony_ci	tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR;
25062306a36Sopenharmony_ci	writel(tmp, MLC_ICR(host->io_base));
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Unlock MLC_TIME_REG
25362306a36Sopenharmony_ci	 * (among others, will be locked again automatically) */
25462306a36Sopenharmony_ci	writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base));
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Compute clock setup values, see LPC and NAND manual */
25762306a36Sopenharmony_ci	tmp = 0;
25862306a36Sopenharmony_ci	tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1);
25962306a36Sopenharmony_ci	tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1);
26062306a36Sopenharmony_ci	tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1);
26162306a36Sopenharmony_ci	tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1);
26262306a36Sopenharmony_ci	tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low);
26362306a36Sopenharmony_ci	tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1);
26462306a36Sopenharmony_ci	tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low);
26562306a36Sopenharmony_ci	writel(tmp, MLC_TIME_REG(host->io_base));
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* Enable IRQ for CONTROLLER_READY and NAND_READY */
26862306a36Sopenharmony_ci	writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY,
26962306a36Sopenharmony_ci			MLC_IRQ_MR(host->io_base));
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Normal nCE operation: nCE controlled by controller */
27262306a36Sopenharmony_ci	writel(MLCCEH_NORMAL, MLC_CEH(host->io_base));
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/*
27662306a36Sopenharmony_ci * Hardware specific access to control lines
27762306a36Sopenharmony_ci */
27862306a36Sopenharmony_cistatic void lpc32xx_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd,
27962306a36Sopenharmony_ci				  unsigned int ctrl)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (cmd != NAND_CMD_NONE) {
28462306a36Sopenharmony_ci		if (ctrl & NAND_CLE)
28562306a36Sopenharmony_ci			writel(cmd, MLC_CMD(host->io_base));
28662306a36Sopenharmony_ci		else
28762306a36Sopenharmony_ci			writel(cmd, MLC_ADDR(host->io_base));
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/*
29262306a36Sopenharmony_ci * Read Device Ready (NAND device _and_ controller ready)
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic int lpc32xx_nand_device_ready(struct nand_chip *nand_chip)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if ((readb(MLC_ISR(host->io_base)) &
29962306a36Sopenharmony_ci	     (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) ==
30062306a36Sopenharmony_ci	    (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY))
30162306a36Sopenharmony_ci		return  1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic irqreturn_t lpc3xxx_nand_irq(int irq, void *data)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = data;
30962306a36Sopenharmony_ci	uint8_t sr;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	/* Clear interrupt flag by reading status */
31262306a36Sopenharmony_ci	sr = readb(MLC_IRQ_SR(host->io_base));
31362306a36Sopenharmony_ci	if (sr & MLCIRQ_NAND_READY)
31462306a36Sopenharmony_ci		complete(&host->comp_nand);
31562306a36Sopenharmony_ci	if (sr & MLCIRQ_CONTROLLER_READY)
31662306a36Sopenharmony_ci		complete(&host->comp_controller);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return IRQ_HANDLED;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int lpc32xx_waitfunc_nand(struct nand_chip *chip)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
32462306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)
32762306a36Sopenharmony_ci		goto exit;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	wait_for_completion(&host->comp_nand);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) {
33262306a36Sopenharmony_ci		/* Seems to be delayed sometimes by controller */
33362306a36Sopenharmony_ci		dev_dbg(&mtd->dev, "Warning: NAND not ready.\n");
33462306a36Sopenharmony_ci		cpu_relax();
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ciexit:
33862306a36Sopenharmony_ci	return NAND_STATUS_READY;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int lpc32xx_waitfunc_controller(struct nand_chip *chip)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
34462306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY)
34762306a36Sopenharmony_ci		goto exit;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	wait_for_completion(&host->comp_controller);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	while (!(readb(MLC_ISR(host->io_base)) &
35262306a36Sopenharmony_ci		 MLCISR_CONTROLLER_READY)) {
35362306a36Sopenharmony_ci		dev_dbg(&mtd->dev, "Warning: Controller not ready.\n");
35462306a36Sopenharmony_ci		cpu_relax();
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ciexit:
35862306a36Sopenharmony_ci	return NAND_STATUS_READY;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int lpc32xx_waitfunc(struct nand_chip *chip)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	lpc32xx_waitfunc_nand(chip);
36462306a36Sopenharmony_ci	lpc32xx_waitfunc_controller(chip);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return NAND_STATUS_READY;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*
37062306a36Sopenharmony_ci * Enable NAND write protect
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	if (host->wp_gpio)
37562306a36Sopenharmony_ci		gpiod_set_value_cansleep(host->wp_gpio, 1);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*
37962306a36Sopenharmony_ci * Disable NAND write protect
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_cistatic void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	if (host->wp_gpio)
38462306a36Sopenharmony_ci		gpiod_set_value_cansleep(host->wp_gpio, 0);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_cistatic void lpc32xx_dma_complete_func(void *completion)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	complete(completion);
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
39362306a36Sopenharmony_ci			    enum dma_transfer_direction dir)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
39662306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
39762306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
39862306a36Sopenharmony_ci	int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
39962306a36Sopenharmony_ci	int res;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	sg_init_one(&host->sgl, mem, len);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1,
40462306a36Sopenharmony_ci			 DMA_BIDIRECTIONAL);
40562306a36Sopenharmony_ci	if (res != 1) {
40662306a36Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to map sg list\n");
40762306a36Sopenharmony_ci		return -ENXIO;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci	desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir,
41062306a36Sopenharmony_ci				       flags);
41162306a36Sopenharmony_ci	if (!desc) {
41262306a36Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
41362306a36Sopenharmony_ci		goto out1;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	init_completion(&host->comp_dma);
41762306a36Sopenharmony_ci	desc->callback = lpc32xx_dma_complete_func;
41862306a36Sopenharmony_ci	desc->callback_param = &host->comp_dma;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	dmaengine_submit(desc);
42162306a36Sopenharmony_ci	dma_async_issue_pending(host->dma_chan);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
42662306a36Sopenharmony_ci		     DMA_BIDIRECTIONAL);
42762306a36Sopenharmony_ci	return 0;
42862306a36Sopenharmony_ciout1:
42962306a36Sopenharmony_ci	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
43062306a36Sopenharmony_ci		     DMA_BIDIRECTIONAL);
43162306a36Sopenharmony_ci	return -ENXIO;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf,
43562306a36Sopenharmony_ci			     int oob_required, int page)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
43862306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
43962306a36Sopenharmony_ci	int i, j;
44062306a36Sopenharmony_ci	uint8_t *oobbuf = chip->oob_poi;
44162306a36Sopenharmony_ci	uint32_t mlc_isr;
44262306a36Sopenharmony_ci	int res;
44362306a36Sopenharmony_ci	uint8_t *dma_buf;
44462306a36Sopenharmony_ci	bool dma_mapped;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if ((void *)buf <= high_memory) {
44762306a36Sopenharmony_ci		dma_buf = buf;
44862306a36Sopenharmony_ci		dma_mapped = true;
44962306a36Sopenharmony_ci	} else {
45062306a36Sopenharmony_ci		dma_buf = host->dma_buf;
45162306a36Sopenharmony_ci		dma_mapped = false;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* Writing Command and Address */
45562306a36Sopenharmony_ci	nand_read_page_op(chip, page, 0, NULL, 0);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* For all sub-pages */
45862306a36Sopenharmony_ci	for (i = 0; i < host->mlcsubpages; i++) {
45962306a36Sopenharmony_ci		/* Start Auto Decode Command */
46062306a36Sopenharmony_ci		writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base));
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		/* Wait for Controller Ready */
46362306a36Sopenharmony_ci		lpc32xx_waitfunc_controller(chip);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		/* Check ECC Error status */
46662306a36Sopenharmony_ci		mlc_isr = readl(MLC_ISR(host->io_base));
46762306a36Sopenharmony_ci		if (mlc_isr & MLCISR_DECODER_FAILURE) {
46862306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
46962306a36Sopenharmony_ci			dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__);
47062306a36Sopenharmony_ci		} else if (mlc_isr & MLCISR_ERRORS_DETECTED) {
47162306a36Sopenharmony_ci			mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		/* Read 512 + 16 Bytes */
47562306a36Sopenharmony_ci		if (use_dma) {
47662306a36Sopenharmony_ci			res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512,
47762306a36Sopenharmony_ci					       DMA_DEV_TO_MEM);
47862306a36Sopenharmony_ci			if (res)
47962306a36Sopenharmony_ci				return res;
48062306a36Sopenharmony_ci		} else {
48162306a36Sopenharmony_ci			for (j = 0; j < (512 >> 2); j++) {
48262306a36Sopenharmony_ci				*((uint32_t *)(buf)) =
48362306a36Sopenharmony_ci					readl(MLC_BUFF(host->io_base));
48462306a36Sopenharmony_ci				buf += 4;
48562306a36Sopenharmony_ci			}
48662306a36Sopenharmony_ci		}
48762306a36Sopenharmony_ci		for (j = 0; j < (16 >> 2); j++) {
48862306a36Sopenharmony_ci			*((uint32_t *)(oobbuf)) =
48962306a36Sopenharmony_ci				readl(MLC_BUFF(host->io_base));
49062306a36Sopenharmony_ci			oobbuf += 4;
49162306a36Sopenharmony_ci		}
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (use_dma && !dma_mapped)
49562306a36Sopenharmony_ci		memcpy(buf, dma_buf, mtd->writesize);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int lpc32xx_write_page_lowlevel(struct nand_chip *chip,
50162306a36Sopenharmony_ci				       const uint8_t *buf, int oob_required,
50262306a36Sopenharmony_ci				       int page)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
50562306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
50662306a36Sopenharmony_ci	const uint8_t *oobbuf = chip->oob_poi;
50762306a36Sopenharmony_ci	uint8_t *dma_buf = (uint8_t *)buf;
50862306a36Sopenharmony_ci	int res;
50962306a36Sopenharmony_ci	int i, j;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (use_dma && (void *)buf >= high_memory) {
51262306a36Sopenharmony_ci		dma_buf = host->dma_buf;
51362306a36Sopenharmony_ci		memcpy(dma_buf, buf, mtd->writesize);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	for (i = 0; i < host->mlcsubpages; i++) {
51962306a36Sopenharmony_ci		/* Start Encode */
52062306a36Sopenharmony_ci		writeb(0x00, MLC_ECC_ENC_REG(host->io_base));
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci		/* Write 512 + 6 Bytes to Buffer */
52362306a36Sopenharmony_ci		if (use_dma) {
52462306a36Sopenharmony_ci			res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512,
52562306a36Sopenharmony_ci					       DMA_MEM_TO_DEV);
52662306a36Sopenharmony_ci			if (res)
52762306a36Sopenharmony_ci				return res;
52862306a36Sopenharmony_ci		} else {
52962306a36Sopenharmony_ci			for (j = 0; j < (512 >> 2); j++) {
53062306a36Sopenharmony_ci				writel(*((uint32_t *)(buf)),
53162306a36Sopenharmony_ci				       MLC_BUFF(host->io_base));
53262306a36Sopenharmony_ci				buf += 4;
53362306a36Sopenharmony_ci			}
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci		writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base));
53662306a36Sopenharmony_ci		oobbuf += 4;
53762306a36Sopenharmony_ci		writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base));
53862306a36Sopenharmony_ci		oobbuf += 12;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci		/* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */
54162306a36Sopenharmony_ci		writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base));
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci		/* Wait for Controller Ready */
54462306a36Sopenharmony_ci		lpc32xx_waitfunc_controller(chip);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return nand_prog_page_end_op(chip);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int lpc32xx_read_oob(struct nand_chip *chip, int page)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Read whole page - necessary with MLC controller! */
55562306a36Sopenharmony_ci	lpc32xx_read_page(chip, host->dummy_buf, 1, page);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return 0;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int lpc32xx_write_oob(struct nand_chip *chip, int page)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	/* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */
56762306a36Sopenharmony_cistatic void lpc32xx_ecc_enable(struct nand_chip *chip, int mode)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	/* Always enabled! */
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int lpc32xx_dma_setup(struct lpc32xx_nand_host *host)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
57562306a36Sopenharmony_ci	dma_cap_mask_t mask;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (!host->pdata || !host->pdata->dma_filter) {
57862306a36Sopenharmony_ci		dev_err(mtd->dev.parent, "no DMA platform data\n");
57962306a36Sopenharmony_ci		return -ENOENT;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	dma_cap_zero(mask);
58362306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
58462306a36Sopenharmony_ci	host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter,
58562306a36Sopenharmony_ci					     "nand-mlc");
58662306a36Sopenharmony_ci	if (!host->dma_chan) {
58762306a36Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
58862306a36Sopenharmony_ci		return -EBUSY;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/*
59262306a36Sopenharmony_ci	 * Set direction to a sensible value even if the dmaengine driver
59362306a36Sopenharmony_ci	 * should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x
59462306a36Sopenharmony_ci	 * driver criticizes it as "alien transfer direction".
59562306a36Sopenharmony_ci	 */
59662306a36Sopenharmony_ci	host->dma_slave_config.direction = DMA_DEV_TO_MEM;
59762306a36Sopenharmony_ci	host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
59862306a36Sopenharmony_ci	host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
59962306a36Sopenharmony_ci	host->dma_slave_config.src_maxburst = 128;
60062306a36Sopenharmony_ci	host->dma_slave_config.dst_maxburst = 128;
60162306a36Sopenharmony_ci	/* DMA controller does flow control: */
60262306a36Sopenharmony_ci	host->dma_slave_config.device_fc = false;
60362306a36Sopenharmony_ci	host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy);
60462306a36Sopenharmony_ci	host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy);
60562306a36Sopenharmony_ci	if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) {
60662306a36Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to setup DMA slave\n");
60762306a36Sopenharmony_ci		goto out1;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return 0;
61162306a36Sopenharmony_ciout1:
61262306a36Sopenharmony_ci	dma_release_channel(host->dma_chan);
61362306a36Sopenharmony_ci	return -ENXIO;
61462306a36Sopenharmony_ci}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct lpc32xx_nand_cfg_mlc *ncfg;
61962306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL);
62262306a36Sopenharmony_ci	if (!ncfg)
62362306a36Sopenharmony_ci		return NULL;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay);
62662306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay);
62762306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta);
62862306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high);
62962306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low);
63062306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high);
63162306a36Sopenharmony_ci	of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta ||
63462306a36Sopenharmony_ci	    !ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high ||
63562306a36Sopenharmony_ci	    !ncfg->wr_low) {
63662306a36Sopenharmony_ci		dev_err(dev, "chip parameters not specified correctly\n");
63762306a36Sopenharmony_ci		return NULL;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return ncfg;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int lpc32xx_nand_attach_chip(struct nand_chip *chip)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
64662306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
64762306a36Sopenharmony_ci	struct device *dev = &host->pdev->dev;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
65062306a36Sopenharmony_ci		return 0;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	host->dma_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL);
65362306a36Sopenharmony_ci	if (!host->dma_buf)
65462306a36Sopenharmony_ci		return -ENOMEM;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	host->dummy_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL);
65762306a36Sopenharmony_ci	if (!host->dummy_buf)
65862306a36Sopenharmony_ci		return -ENOMEM;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	chip->ecc.size = 512;
66162306a36Sopenharmony_ci	chip->ecc.hwctl = lpc32xx_ecc_enable;
66262306a36Sopenharmony_ci	chip->ecc.read_page_raw = lpc32xx_read_page;
66362306a36Sopenharmony_ci	chip->ecc.read_page = lpc32xx_read_page;
66462306a36Sopenharmony_ci	chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel;
66562306a36Sopenharmony_ci	chip->ecc.write_page = lpc32xx_write_page_lowlevel;
66662306a36Sopenharmony_ci	chip->ecc.write_oob = lpc32xx_write_oob;
66762306a36Sopenharmony_ci	chip->ecc.read_oob = lpc32xx_read_oob;
66862306a36Sopenharmony_ci	chip->ecc.strength = 4;
66962306a36Sopenharmony_ci	chip->ecc.bytes = 10;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
67262306a36Sopenharmony_ci	host->mlcsubpages = mtd->writesize / 512;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return 0;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic const struct nand_controller_ops lpc32xx_nand_controller_ops = {
67862306a36Sopenharmony_ci	.attach_chip = lpc32xx_nand_attach_chip,
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/*
68262306a36Sopenharmony_ci * Probe for NAND controller
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_cistatic int lpc32xx_nand_probe(struct platform_device *pdev)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct lpc32xx_nand_host *host;
68762306a36Sopenharmony_ci	struct mtd_info *mtd;
68862306a36Sopenharmony_ci	struct nand_chip *nand_chip;
68962306a36Sopenharmony_ci	struct resource *rc;
69062306a36Sopenharmony_ci	int res;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Allocate memory for the device structure (and zero it) */
69362306a36Sopenharmony_ci	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
69462306a36Sopenharmony_ci	if (!host)
69562306a36Sopenharmony_ci		return -ENOMEM;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	host->pdev = pdev;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &rc);
70062306a36Sopenharmony_ci	if (IS_ERR(host->io_base))
70162306a36Sopenharmony_ci		return PTR_ERR(host->io_base);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	host->io_base_phy = rc->start;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	nand_chip = &host->nand_chip;
70662306a36Sopenharmony_ci	mtd = nand_to_mtd(nand_chip);
70762306a36Sopenharmony_ci	if (pdev->dev.of_node)
70862306a36Sopenharmony_ci		host->ncfg = lpc32xx_parse_dt(&pdev->dev);
70962306a36Sopenharmony_ci	if (!host->ncfg) {
71062306a36Sopenharmony_ci		dev_err(&pdev->dev,
71162306a36Sopenharmony_ci			"Missing or bad NAND config from device tree\n");
71262306a36Sopenharmony_ci		return -ENOENT;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/* Start with WP disabled, if available */
71662306a36Sopenharmony_ci	host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
71762306a36Sopenharmony_ci	res = PTR_ERR_OR_ZERO(host->wp_gpio);
71862306a36Sopenharmony_ci	if (res) {
71962306a36Sopenharmony_ci		if (res != -EPROBE_DEFER)
72062306a36Sopenharmony_ci			dev_err(&pdev->dev, "WP GPIO is not available: %d\n",
72162306a36Sopenharmony_ci				res);
72262306a36Sopenharmony_ci		return res;
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	host->pdata = dev_get_platdata(&pdev->dev);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* link the private data structures */
73062306a36Sopenharmony_ci	nand_set_controller_data(nand_chip, host);
73162306a36Sopenharmony_ci	nand_set_flash_node(nand_chip, pdev->dev.of_node);
73262306a36Sopenharmony_ci	mtd->dev.parent = &pdev->dev;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* Get NAND clock */
73562306a36Sopenharmony_ci	host->clk = clk_get(&pdev->dev, NULL);
73662306a36Sopenharmony_ci	if (IS_ERR(host->clk)) {
73762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Clock initialization failure\n");
73862306a36Sopenharmony_ci		res = -ENOENT;
73962306a36Sopenharmony_ci		goto free_gpio;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci	res = clk_prepare_enable(host->clk);
74262306a36Sopenharmony_ci	if (res)
74362306a36Sopenharmony_ci		goto put_clk;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	nand_chip->legacy.cmd_ctrl = lpc32xx_nand_cmd_ctrl;
74662306a36Sopenharmony_ci	nand_chip->legacy.dev_ready = lpc32xx_nand_device_ready;
74762306a36Sopenharmony_ci	nand_chip->legacy.chip_delay = 25; /* us */
74862306a36Sopenharmony_ci	nand_chip->legacy.IO_ADDR_R = MLC_DATA(host->io_base);
74962306a36Sopenharmony_ci	nand_chip->legacy.IO_ADDR_W = MLC_DATA(host->io_base);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* Init NAND controller */
75262306a36Sopenharmony_ci	lpc32xx_nand_setup(host);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/* Initialize function pointers */
75762306a36Sopenharmony_ci	nand_chip->legacy.waitfunc = lpc32xx_waitfunc;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	nand_chip->options = NAND_NO_SUBPAGE_WRITE;
76062306a36Sopenharmony_ci	nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
76162306a36Sopenharmony_ci	nand_chip->bbt_td = &lpc32xx_nand_bbt;
76262306a36Sopenharmony_ci	nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	if (use_dma) {
76562306a36Sopenharmony_ci		res = lpc32xx_dma_setup(host);
76662306a36Sopenharmony_ci		if (res) {
76762306a36Sopenharmony_ci			res = -EIO;
76862306a36Sopenharmony_ci			goto unprepare_clk;
76962306a36Sopenharmony_ci		}
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* initially clear interrupt status */
77362306a36Sopenharmony_ci	readb(MLC_IRQ_SR(host->io_base));
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	init_completion(&host->comp_nand);
77662306a36Sopenharmony_ci	init_completion(&host->comp_controller);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	host->irq = platform_get_irq(pdev, 0);
77962306a36Sopenharmony_ci	if (host->irq < 0) {
78062306a36Sopenharmony_ci		res = -EINVAL;
78162306a36Sopenharmony_ci		goto release_dma_chan;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (request_irq(host->irq, &lpc3xxx_nand_irq,
78562306a36Sopenharmony_ci			IRQF_TRIGGER_HIGH, DRV_NAME, host)) {
78662306a36Sopenharmony_ci		dev_err(&pdev->dev, "Error requesting NAND IRQ\n");
78762306a36Sopenharmony_ci		res = -ENXIO;
78862306a36Sopenharmony_ci		goto release_dma_chan;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/*
79262306a36Sopenharmony_ci	 * Scan to find existence of the device and get the type of NAND device:
79362306a36Sopenharmony_ci	 * SMALL block or LARGE block.
79462306a36Sopenharmony_ci	 */
79562306a36Sopenharmony_ci	nand_chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops;
79662306a36Sopenharmony_ci	res = nand_scan(nand_chip, 1);
79762306a36Sopenharmony_ci	if (res)
79862306a36Sopenharmony_ci		goto free_irq;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	mtd->name = DRV_NAME;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	res = mtd_device_register(mtd, host->ncfg->parts,
80362306a36Sopenharmony_ci				  host->ncfg->num_parts);
80462306a36Sopenharmony_ci	if (res)
80562306a36Sopenharmony_ci		goto cleanup_nand;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return 0;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cicleanup_nand:
81062306a36Sopenharmony_ci	nand_cleanup(nand_chip);
81162306a36Sopenharmony_cifree_irq:
81262306a36Sopenharmony_ci	free_irq(host->irq, host);
81362306a36Sopenharmony_cirelease_dma_chan:
81462306a36Sopenharmony_ci	if (use_dma)
81562306a36Sopenharmony_ci		dma_release_channel(host->dma_chan);
81662306a36Sopenharmony_ciunprepare_clk:
81762306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
81862306a36Sopenharmony_ciput_clk:
81962306a36Sopenharmony_ci	clk_put(host->clk);
82062306a36Sopenharmony_cifree_gpio:
82162306a36Sopenharmony_ci	lpc32xx_wp_enable(host);
82262306a36Sopenharmony_ci	gpiod_put(host->wp_gpio);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	return res;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci/*
82862306a36Sopenharmony_ci * Remove NAND device
82962306a36Sopenharmony_ci */
83062306a36Sopenharmony_cistatic void lpc32xx_nand_remove(struct platform_device *pdev)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
83362306a36Sopenharmony_ci	struct nand_chip *chip = &host->nand_chip;
83462306a36Sopenharmony_ci	int ret;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
83762306a36Sopenharmony_ci	WARN_ON(ret);
83862306a36Sopenharmony_ci	nand_cleanup(chip);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	free_irq(host->irq, host);
84162306a36Sopenharmony_ci	if (use_dma)
84262306a36Sopenharmony_ci		dma_release_channel(host->dma_chan);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
84562306a36Sopenharmony_ci	clk_put(host->clk);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	lpc32xx_wp_enable(host);
84862306a36Sopenharmony_ci	gpiod_put(host->wp_gpio);
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic int lpc32xx_nand_resume(struct platform_device *pdev)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
85462306a36Sopenharmony_ci	int ret;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/* Re-enable NAND clock */
85762306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk);
85862306a36Sopenharmony_ci	if (ret)
85962306a36Sopenharmony_ci		return ret;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* Fresh init of NAND controller */
86262306a36Sopenharmony_ci	lpc32xx_nand_setup(host);
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/* Disable write protect */
86562306a36Sopenharmony_ci	lpc32xx_wp_disable(host);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	return 0;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* Enable write protect for safety */
87562306a36Sopenharmony_ci	lpc32xx_wp_enable(host);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Disable clock */
87862306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
87962306a36Sopenharmony_ci	return 0;
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic const struct of_device_id lpc32xx_nand_match[] = {
88362306a36Sopenharmony_ci	{ .compatible = "nxp,lpc3220-mlc" },
88462306a36Sopenharmony_ci	{ /* sentinel */ },
88562306a36Sopenharmony_ci};
88662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic struct platform_driver lpc32xx_nand_driver = {
88962306a36Sopenharmony_ci	.probe		= lpc32xx_nand_probe,
89062306a36Sopenharmony_ci	.remove_new	= lpc32xx_nand_remove,
89162306a36Sopenharmony_ci	.resume		= pm_ptr(lpc32xx_nand_resume),
89262306a36Sopenharmony_ci	.suspend	= pm_ptr(lpc32xx_nand_suspend),
89362306a36Sopenharmony_ci	.driver		= {
89462306a36Sopenharmony_ci		.name	= DRV_NAME,
89562306a36Sopenharmony_ci		.of_match_table = lpc32xx_nand_match,
89662306a36Sopenharmony_ci	},
89762306a36Sopenharmony_ci};
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cimodule_platform_driver(lpc32xx_nand_driver);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
90262306a36Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
90362306a36Sopenharmony_ciMODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller");
904