18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for NAND MLC Controller in LPC32xx
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Roland Stigge <stigge@antcom.de>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright © 2011 WORK Microwave GmbH
88c2ecf20Sopenharmony_ci * Copyright © 2011, 2012 Roland Stigge
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * NAND Flash Controller Operation:
118c2ecf20Sopenharmony_ci * - Read: Auto Decode
128c2ecf20Sopenharmony_ci * - Write: Auto Encode
138c2ecf20Sopenharmony_ci * - Tested Page Sizes: 2048, 4096
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
198c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
208c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
218c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
228c2ecf20Sopenharmony_ci#include <linux/clk.h>
238c2ecf20Sopenharmony_ci#include <linux/err.h>
248c2ecf20Sopenharmony_ci#include <linux/delay.h>
258c2ecf20Sopenharmony_ci#include <linux/completion.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/of.h>
288c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
298c2ecf20Sopenharmony_ci#include <linux/mtd/lpc32xx_mlc.h>
308c2ecf20Sopenharmony_ci#include <linux/io.h>
318c2ecf20Sopenharmony_ci#include <linux/mm.h>
328c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
338c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
348c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define DRV_NAME "lpc32xx_mlc"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/**********************************************************************
398c2ecf20Sopenharmony_ci* MLC NAND controller register offsets
408c2ecf20Sopenharmony_ci**********************************************************************/
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define MLC_BUFF(x)			(x + 0x00000)
438c2ecf20Sopenharmony_ci#define MLC_DATA(x)			(x + 0x08000)
448c2ecf20Sopenharmony_ci#define MLC_CMD(x)			(x + 0x10000)
458c2ecf20Sopenharmony_ci#define MLC_ADDR(x)			(x + 0x10004)
468c2ecf20Sopenharmony_ci#define MLC_ECC_ENC_REG(x)		(x + 0x10008)
478c2ecf20Sopenharmony_ci#define MLC_ECC_DEC_REG(x)		(x + 0x1000C)
488c2ecf20Sopenharmony_ci#define MLC_ECC_AUTO_ENC_REG(x)		(x + 0x10010)
498c2ecf20Sopenharmony_ci#define MLC_ECC_AUTO_DEC_REG(x)		(x + 0x10014)
508c2ecf20Sopenharmony_ci#define MLC_RPR(x)			(x + 0x10018)
518c2ecf20Sopenharmony_ci#define MLC_WPR(x)			(x + 0x1001C)
528c2ecf20Sopenharmony_ci#define MLC_RUBP(x)			(x + 0x10020)
538c2ecf20Sopenharmony_ci#define MLC_ROBP(x)			(x + 0x10024)
548c2ecf20Sopenharmony_ci#define MLC_SW_WP_ADD_LOW(x)		(x + 0x10028)
558c2ecf20Sopenharmony_ci#define MLC_SW_WP_ADD_HIG(x)		(x + 0x1002C)
568c2ecf20Sopenharmony_ci#define MLC_ICR(x)			(x + 0x10030)
578c2ecf20Sopenharmony_ci#define MLC_TIME_REG(x)			(x + 0x10034)
588c2ecf20Sopenharmony_ci#define MLC_IRQ_MR(x)			(x + 0x10038)
598c2ecf20Sopenharmony_ci#define MLC_IRQ_SR(x)			(x + 0x1003C)
608c2ecf20Sopenharmony_ci#define MLC_LOCK_PR(x)			(x + 0x10044)
618c2ecf20Sopenharmony_ci#define MLC_ISR(x)			(x + 0x10048)
628c2ecf20Sopenharmony_ci#define MLC_CEH(x)			(x + 0x1004C)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/**********************************************************************
658c2ecf20Sopenharmony_ci* MLC_CMD bit definitions
668c2ecf20Sopenharmony_ci**********************************************************************/
678c2ecf20Sopenharmony_ci#define MLCCMD_RESET			0xFF
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/**********************************************************************
708c2ecf20Sopenharmony_ci* MLC_ICR bit definitions
718c2ecf20Sopenharmony_ci**********************************************************************/
728c2ecf20Sopenharmony_ci#define MLCICR_WPROT			(1 << 3)
738c2ecf20Sopenharmony_ci#define MLCICR_LARGEBLOCK		(1 << 2)
748c2ecf20Sopenharmony_ci#define MLCICR_LONGADDR			(1 << 1)
758c2ecf20Sopenharmony_ci#define MLCICR_16BIT			(1 << 0)  /* unsupported by LPC32x0! */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/**********************************************************************
788c2ecf20Sopenharmony_ci* MLC_TIME_REG bit definitions
798c2ecf20Sopenharmony_ci**********************************************************************/
808c2ecf20Sopenharmony_ci#define MLCTIMEREG_TCEA_DELAY(n)	(((n) & 0x03) << 24)
818c2ecf20Sopenharmony_ci#define MLCTIMEREG_BUSY_DELAY(n)	(((n) & 0x1F) << 19)
828c2ecf20Sopenharmony_ci#define MLCTIMEREG_NAND_TA(n)		(((n) & 0x07) << 16)
838c2ecf20Sopenharmony_ci#define MLCTIMEREG_RD_HIGH(n)		(((n) & 0x0F) << 12)
848c2ecf20Sopenharmony_ci#define MLCTIMEREG_RD_LOW(n)		(((n) & 0x0F) << 8)
858c2ecf20Sopenharmony_ci#define MLCTIMEREG_WR_HIGH(n)		(((n) & 0x0F) << 4)
868c2ecf20Sopenharmony_ci#define MLCTIMEREG_WR_LOW(n)		(((n) & 0x0F) << 0)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci/**********************************************************************
898c2ecf20Sopenharmony_ci* MLC_IRQ_MR and MLC_IRQ_SR bit definitions
908c2ecf20Sopenharmony_ci**********************************************************************/
918c2ecf20Sopenharmony_ci#define MLCIRQ_NAND_READY		(1 << 5)
928c2ecf20Sopenharmony_ci#define MLCIRQ_CONTROLLER_READY		(1 << 4)
938c2ecf20Sopenharmony_ci#define MLCIRQ_DECODE_FAILURE		(1 << 3)
948c2ecf20Sopenharmony_ci#define MLCIRQ_DECODE_ERROR		(1 << 2)
958c2ecf20Sopenharmony_ci#define MLCIRQ_ECC_READY		(1 << 1)
968c2ecf20Sopenharmony_ci#define MLCIRQ_WRPROT_FAULT		(1 << 0)
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**********************************************************************
998c2ecf20Sopenharmony_ci* MLC_LOCK_PR bit definitions
1008c2ecf20Sopenharmony_ci**********************************************************************/
1018c2ecf20Sopenharmony_ci#define MLCLOCKPR_MAGIC			0xA25E
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/**********************************************************************
1048c2ecf20Sopenharmony_ci* MLC_ISR bit definitions
1058c2ecf20Sopenharmony_ci**********************************************************************/
1068c2ecf20Sopenharmony_ci#define MLCISR_DECODER_FAILURE		(1 << 6)
1078c2ecf20Sopenharmony_ci#define MLCISR_ERRORS			((1 << 4) | (1 << 5))
1088c2ecf20Sopenharmony_ci#define MLCISR_ERRORS_DETECTED		(1 << 3)
1098c2ecf20Sopenharmony_ci#define MLCISR_ECC_READY		(1 << 2)
1108c2ecf20Sopenharmony_ci#define MLCISR_CONTROLLER_READY		(1 << 1)
1118c2ecf20Sopenharmony_ci#define MLCISR_NAND_READY		(1 << 0)
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/**********************************************************************
1148c2ecf20Sopenharmony_ci* MLC_CEH bit definitions
1158c2ecf20Sopenharmony_ci**********************************************************************/
1168c2ecf20Sopenharmony_ci#define MLCCEH_NORMAL			(1 << 0)
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct lpc32xx_nand_cfg_mlc {
1198c2ecf20Sopenharmony_ci	uint32_t tcea_delay;
1208c2ecf20Sopenharmony_ci	uint32_t busy_delay;
1218c2ecf20Sopenharmony_ci	uint32_t nand_ta;
1228c2ecf20Sopenharmony_ci	uint32_t rd_high;
1238c2ecf20Sopenharmony_ci	uint32_t rd_low;
1248c2ecf20Sopenharmony_ci	uint32_t wr_high;
1258c2ecf20Sopenharmony_ci	uint32_t wr_low;
1268c2ecf20Sopenharmony_ci	int wp_gpio;
1278c2ecf20Sopenharmony_ci	struct mtd_partition *parts;
1288c2ecf20Sopenharmony_ci	unsigned num_parts;
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section,
1328c2ecf20Sopenharmony_ci				 struct mtd_oob_region *oobregion)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
1378c2ecf20Sopenharmony_ci		return -ERANGE;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	oobregion->offset = ((section + 1) * 16) - nand_chip->ecc.bytes;
1408c2ecf20Sopenharmony_ci	oobregion->length = nand_chip->ecc.bytes;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section,
1468c2ecf20Sopenharmony_ci				  struct mtd_oob_region *oobregion)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (section >= nand_chip->ecc.steps)
1518c2ecf20Sopenharmony_ci		return -ERANGE;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	oobregion->offset = 16 * section;
1548c2ecf20Sopenharmony_ci	oobregion->length = 16 - nand_chip->ecc.bytes;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = {
1608c2ecf20Sopenharmony_ci	.ecc = lpc32xx_ooblayout_ecc,
1618c2ecf20Sopenharmony_ci	.free = lpc32xx_ooblayout_free,
1628c2ecf20Sopenharmony_ci};
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic struct nand_bbt_descr lpc32xx_nand_bbt = {
1658c2ecf20Sopenharmony_ci	.options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB |
1668c2ecf20Sopenharmony_ci		   NAND_BBT_WRITE,
1678c2ecf20Sopenharmony_ci	.pages = { 524224, 0, 0, 0, 0, 0, 0, 0 },
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic struct nand_bbt_descr lpc32xx_nand_bbt_mirror = {
1718c2ecf20Sopenharmony_ci	.options = NAND_BBT_ABSPAGE | NAND_BBT_2BIT | NAND_BBT_NO_OOB |
1728c2ecf20Sopenharmony_ci		   NAND_BBT_WRITE,
1738c2ecf20Sopenharmony_ci	.pages = { 524160, 0, 0, 0, 0, 0, 0, 0 },
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistruct lpc32xx_nand_host {
1778c2ecf20Sopenharmony_ci	struct platform_device	*pdev;
1788c2ecf20Sopenharmony_ci	struct nand_chip	nand_chip;
1798c2ecf20Sopenharmony_ci	struct lpc32xx_mlc_platform_data *pdata;
1808c2ecf20Sopenharmony_ci	struct clk		*clk;
1818c2ecf20Sopenharmony_ci	void __iomem		*io_base;
1828c2ecf20Sopenharmony_ci	int			irq;
1838c2ecf20Sopenharmony_ci	struct lpc32xx_nand_cfg_mlc	*ncfg;
1848c2ecf20Sopenharmony_ci	struct completion       comp_nand;
1858c2ecf20Sopenharmony_ci	struct completion       comp_controller;
1868c2ecf20Sopenharmony_ci	uint32_t llptr;
1878c2ecf20Sopenharmony_ci	/*
1888c2ecf20Sopenharmony_ci	 * Physical addresses of ECC buffer, DMA data buffers, OOB data buffer
1898c2ecf20Sopenharmony_ci	 */
1908c2ecf20Sopenharmony_ci	dma_addr_t		oob_buf_phy;
1918c2ecf20Sopenharmony_ci	/*
1928c2ecf20Sopenharmony_ci	 * Virtual addresses of ECC buffer, DMA data buffers, OOB data buffer
1938c2ecf20Sopenharmony_ci	 */
1948c2ecf20Sopenharmony_ci	uint8_t			*oob_buf;
1958c2ecf20Sopenharmony_ci	/* Physical address of DMA base address */
1968c2ecf20Sopenharmony_ci	dma_addr_t		io_base_phy;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	struct completion	comp_dma;
1998c2ecf20Sopenharmony_ci	struct dma_chan		*dma_chan;
2008c2ecf20Sopenharmony_ci	struct dma_slave_config	dma_slave_config;
2018c2ecf20Sopenharmony_ci	struct scatterlist	sgl;
2028c2ecf20Sopenharmony_ci	uint8_t			*dma_buf;
2038c2ecf20Sopenharmony_ci	uint8_t			*dummy_buf;
2048c2ecf20Sopenharmony_ci	int			mlcsubpages; /* number of 512bytes-subpages */
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/*
2088c2ecf20Sopenharmony_ci * Activate/Deactivate DMA Operation:
2098c2ecf20Sopenharmony_ci *
2108c2ecf20Sopenharmony_ci * Using the PL080 DMA Controller for transferring the 512 byte subpages
2118c2ecf20Sopenharmony_ci * instead of doing readl() / writel() in a loop slows it down significantly.
2128c2ecf20Sopenharmony_ci * Measurements via getnstimeofday() upon 512 byte subpage reads reveal:
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * - readl() of 128 x 32 bits in a loop: ~20us
2158c2ecf20Sopenharmony_ci * - DMA read of 512 bytes (32 bit, 4...128 words bursts): ~60us
2168c2ecf20Sopenharmony_ci * - DMA read of 512 bytes (32 bit, no bursts): ~100us
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * This applies to the transfer itself. In the DMA case: only the
2198c2ecf20Sopenharmony_ci * wait_for_completion() (DMA setup _not_ included).
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * Note that the 512 bytes subpage transfer is done directly from/to a
2228c2ecf20Sopenharmony_ci * FIFO/buffer inside the NAND controller. Most of the time (~400-800us for a
2238c2ecf20Sopenharmony_ci * 2048 bytes page) is spent waiting for the NAND IRQ, anyway. (The NAND
2248c2ecf20Sopenharmony_ci * controller transferring data between its internal buffer to/from the NAND
2258c2ecf20Sopenharmony_ci * chip.)
2268c2ecf20Sopenharmony_ci *
2278c2ecf20Sopenharmony_ci * Therefore, using the PL080 DMA is disabled by default, for now.
2288c2ecf20Sopenharmony_ci *
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic int use_dma;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void lpc32xx_nand_setup(struct lpc32xx_nand_host *host)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	uint32_t clkrate, tmp;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Reset MLC controller */
2378c2ecf20Sopenharmony_ci	writel(MLCCMD_RESET, MLC_CMD(host->io_base));
2388c2ecf20Sopenharmony_ci	udelay(1000);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Get base clock for MLC block */
2418c2ecf20Sopenharmony_ci	clkrate = clk_get_rate(host->clk);
2428c2ecf20Sopenharmony_ci	if (clkrate == 0)
2438c2ecf20Sopenharmony_ci		clkrate = 104000000;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/* Unlock MLC_ICR
2468c2ecf20Sopenharmony_ci	 * (among others, will be locked again automatically) */
2478c2ecf20Sopenharmony_ci	writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base));
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* Configure MLC Controller: Large Block, 5 Byte Address */
2508c2ecf20Sopenharmony_ci	tmp = MLCICR_LARGEBLOCK | MLCICR_LONGADDR;
2518c2ecf20Sopenharmony_ci	writel(tmp, MLC_ICR(host->io_base));
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* Unlock MLC_TIME_REG
2548c2ecf20Sopenharmony_ci	 * (among others, will be locked again automatically) */
2558c2ecf20Sopenharmony_ci	writew(MLCLOCKPR_MAGIC, MLC_LOCK_PR(host->io_base));
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Compute clock setup values, see LPC and NAND manual */
2588c2ecf20Sopenharmony_ci	tmp = 0;
2598c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_TCEA_DELAY(clkrate / host->ncfg->tcea_delay + 1);
2608c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_BUSY_DELAY(clkrate / host->ncfg->busy_delay + 1);
2618c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_NAND_TA(clkrate / host->ncfg->nand_ta + 1);
2628c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_RD_HIGH(clkrate / host->ncfg->rd_high + 1);
2638c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_RD_LOW(clkrate / host->ncfg->rd_low);
2648c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_WR_HIGH(clkrate / host->ncfg->wr_high + 1);
2658c2ecf20Sopenharmony_ci	tmp |= MLCTIMEREG_WR_LOW(clkrate / host->ncfg->wr_low);
2668c2ecf20Sopenharmony_ci	writel(tmp, MLC_TIME_REG(host->io_base));
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* Enable IRQ for CONTROLLER_READY and NAND_READY */
2698c2ecf20Sopenharmony_ci	writeb(MLCIRQ_CONTROLLER_READY | MLCIRQ_NAND_READY,
2708c2ecf20Sopenharmony_ci			MLC_IRQ_MR(host->io_base));
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	/* Normal nCE operation: nCE controlled by controller */
2738c2ecf20Sopenharmony_ci	writel(MLCCEH_NORMAL, MLC_CEH(host->io_base));
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci/*
2778c2ecf20Sopenharmony_ci * Hardware specific access to control lines
2788c2ecf20Sopenharmony_ci */
2798c2ecf20Sopenharmony_cistatic void lpc32xx_nand_cmd_ctrl(struct nand_chip *nand_chip, int cmd,
2808c2ecf20Sopenharmony_ci				  unsigned int ctrl)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (cmd != NAND_CMD_NONE) {
2858c2ecf20Sopenharmony_ci		if (ctrl & NAND_CLE)
2868c2ecf20Sopenharmony_ci			writel(cmd, MLC_CMD(host->io_base));
2878c2ecf20Sopenharmony_ci		else
2888c2ecf20Sopenharmony_ci			writel(cmd, MLC_ADDR(host->io_base));
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/*
2938c2ecf20Sopenharmony_ci * Read Device Ready (NAND device _and_ controller ready)
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_cistatic int lpc32xx_nand_device_ready(struct nand_chip *nand_chip)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(nand_chip);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if ((readb(MLC_ISR(host->io_base)) &
3008c2ecf20Sopenharmony_ci	     (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY)) ==
3018c2ecf20Sopenharmony_ci	    (MLCISR_CONTROLLER_READY | MLCISR_NAND_READY))
3028c2ecf20Sopenharmony_ci		return  1;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	uint8_t sr;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* Clear interrupt flag by reading status */
3128c2ecf20Sopenharmony_ci	sr = readb(MLC_IRQ_SR(host->io_base));
3138c2ecf20Sopenharmony_ci	if (sr & MLCIRQ_NAND_READY)
3148c2ecf20Sopenharmony_ci		complete(&host->comp_nand);
3158c2ecf20Sopenharmony_ci	if (sr & MLCIRQ_CONTROLLER_READY)
3168c2ecf20Sopenharmony_ci		complete(&host->comp_controller);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int lpc32xx_waitfunc_nand(struct nand_chip *chip)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
3248c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)
3278c2ecf20Sopenharmony_ci		goto exit;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	wait_for_completion(&host->comp_nand);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	while (!(readb(MLC_ISR(host->io_base)) & MLCISR_NAND_READY)) {
3328c2ecf20Sopenharmony_ci		/* Seems to be delayed sometimes by controller */
3338c2ecf20Sopenharmony_ci		dev_dbg(&mtd->dev, "Warning: NAND not ready.\n");
3348c2ecf20Sopenharmony_ci		cpu_relax();
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ciexit:
3388c2ecf20Sopenharmony_ci	return NAND_STATUS_READY;
3398c2ecf20Sopenharmony_ci}
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic int lpc32xx_waitfunc_controller(struct nand_chip *chip)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
3448c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (readb(MLC_ISR(host->io_base)) & MLCISR_CONTROLLER_READY)
3478c2ecf20Sopenharmony_ci		goto exit;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	wait_for_completion(&host->comp_controller);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	while (!(readb(MLC_ISR(host->io_base)) &
3528c2ecf20Sopenharmony_ci		 MLCISR_CONTROLLER_READY)) {
3538c2ecf20Sopenharmony_ci		dev_dbg(&mtd->dev, "Warning: Controller not ready.\n");
3548c2ecf20Sopenharmony_ci		cpu_relax();
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ciexit:
3588c2ecf20Sopenharmony_ci	return NAND_STATUS_READY;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic int lpc32xx_waitfunc(struct nand_chip *chip)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	lpc32xx_waitfunc_nand(chip);
3648c2ecf20Sopenharmony_ci	lpc32xx_waitfunc_controller(chip);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return NAND_STATUS_READY;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci/*
3708c2ecf20Sopenharmony_ci * Enable NAND write protect
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_cistatic void lpc32xx_wp_enable(struct lpc32xx_nand_host *host)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	if (gpio_is_valid(host->ncfg->wp_gpio))
3758c2ecf20Sopenharmony_ci		gpio_set_value(host->ncfg->wp_gpio, 0);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/*
3798c2ecf20Sopenharmony_ci * Disable NAND write protect
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_cistatic void lpc32xx_wp_disable(struct lpc32xx_nand_host *host)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	if (gpio_is_valid(host->ncfg->wp_gpio))
3848c2ecf20Sopenharmony_ci		gpio_set_value(host->ncfg->wp_gpio, 1);
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void lpc32xx_dma_complete_func(void *completion)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	complete(completion);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
3938c2ecf20Sopenharmony_ci			    enum dma_transfer_direction dir)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
3968c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
3978c2ecf20Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
3988c2ecf20Sopenharmony_ci	int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
3998c2ecf20Sopenharmony_ci	int res;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	sg_init_one(&host->sgl, mem, len);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1,
4048c2ecf20Sopenharmony_ci			 DMA_BIDIRECTIONAL);
4058c2ecf20Sopenharmony_ci	if (res != 1) {
4068c2ecf20Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to map sg list\n");
4078c2ecf20Sopenharmony_ci		return -ENXIO;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir,
4108c2ecf20Sopenharmony_ci				       flags);
4118c2ecf20Sopenharmony_ci	if (!desc) {
4128c2ecf20Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
4138c2ecf20Sopenharmony_ci		goto out1;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	init_completion(&host->comp_dma);
4178c2ecf20Sopenharmony_ci	desc->callback = lpc32xx_dma_complete_func;
4188c2ecf20Sopenharmony_ci	desc->callback_param = &host->comp_dma;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	dmaengine_submit(desc);
4218c2ecf20Sopenharmony_ci	dma_async_issue_pending(host->dma_chan);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
4268c2ecf20Sopenharmony_ci		     DMA_BIDIRECTIONAL);
4278c2ecf20Sopenharmony_ci	return 0;
4288c2ecf20Sopenharmony_ciout1:
4298c2ecf20Sopenharmony_ci	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
4308c2ecf20Sopenharmony_ci		     DMA_BIDIRECTIONAL);
4318c2ecf20Sopenharmony_ci	return -ENXIO;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf,
4358c2ecf20Sopenharmony_ci			     int oob_required, int page)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
4388c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
4398c2ecf20Sopenharmony_ci	int i, j;
4408c2ecf20Sopenharmony_ci	uint8_t *oobbuf = chip->oob_poi;
4418c2ecf20Sopenharmony_ci	uint32_t mlc_isr;
4428c2ecf20Sopenharmony_ci	int res;
4438c2ecf20Sopenharmony_ci	uint8_t *dma_buf;
4448c2ecf20Sopenharmony_ci	bool dma_mapped;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if ((void *)buf <= high_memory) {
4478c2ecf20Sopenharmony_ci		dma_buf = buf;
4488c2ecf20Sopenharmony_ci		dma_mapped = true;
4498c2ecf20Sopenharmony_ci	} else {
4508c2ecf20Sopenharmony_ci		dma_buf = host->dma_buf;
4518c2ecf20Sopenharmony_ci		dma_mapped = false;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Writing Command and Address */
4558c2ecf20Sopenharmony_ci	nand_read_page_op(chip, page, 0, NULL, 0);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* For all sub-pages */
4588c2ecf20Sopenharmony_ci	for (i = 0; i < host->mlcsubpages; i++) {
4598c2ecf20Sopenharmony_ci		/* Start Auto Decode Command */
4608c2ecf20Sopenharmony_ci		writeb(0x00, MLC_ECC_AUTO_DEC_REG(host->io_base));
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		/* Wait for Controller Ready */
4638c2ecf20Sopenharmony_ci		lpc32xx_waitfunc_controller(chip);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci		/* Check ECC Error status */
4668c2ecf20Sopenharmony_ci		mlc_isr = readl(MLC_ISR(host->io_base));
4678c2ecf20Sopenharmony_ci		if (mlc_isr & MLCISR_DECODER_FAILURE) {
4688c2ecf20Sopenharmony_ci			mtd->ecc_stats.failed++;
4698c2ecf20Sopenharmony_ci			dev_warn(&mtd->dev, "%s: DECODER_FAILURE\n", __func__);
4708c2ecf20Sopenharmony_ci		} else if (mlc_isr & MLCISR_ERRORS_DETECTED) {
4718c2ecf20Sopenharmony_ci			mtd->ecc_stats.corrected += ((mlc_isr >> 4) & 0x3) + 1;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		/* Read 512 + 16 Bytes */
4758c2ecf20Sopenharmony_ci		if (use_dma) {
4768c2ecf20Sopenharmony_ci			res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512,
4778c2ecf20Sopenharmony_ci					       DMA_DEV_TO_MEM);
4788c2ecf20Sopenharmony_ci			if (res)
4798c2ecf20Sopenharmony_ci				return res;
4808c2ecf20Sopenharmony_ci		} else {
4818c2ecf20Sopenharmony_ci			for (j = 0; j < (512 >> 2); j++) {
4828c2ecf20Sopenharmony_ci				*((uint32_t *)(buf)) =
4838c2ecf20Sopenharmony_ci					readl(MLC_BUFF(host->io_base));
4848c2ecf20Sopenharmony_ci				buf += 4;
4858c2ecf20Sopenharmony_ci			}
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci		for (j = 0; j < (16 >> 2); j++) {
4888c2ecf20Sopenharmony_ci			*((uint32_t *)(oobbuf)) =
4898c2ecf20Sopenharmony_ci				readl(MLC_BUFF(host->io_base));
4908c2ecf20Sopenharmony_ci			oobbuf += 4;
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (use_dma && !dma_mapped)
4958c2ecf20Sopenharmony_ci		memcpy(buf, dma_buf, mtd->writesize);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	return 0;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int lpc32xx_write_page_lowlevel(struct nand_chip *chip,
5018c2ecf20Sopenharmony_ci				       const uint8_t *buf, int oob_required,
5028c2ecf20Sopenharmony_ci				       int page)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
5058c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
5068c2ecf20Sopenharmony_ci	const uint8_t *oobbuf = chip->oob_poi;
5078c2ecf20Sopenharmony_ci	uint8_t *dma_buf = (uint8_t *)buf;
5088c2ecf20Sopenharmony_ci	int res;
5098c2ecf20Sopenharmony_ci	int i, j;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (use_dma && (void *)buf >= high_memory) {
5128c2ecf20Sopenharmony_ci		dma_buf = host->dma_buf;
5138c2ecf20Sopenharmony_ci		memcpy(dma_buf, buf, mtd->writesize);
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	for (i = 0; i < host->mlcsubpages; i++) {
5198c2ecf20Sopenharmony_ci		/* Start Encode */
5208c2ecf20Sopenharmony_ci		writeb(0x00, MLC_ECC_ENC_REG(host->io_base));
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		/* Write 512 + 6 Bytes to Buffer */
5238c2ecf20Sopenharmony_ci		if (use_dma) {
5248c2ecf20Sopenharmony_ci			res = lpc32xx_xmit_dma(mtd, dma_buf + i * 512, 512,
5258c2ecf20Sopenharmony_ci					       DMA_MEM_TO_DEV);
5268c2ecf20Sopenharmony_ci			if (res)
5278c2ecf20Sopenharmony_ci				return res;
5288c2ecf20Sopenharmony_ci		} else {
5298c2ecf20Sopenharmony_ci			for (j = 0; j < (512 >> 2); j++) {
5308c2ecf20Sopenharmony_ci				writel(*((uint32_t *)(buf)),
5318c2ecf20Sopenharmony_ci				       MLC_BUFF(host->io_base));
5328c2ecf20Sopenharmony_ci				buf += 4;
5338c2ecf20Sopenharmony_ci			}
5348c2ecf20Sopenharmony_ci		}
5358c2ecf20Sopenharmony_ci		writel(*((uint32_t *)(oobbuf)), MLC_BUFF(host->io_base));
5368c2ecf20Sopenharmony_ci		oobbuf += 4;
5378c2ecf20Sopenharmony_ci		writew(*((uint16_t *)(oobbuf)), MLC_BUFF(host->io_base));
5388c2ecf20Sopenharmony_ci		oobbuf += 12;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		/* Auto Encode w/ Bit 8 = 0 (see LPC MLC Controller manual) */
5418c2ecf20Sopenharmony_ci		writeb(0x00, MLC_ECC_AUTO_ENC_REG(host->io_base));
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci		/* Wait for Controller Ready */
5448c2ecf20Sopenharmony_ci		lpc32xx_waitfunc_controller(chip);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
5488c2ecf20Sopenharmony_ci}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic int lpc32xx_read_oob(struct nand_chip *chip, int page)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/* Read whole page - necessary with MLC controller! */
5558c2ecf20Sopenharmony_ci	lpc32xx_read_page(chip, host->dummy_buf, 1, page);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	return 0;
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_cistatic int lpc32xx_write_oob(struct nand_chip *chip, int page)
5618c2ecf20Sopenharmony_ci{
5628c2ecf20Sopenharmony_ci	/* None, write_oob conflicts with the automatic LPC MLC ECC decoder! */
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci/* Prepares MLC for transfers with H/W ECC enabled: always enabled anyway */
5678c2ecf20Sopenharmony_cistatic void lpc32xx_ecc_enable(struct nand_chip *chip, int mode)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	/* Always enabled! */
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic int lpc32xx_dma_setup(struct lpc32xx_nand_host *host)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
5758c2ecf20Sopenharmony_ci	dma_cap_mask_t mask;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	if (!host->pdata || !host->pdata->dma_filter) {
5788c2ecf20Sopenharmony_ci		dev_err(mtd->dev.parent, "no DMA platform data\n");
5798c2ecf20Sopenharmony_ci		return -ENOENT;
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	dma_cap_zero(mask);
5838c2ecf20Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
5848c2ecf20Sopenharmony_ci	host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter,
5858c2ecf20Sopenharmony_ci					     "nand-mlc");
5868c2ecf20Sopenharmony_ci	if (!host->dma_chan) {
5878c2ecf20Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
5888c2ecf20Sopenharmony_ci		return -EBUSY;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/*
5928c2ecf20Sopenharmony_ci	 * Set direction to a sensible value even if the dmaengine driver
5938c2ecf20Sopenharmony_ci	 * should ignore it. With the default (DMA_MEM_TO_MEM), the amba-pl08x
5948c2ecf20Sopenharmony_ci	 * driver criticizes it as "alien transfer direction".
5958c2ecf20Sopenharmony_ci	 */
5968c2ecf20Sopenharmony_ci	host->dma_slave_config.direction = DMA_DEV_TO_MEM;
5978c2ecf20Sopenharmony_ci	host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
5988c2ecf20Sopenharmony_ci	host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
5998c2ecf20Sopenharmony_ci	host->dma_slave_config.src_maxburst = 128;
6008c2ecf20Sopenharmony_ci	host->dma_slave_config.dst_maxburst = 128;
6018c2ecf20Sopenharmony_ci	/* DMA controller does flow control: */
6028c2ecf20Sopenharmony_ci	host->dma_slave_config.device_fc = false;
6038c2ecf20Sopenharmony_ci	host->dma_slave_config.src_addr = MLC_BUFF(host->io_base_phy);
6048c2ecf20Sopenharmony_ci	host->dma_slave_config.dst_addr = MLC_BUFF(host->io_base_phy);
6058c2ecf20Sopenharmony_ci	if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) {
6068c2ecf20Sopenharmony_ci		dev_err(mtd->dev.parent, "Failed to setup DMA slave\n");
6078c2ecf20Sopenharmony_ci		goto out1;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	return 0;
6118c2ecf20Sopenharmony_ciout1:
6128c2ecf20Sopenharmony_ci	dma_release_channel(host->dma_chan);
6138c2ecf20Sopenharmony_ci	return -ENXIO;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic struct lpc32xx_nand_cfg_mlc *lpc32xx_parse_dt(struct device *dev)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct lpc32xx_nand_cfg_mlc *ncfg;
6198c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL);
6228c2ecf20Sopenharmony_ci	if (!ncfg)
6238c2ecf20Sopenharmony_ci		return NULL;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,tcea-delay", &ncfg->tcea_delay);
6268c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,busy-delay", &ncfg->busy_delay);
6278c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,nand-ta", &ncfg->nand_ta);
6288c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,rd-high", &ncfg->rd_high);
6298c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,rd-low", &ncfg->rd_low);
6308c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,wr-high", &ncfg->wr_high);
6318c2ecf20Sopenharmony_ci	of_property_read_u32(np, "nxp,wr-low", &ncfg->wr_low);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (!ncfg->tcea_delay || !ncfg->busy_delay || !ncfg->nand_ta ||
6348c2ecf20Sopenharmony_ci	    !ncfg->rd_high || !ncfg->rd_low || !ncfg->wr_high ||
6358c2ecf20Sopenharmony_ci	    !ncfg->wr_low) {
6368c2ecf20Sopenharmony_ci		dev_err(dev, "chip parameters not specified correctly\n");
6378c2ecf20Sopenharmony_ci		return NULL;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	ncfg->wp_gpio = of_get_named_gpio(np, "gpios", 0);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	return ncfg;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic int lpc32xx_nand_attach_chip(struct nand_chip *chip)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6488c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
6498c2ecf20Sopenharmony_ci	struct device *dev = &host->pdev->dev;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST)
6528c2ecf20Sopenharmony_ci		return 0;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	host->dma_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL);
6558c2ecf20Sopenharmony_ci	if (!host->dma_buf)
6568c2ecf20Sopenharmony_ci		return -ENOMEM;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	host->dummy_buf = devm_kzalloc(dev, mtd->writesize, GFP_KERNEL);
6598c2ecf20Sopenharmony_ci	if (!host->dummy_buf)
6608c2ecf20Sopenharmony_ci		return -ENOMEM;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	chip->ecc.size = 512;
6638c2ecf20Sopenharmony_ci	chip->ecc.hwctl = lpc32xx_ecc_enable;
6648c2ecf20Sopenharmony_ci	chip->ecc.read_page_raw = lpc32xx_read_page;
6658c2ecf20Sopenharmony_ci	chip->ecc.read_page = lpc32xx_read_page;
6668c2ecf20Sopenharmony_ci	chip->ecc.write_page_raw = lpc32xx_write_page_lowlevel;
6678c2ecf20Sopenharmony_ci	chip->ecc.write_page = lpc32xx_write_page_lowlevel;
6688c2ecf20Sopenharmony_ci	chip->ecc.write_oob = lpc32xx_write_oob;
6698c2ecf20Sopenharmony_ci	chip->ecc.read_oob = lpc32xx_read_oob;
6708c2ecf20Sopenharmony_ci	chip->ecc.strength = 4;
6718c2ecf20Sopenharmony_ci	chip->ecc.bytes = 10;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops);
6748c2ecf20Sopenharmony_ci	host->mlcsubpages = mtd->writesize / 512;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	return 0;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic const struct nand_controller_ops lpc32xx_nand_controller_ops = {
6808c2ecf20Sopenharmony_ci	.attach_chip = lpc32xx_nand_attach_chip,
6818c2ecf20Sopenharmony_ci};
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/*
6848c2ecf20Sopenharmony_ci * Probe for NAND controller
6858c2ecf20Sopenharmony_ci */
6868c2ecf20Sopenharmony_cistatic int lpc32xx_nand_probe(struct platform_device *pdev)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host;
6898c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
6908c2ecf20Sopenharmony_ci	struct nand_chip *nand_chip;
6918c2ecf20Sopenharmony_ci	struct resource *rc;
6928c2ecf20Sopenharmony_ci	int res;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	/* Allocate memory for the device structure (and zero it) */
6958c2ecf20Sopenharmony_ci	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
6968c2ecf20Sopenharmony_ci	if (!host)
6978c2ecf20Sopenharmony_ci		return -ENOMEM;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	host->pdev = pdev;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7028c2ecf20Sopenharmony_ci	host->io_base = devm_ioremap_resource(&pdev->dev, rc);
7038c2ecf20Sopenharmony_ci	if (IS_ERR(host->io_base))
7048c2ecf20Sopenharmony_ci		return PTR_ERR(host->io_base);
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	host->io_base_phy = rc->start;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	nand_chip = &host->nand_chip;
7098c2ecf20Sopenharmony_ci	mtd = nand_to_mtd(nand_chip);
7108c2ecf20Sopenharmony_ci	if (pdev->dev.of_node)
7118c2ecf20Sopenharmony_ci		host->ncfg = lpc32xx_parse_dt(&pdev->dev);
7128c2ecf20Sopenharmony_ci	if (!host->ncfg) {
7138c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
7148c2ecf20Sopenharmony_ci			"Missing or bad NAND config from device tree\n");
7158c2ecf20Sopenharmony_ci		return -ENOENT;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	if (host->ncfg->wp_gpio == -EPROBE_DEFER)
7188c2ecf20Sopenharmony_ci		return -EPROBE_DEFER;
7198c2ecf20Sopenharmony_ci	if (gpio_is_valid(host->ncfg->wp_gpio) &&
7208c2ecf20Sopenharmony_ci			gpio_request(host->ncfg->wp_gpio, "NAND WP")) {
7218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "GPIO not available\n");
7228c2ecf20Sopenharmony_ci		return -EBUSY;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci	lpc32xx_wp_disable(host);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	host->pdata = dev_get_platdata(&pdev->dev);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/* link the private data structures */
7298c2ecf20Sopenharmony_ci	nand_set_controller_data(nand_chip, host);
7308c2ecf20Sopenharmony_ci	nand_set_flash_node(nand_chip, pdev->dev.of_node);
7318c2ecf20Sopenharmony_ci	mtd->dev.parent = &pdev->dev;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	/* Get NAND clock */
7348c2ecf20Sopenharmony_ci	host->clk = clk_get(&pdev->dev, NULL);
7358c2ecf20Sopenharmony_ci	if (IS_ERR(host->clk)) {
7368c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Clock initialization failure\n");
7378c2ecf20Sopenharmony_ci		res = -ENOENT;
7388c2ecf20Sopenharmony_ci		goto free_gpio;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci	res = clk_prepare_enable(host->clk);
7418c2ecf20Sopenharmony_ci	if (res)
7428c2ecf20Sopenharmony_ci		goto put_clk;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	nand_chip->legacy.cmd_ctrl = lpc32xx_nand_cmd_ctrl;
7458c2ecf20Sopenharmony_ci	nand_chip->legacy.dev_ready = lpc32xx_nand_device_ready;
7468c2ecf20Sopenharmony_ci	nand_chip->legacy.chip_delay = 25; /* us */
7478c2ecf20Sopenharmony_ci	nand_chip->legacy.IO_ADDR_R = MLC_DATA(host->io_base);
7488c2ecf20Sopenharmony_ci	nand_chip->legacy.IO_ADDR_W = MLC_DATA(host->io_base);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/* Init NAND controller */
7518c2ecf20Sopenharmony_ci	lpc32xx_nand_setup(host);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, host);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* Initialize function pointers */
7568c2ecf20Sopenharmony_ci	nand_chip->legacy.waitfunc = lpc32xx_waitfunc;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	nand_chip->options = NAND_NO_SUBPAGE_WRITE;
7598c2ecf20Sopenharmony_ci	nand_chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
7608c2ecf20Sopenharmony_ci	nand_chip->bbt_td = &lpc32xx_nand_bbt;
7618c2ecf20Sopenharmony_ci	nand_chip->bbt_md = &lpc32xx_nand_bbt_mirror;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (use_dma) {
7648c2ecf20Sopenharmony_ci		res = lpc32xx_dma_setup(host);
7658c2ecf20Sopenharmony_ci		if (res) {
7668c2ecf20Sopenharmony_ci			res = -EIO;
7678c2ecf20Sopenharmony_ci			goto unprepare_clk;
7688c2ecf20Sopenharmony_ci		}
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	/* initially clear interrupt status */
7728c2ecf20Sopenharmony_ci	readb(MLC_IRQ_SR(host->io_base));
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	init_completion(&host->comp_nand);
7758c2ecf20Sopenharmony_ci	init_completion(&host->comp_controller);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	host->irq = platform_get_irq(pdev, 0);
7788c2ecf20Sopenharmony_ci	if (host->irq < 0) {
7798c2ecf20Sopenharmony_ci		res = -EINVAL;
7808c2ecf20Sopenharmony_ci		goto release_dma_chan;
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq,
7848c2ecf20Sopenharmony_ci			IRQF_TRIGGER_HIGH, DRV_NAME, host)) {
7858c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error requesting NAND IRQ\n");
7868c2ecf20Sopenharmony_ci		res = -ENXIO;
7878c2ecf20Sopenharmony_ci		goto release_dma_chan;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	/*
7918c2ecf20Sopenharmony_ci	 * Scan to find existence of the device and get the type of NAND device:
7928c2ecf20Sopenharmony_ci	 * SMALL block or LARGE block.
7938c2ecf20Sopenharmony_ci	 */
7948c2ecf20Sopenharmony_ci	nand_chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops;
7958c2ecf20Sopenharmony_ci	res = nand_scan(nand_chip, 1);
7968c2ecf20Sopenharmony_ci	if (res)
7978c2ecf20Sopenharmony_ci		goto free_irq;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	mtd->name = DRV_NAME;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	res = mtd_device_register(mtd, host->ncfg->parts,
8028c2ecf20Sopenharmony_ci				  host->ncfg->num_parts);
8038c2ecf20Sopenharmony_ci	if (res)
8048c2ecf20Sopenharmony_ci		goto cleanup_nand;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	return 0;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cicleanup_nand:
8098c2ecf20Sopenharmony_ci	nand_cleanup(nand_chip);
8108c2ecf20Sopenharmony_cifree_irq:
8118c2ecf20Sopenharmony_ci	free_irq(host->irq, host);
8128c2ecf20Sopenharmony_cirelease_dma_chan:
8138c2ecf20Sopenharmony_ci	if (use_dma)
8148c2ecf20Sopenharmony_ci		dma_release_channel(host->dma_chan);
8158c2ecf20Sopenharmony_ciunprepare_clk:
8168c2ecf20Sopenharmony_ci	clk_disable_unprepare(host->clk);
8178c2ecf20Sopenharmony_ciput_clk:
8188c2ecf20Sopenharmony_ci	clk_put(host->clk);
8198c2ecf20Sopenharmony_cifree_gpio:
8208c2ecf20Sopenharmony_ci	lpc32xx_wp_enable(host);
8218c2ecf20Sopenharmony_ci	gpio_free(host->ncfg->wp_gpio);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return res;
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci/*
8278c2ecf20Sopenharmony_ci * Remove NAND device
8288c2ecf20Sopenharmony_ci */
8298c2ecf20Sopenharmony_cistatic int lpc32xx_nand_remove(struct platform_device *pdev)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
8328c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->nand_chip;
8338c2ecf20Sopenharmony_ci	int ret;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
8368c2ecf20Sopenharmony_ci	WARN_ON(ret);
8378c2ecf20Sopenharmony_ci	nand_cleanup(chip);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	free_irq(host->irq, host);
8408c2ecf20Sopenharmony_ci	if (use_dma)
8418c2ecf20Sopenharmony_ci		dma_release_channel(host->dma_chan);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	clk_disable_unprepare(host->clk);
8448c2ecf20Sopenharmony_ci	clk_put(host->clk);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	lpc32xx_wp_enable(host);
8478c2ecf20Sopenharmony_ci	gpio_free(host->ncfg->wp_gpio);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	return 0;
8508c2ecf20Sopenharmony_ci}
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
8538c2ecf20Sopenharmony_cistatic int lpc32xx_nand_resume(struct platform_device *pdev)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
8568c2ecf20Sopenharmony_ci	int ret;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	/* Re-enable NAND clock */
8598c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(host->clk);
8608c2ecf20Sopenharmony_ci	if (ret)
8618c2ecf20Sopenharmony_ci		return ret;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* Fresh init of NAND controller */
8648c2ecf20Sopenharmony_ci	lpc32xx_nand_setup(host);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	/* Disable write protect */
8678c2ecf20Sopenharmony_ci	lpc32xx_wp_disable(host);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	return 0;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct lpc32xx_nand_host *host = platform_get_drvdata(pdev);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	/* Enable write protect for safety */
8778c2ecf20Sopenharmony_ci	lpc32xx_wp_enable(host);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* Disable clock */
8808c2ecf20Sopenharmony_ci	clk_disable_unprepare(host->clk);
8818c2ecf20Sopenharmony_ci	return 0;
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci#else
8858c2ecf20Sopenharmony_ci#define lpc32xx_nand_resume NULL
8868c2ecf20Sopenharmony_ci#define lpc32xx_nand_suspend NULL
8878c2ecf20Sopenharmony_ci#endif
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic const struct of_device_id lpc32xx_nand_match[] = {
8908c2ecf20Sopenharmony_ci	{ .compatible = "nxp,lpc3220-mlc" },
8918c2ecf20Sopenharmony_ci	{ /* sentinel */ },
8928c2ecf20Sopenharmony_ci};
8938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc32xx_nand_match);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_cistatic struct platform_driver lpc32xx_nand_driver = {
8968c2ecf20Sopenharmony_ci	.probe		= lpc32xx_nand_probe,
8978c2ecf20Sopenharmony_ci	.remove		= lpc32xx_nand_remove,
8988c2ecf20Sopenharmony_ci	.resume		= lpc32xx_nand_resume,
8998c2ecf20Sopenharmony_ci	.suspend	= lpc32xx_nand_suspend,
9008c2ecf20Sopenharmony_ci	.driver		= {
9018c2ecf20Sopenharmony_ci		.name	= DRV_NAME,
9028c2ecf20Sopenharmony_ci		.of_match_table = lpc32xx_nand_match,
9038c2ecf20Sopenharmony_ci	},
9048c2ecf20Sopenharmony_ci};
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cimodule_platform_driver(lpc32xx_nand_driver);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
9098c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
9108c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND driver for the NXP LPC32XX MLC controller");
911