162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* Copyright (c) 2020 Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/clk.h>
562306a36Sopenharmony_ci#include <linux/completion.h>
662306a36Sopenharmony_ci#include <linux/dmaengine.h>
762306a36Sopenharmony_ci#include <linux/dma-direction.h>
862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/iopoll.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1662306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
1762306a36Sopenharmony_ci#include <linux/mtd/nand.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/sched.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/types.h>
2462306a36Sopenharmony_ci#include <linux/units.h>
2562306a36Sopenharmony_ci#include <asm/unaligned.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define EBU_CLC			0x000
2862306a36Sopenharmony_ci#define EBU_CLC_RST		0x00000000u
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define EBU_ADDR_SEL(n)		(0x020 + (n) * 4)
3162306a36Sopenharmony_ci/* 5 bits 26:22 included for comparison in the ADDR_SELx */
3262306a36Sopenharmony_ci#define EBU_ADDR_MASK(x)	((x) << 4)
3362306a36Sopenharmony_ci#define EBU_ADDR_SEL_REGEN	0x1
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define EBU_BUSCON(n)		(0x060 + (n) * 4)
3662306a36Sopenharmony_ci#define EBU_BUSCON_CMULT_V4	0x1
3762306a36Sopenharmony_ci#define EBU_BUSCON_RECOVC(n)	((n) << 2)
3862306a36Sopenharmony_ci#define EBU_BUSCON_HOLDC(n)	((n) << 4)
3962306a36Sopenharmony_ci#define EBU_BUSCON_WAITRDC(n)	((n) << 6)
4062306a36Sopenharmony_ci#define EBU_BUSCON_WAITWRC(n)	((n) << 8)
4162306a36Sopenharmony_ci#define EBU_BUSCON_BCGEN_CS	0x0
4262306a36Sopenharmony_ci#define EBU_BUSCON_SETUP_EN	BIT(22)
4362306a36Sopenharmony_ci#define EBU_BUSCON_ALEC		0xC000
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define EBU_CON			0x0B0
4662306a36Sopenharmony_ci#define EBU_CON_NANDM_EN	BIT(0)
4762306a36Sopenharmony_ci#define EBU_CON_NANDM_DIS	0x0
4862306a36Sopenharmony_ci#define EBU_CON_CSMUX_E_EN	BIT(1)
4962306a36Sopenharmony_ci#define EBU_CON_ALE_P_LOW	BIT(2)
5062306a36Sopenharmony_ci#define EBU_CON_CLE_P_LOW	BIT(3)
5162306a36Sopenharmony_ci#define EBU_CON_CS_P_LOW	BIT(4)
5262306a36Sopenharmony_ci#define EBU_CON_SE_P_LOW	BIT(5)
5362306a36Sopenharmony_ci#define EBU_CON_WP_P_LOW	BIT(6)
5462306a36Sopenharmony_ci#define EBU_CON_PRE_P_LOW	BIT(7)
5562306a36Sopenharmony_ci#define EBU_CON_IN_CS_S(n)	((n) << 8)
5662306a36Sopenharmony_ci#define EBU_CON_OUT_CS_S(n)	((n) << 10)
5762306a36Sopenharmony_ci#define EBU_CON_LAT_EN_CS_P	((0x3D) << 18)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define EBU_WAIT		0x0B4
6062306a36Sopenharmony_ci#define EBU_WAIT_RDBY		BIT(0)
6162306a36Sopenharmony_ci#define EBU_WAIT_WR_C		BIT(3)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define HSNAND_CTL1		0x110
6462306a36Sopenharmony_ci#define HSNAND_CTL1_ADDR_SHIFT	24
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define HSNAND_CTL2		0x114
6762306a36Sopenharmony_ci#define HSNAND_CTL2_ADDR_SHIFT	8
6862306a36Sopenharmony_ci#define HSNAND_CTL2_CYC_N_V5	(0x2 << 16)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define HSNAND_INT_MSK_CTL	0x124
7162306a36Sopenharmony_ci#define HSNAND_INT_MSK_CTL_WR_C	BIT(4)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define HSNAND_INT_STA		0x128
7462306a36Sopenharmony_ci#define HSNAND_INT_STA_WR_C	BIT(4)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define HSNAND_CTL		0x130
7762306a36Sopenharmony_ci#define HSNAND_CTL_ENABLE_ECC	BIT(0)
7862306a36Sopenharmony_ci#define HSNAND_CTL_GO		BIT(2)
7962306a36Sopenharmony_ci#define HSNAND_CTL_CE_SEL_CS(n)	BIT(3 + (n))
8062306a36Sopenharmony_ci#define HSNAND_CTL_RW_READ	0x0
8162306a36Sopenharmony_ci#define HSNAND_CTL_RW_WRITE	BIT(10)
8262306a36Sopenharmony_ci#define HSNAND_CTL_ECC_OFF_V8TH	BIT(11)
8362306a36Sopenharmony_ci#define HSNAND_CTL_CKFF_EN	0x0
8462306a36Sopenharmony_ci#define HSNAND_CTL_MSG_EN	BIT(17)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define HSNAND_PARA0		0x13c
8762306a36Sopenharmony_ci#define HSNAND_PARA0_PAGE_V8192	0x3
8862306a36Sopenharmony_ci#define HSNAND_PARA0_PIB_V256	(0x3 << 4)
8962306a36Sopenharmony_ci#define HSNAND_PARA0_BYP_EN_NP	0x0
9062306a36Sopenharmony_ci#define HSNAND_PARA0_BYP_DEC_NP	0x0
9162306a36Sopenharmony_ci#define HSNAND_PARA0_TYPE_ONFI	BIT(18)
9262306a36Sopenharmony_ci#define HSNAND_PARA0_ADEP_EN	BIT(21)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define HSNAND_CMSG_0		0x150
9562306a36Sopenharmony_ci#define HSNAND_CMSG_1		0x154
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci#define HSNAND_ALE_OFFS		BIT(2)
9862306a36Sopenharmony_ci#define HSNAND_CLE_OFFS		BIT(3)
9962306a36Sopenharmony_ci#define HSNAND_CS_OFFS		BIT(4)
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define HSNAND_ECC_OFFSET	0x008
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define MAX_CS	2
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define USEC_PER_SEC	1000000L
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistruct ebu_nand_cs {
10862306a36Sopenharmony_ci	void __iomem *chipaddr;
10962306a36Sopenharmony_ci	u32 addr_sel;
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistruct ebu_nand_controller {
11362306a36Sopenharmony_ci	struct nand_controller controller;
11462306a36Sopenharmony_ci	struct nand_chip chip;
11562306a36Sopenharmony_ci	struct device *dev;
11662306a36Sopenharmony_ci	void __iomem *ebu;
11762306a36Sopenharmony_ci	void __iomem *hsnand;
11862306a36Sopenharmony_ci	struct dma_chan *dma_tx;
11962306a36Sopenharmony_ci	struct dma_chan *dma_rx;
12062306a36Sopenharmony_ci	struct completion dma_access_complete;
12162306a36Sopenharmony_ci	struct clk *clk;
12262306a36Sopenharmony_ci	u32 nd_para0;
12362306a36Sopenharmony_ci	u8 cs_num;
12462306a36Sopenharmony_ci	struct ebu_nand_cs cs[MAX_CS];
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	return container_of(chip, struct ebu_nand_controller, chip);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic int ebu_nand_waitrdy(struct nand_chip *chip, int timeout_ms)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
13562306a36Sopenharmony_ci	u32 status;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return readl_poll_timeout(ctrl->ebu + EBU_WAIT, status,
13862306a36Sopenharmony_ci				  (status & EBU_WAIT_RDBY) ||
13962306a36Sopenharmony_ci				  (status & EBU_WAIT_WR_C), 20, timeout_ms);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic u8 ebu_nand_readb(struct nand_chip *chip)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
14562306a36Sopenharmony_ci	u8 cs_num = ebu_host->cs_num;
14662306a36Sopenharmony_ci	u8 val;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS);
14962306a36Sopenharmony_ci	ebu_nand_waitrdy(chip, 1000);
15062306a36Sopenharmony_ci	return val;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
15662306a36Sopenharmony_ci	u8 cs_num = ebu_host->cs_num;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	writeb(value, ebu_host->cs[cs_num].chipaddr + offset);
15962306a36Sopenharmony_ci	ebu_nand_waitrdy(chip, 1000);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	int i;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	for (i = 0; i < len; i++)
16762306a36Sopenharmony_ci		buf[i] = ebu_nand_readb(chip);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	int i;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	for (i = 0; i < len; i++)
17562306a36Sopenharmony_ci		ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void ebu_nand_disable(struct nand_chip *chip)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	writel(0, ebu_host->ebu + EBU_CON);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void ebu_select_chip(struct nand_chip *chip)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
18862306a36Sopenharmony_ci	void __iomem *nand_con = ebu_host->ebu + EBU_CON;
18962306a36Sopenharmony_ci	u32 cs = ebu_host->cs_num;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW |
19262306a36Sopenharmony_ci	       EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW |
19362306a36Sopenharmony_ci	       EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) |
19462306a36Sopenharmony_ci	       EBU_CON_LAT_EN_CS_P, nand_con);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int ebu_nand_set_timings(struct nand_chip *chip, int csline,
19862306a36Sopenharmony_ci				const struct nand_interface_config *conf)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct ebu_nand_controller *ctrl = nand_to_ebu(chip);
20162306a36Sopenharmony_ci	unsigned int rate = clk_get_rate(ctrl->clk) / HZ_PER_MHZ;
20262306a36Sopenharmony_ci	unsigned int period = DIV_ROUND_UP(USEC_PER_SEC, rate);
20362306a36Sopenharmony_ci	const struct nand_sdr_timings *timings;
20462306a36Sopenharmony_ci	u32 trecov, thold, twrwait, trdwait;
20562306a36Sopenharmony_ci	u32 reg = 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	timings = nand_get_sdr_timings(conf);
20862306a36Sopenharmony_ci	if (IS_ERR(timings))
20962306a36Sopenharmony_ci		return PTR_ERR(timings);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
21262306a36Sopenharmony_ci		return 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min),
21562306a36Sopenharmony_ci			      period);
21662306a36Sopenharmony_ci	reg |= EBU_BUSCON_RECOVC(trecov);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period);
21962306a36Sopenharmony_ci	reg |= EBU_BUSCON_HOLDC(thold);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min),
22262306a36Sopenharmony_ci			       period);
22362306a36Sopenharmony_ci	reg |= EBU_BUSCON_WAITRDC(trdwait);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period);
22662306a36Sopenharmony_ci	reg |= EBU_BUSCON_WAITWRC(twrwait);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC |
22962306a36Sopenharmony_ci		EBU_BUSCON_SETUP_EN;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num));
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
23762306a36Sopenharmony_ci				  struct mtd_oob_region *oobregion)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (section)
24262306a36Sopenharmony_ci		return -ERANGE;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	oobregion->offset = HSNAND_ECC_OFFSET;
24562306a36Sopenharmony_ci	oobregion->length = chip->ecc.total;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section,
25162306a36Sopenharmony_ci				   struct mtd_oob_region *oobregion)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (section)
25662306a36Sopenharmony_ci		return -ERANGE;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET;
25962306a36Sopenharmony_ci	oobregion->length = mtd->oobsize - oobregion->offset;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return 0;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = {
26562306a36Sopenharmony_ci	.ecc = ebu_nand_ooblayout_ecc,
26662306a36Sopenharmony_ci	.free = ebu_nand_ooblayout_free,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic void ebu_dma_rx_callback(void *cookie)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = cookie;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	dmaengine_terminate_async(ebu_host->dma_rx);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	complete(&ebu_host->dma_access_complete);
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void ebu_dma_tx_callback(void *cookie)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = cookie;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dmaengine_terminate_async(ebu_host->dma_tx);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	complete(&ebu_host->dma_access_complete);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
28862306a36Sopenharmony_ci			 const u8 *buf, u32 len)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
29162306a36Sopenharmony_ci	struct completion *dma_completion;
29262306a36Sopenharmony_ci	dma_async_tx_callback callback;
29362306a36Sopenharmony_ci	struct dma_chan *chan;
29462306a36Sopenharmony_ci	dma_cookie_t cookie;
29562306a36Sopenharmony_ci	unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
29662306a36Sopenharmony_ci	dma_addr_t buf_dma;
29762306a36Sopenharmony_ci	int ret;
29862306a36Sopenharmony_ci	u32 timeout;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
30162306a36Sopenharmony_ci		chan = ebu_host->dma_rx;
30262306a36Sopenharmony_ci		dma_completion = &ebu_host->dma_access_complete;
30362306a36Sopenharmony_ci		callback = ebu_dma_rx_callback;
30462306a36Sopenharmony_ci	} else {
30562306a36Sopenharmony_ci		chan = ebu_host->dma_tx;
30662306a36Sopenharmony_ci		dma_completion = &ebu_host->dma_access_complete;
30762306a36Sopenharmony_ci		callback = ebu_dma_tx_callback;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir);
31162306a36Sopenharmony_ci	if (dma_mapping_error(chan->device->dev, buf_dma)) {
31262306a36Sopenharmony_ci		dev_err(ebu_host->dev, "Failed to map DMA buffer\n");
31362306a36Sopenharmony_ci		ret = -EIO;
31462306a36Sopenharmony_ci		goto err_unmap;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags);
31862306a36Sopenharmony_ci	if (!tx) {
31962306a36Sopenharmony_ci		ret = -ENXIO;
32062306a36Sopenharmony_ci		goto err_unmap;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	tx->callback = callback;
32462306a36Sopenharmony_ci	tx->callback_param = ebu_host;
32562306a36Sopenharmony_ci	cookie = tx->tx_submit(tx);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ret = dma_submit_error(cookie);
32862306a36Sopenharmony_ci	if (ret) {
32962306a36Sopenharmony_ci		dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie);
33062306a36Sopenharmony_ci		ret = -EIO;
33162306a36Sopenharmony_ci		goto err_unmap;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	init_completion(dma_completion);
33562306a36Sopenharmony_ci	dma_async_issue_pending(chan);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Wait DMA to finish the data transfer.*/
33862306a36Sopenharmony_ci	timeout = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
33962306a36Sopenharmony_ci	if (!timeout) {
34062306a36Sopenharmony_ci		dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n",
34162306a36Sopenharmony_ci			dmaengine_tx_status(chan, cookie, NULL));
34262306a36Sopenharmony_ci		dmaengine_terminate_sync(chan);
34362306a36Sopenharmony_ci		ret = -ETIMEDOUT;
34462306a36Sopenharmony_ci		goto err_unmap;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	return 0;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cierr_unmap:
35062306a36Sopenharmony_ci	dma_unmap_single(ebu_host->dev, buf_dma, len, dir);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return ret;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void ebu_nand_trigger(struct ebu_nand_controller *ebu_host,
35662306a36Sopenharmony_ci			     int page, u32 cmd)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	unsigned int val;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT;
36162306a36Sopenharmony_ci	writel(val, ebu_host->hsnand + HSNAND_CTL1);
36262306a36Sopenharmony_ci	val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5;
36362306a36Sopenharmony_ci	writel(val, ebu_host->hsnand + HSNAND_CTL2);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* clear first, will update later */
36862306a36Sopenharmony_ci	writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0);
36962306a36Sopenharmony_ci	writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	writel(HSNAND_INT_MSK_CTL_WR_C,
37262306a36Sopenharmony_ci	       ebu_host->hsnand + HSNAND_INT_MSK_CTL);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (!cmd)
37562306a36Sopenharmony_ci		val = HSNAND_CTL_RW_READ;
37662306a36Sopenharmony_ci	else
37762306a36Sopenharmony_ci		val = HSNAND_CTL_RW_WRITE;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN |
38062306a36Sopenharmony_ci	       HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) |
38162306a36Sopenharmony_ci	       HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val,
38262306a36Sopenharmony_ci	       ebu_host->hsnand + HSNAND_CTL);
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
38662306a36Sopenharmony_ci				    int oob_required, int page)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
38962306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
39062306a36Sopenharmony_ci	int ret, reg_data;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize);
39562306a36Sopenharmony_ci	if (ret)
39662306a36Sopenharmony_ci		return ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (oob_required)
39962306a36Sopenharmony_ci		chip->ecc.read_oob(chip, page);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	reg_data = readl(ebu_host->hsnand + HSNAND_CTL);
40262306a36Sopenharmony_ci	reg_data &= ~HSNAND_CTL_GO;
40362306a36Sopenharmony_ci	writel(reg_data, ebu_host->hsnand + HSNAND_CTL);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf,
40962306a36Sopenharmony_ci				     int oob_required, int page)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
41262306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
41362306a36Sopenharmony_ci	void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA;
41462306a36Sopenharmony_ci	int reg_data, ret, val;
41562306a36Sopenharmony_ci	u32 reg;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize);
42062306a36Sopenharmony_ci	if (ret)
42162306a36Sopenharmony_ci		return ret;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (oob_required) {
42462306a36Sopenharmony_ci		reg = get_unaligned_le32(chip->oob_poi);
42562306a36Sopenharmony_ci		writel(reg, ebu_host->hsnand + HSNAND_CMSG_0);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci		reg = get_unaligned_le32(chip->oob_poi + 4);
42862306a36Sopenharmony_ci		writel(reg, ebu_host->hsnand + HSNAND_CMSG_1);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	ret = readl_poll_timeout_atomic(int_sta, val, !(val & HSNAND_INT_STA_WR_C),
43262306a36Sopenharmony_ci					10, 1000);
43362306a36Sopenharmony_ci	if (ret)
43462306a36Sopenharmony_ci		return ret;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	reg_data = readl(ebu_host->hsnand + HSNAND_CTL);
43762306a36Sopenharmony_ci	reg_data &= ~HSNAND_CTL_GO;
43862306a36Sopenharmony_ci	writel(reg_data, ebu_host->hsnand + HSNAND_CTL);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return 0;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, };
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic int ebu_nand_attach_chip(struct nand_chip *chip)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
44862306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip);
44962306a36Sopenharmony_ci	u32 ecc_steps, ecc_bytes, ecc_total, pagesize, pg_per_blk;
45062306a36Sopenharmony_ci	u32 ecc_strength_ds = chip->ecc.strength;
45162306a36Sopenharmony_ci	u32 ecc_size = chip->ecc.size;
45262306a36Sopenharmony_ci	u32 writesize = mtd->writesize;
45362306a36Sopenharmony_ci	u32 blocksize = mtd->erasesize;
45462306a36Sopenharmony_ci	int bch_algo, start, val;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* Default to an ECC size of 512 */
45762306a36Sopenharmony_ci	if (!chip->ecc.size)
45862306a36Sopenharmony_ci		chip->ecc.size = 512;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	switch (ecc_size) {
46162306a36Sopenharmony_ci	case 512:
46262306a36Sopenharmony_ci		start = 1;
46362306a36Sopenharmony_ci		if (!ecc_strength_ds)
46462306a36Sopenharmony_ci			ecc_strength_ds = 4;
46562306a36Sopenharmony_ci		break;
46662306a36Sopenharmony_ci	case 1024:
46762306a36Sopenharmony_ci		start = 4;
46862306a36Sopenharmony_ci		if (!ecc_strength_ds)
46962306a36Sopenharmony_ci			ecc_strength_ds = 32;
47062306a36Sopenharmony_ci		break;
47162306a36Sopenharmony_ci	default:
47262306a36Sopenharmony_ci		return -EINVAL;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* BCH ECC algorithm Settings for number of bits per 512B/1024B */
47662306a36Sopenharmony_ci	bch_algo = round_up(start + 1, 4);
47762306a36Sopenharmony_ci	for (val = start; val < bch_algo; val++) {
47862306a36Sopenharmony_ci		if (ecc_strength_ds == ecc_strength[val])
47962306a36Sopenharmony_ci			break;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	if (val == bch_algo)
48262306a36Sopenharmony_ci		return -EINVAL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (ecc_strength_ds == 8)
48562306a36Sopenharmony_ci		ecc_bytes = 14;
48662306a36Sopenharmony_ci	else
48762306a36Sopenharmony_ci		ecc_bytes = DIV_ROUND_UP(ecc_strength_ds * fls(8 * ecc_size), 8);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	ecc_steps = writesize / ecc_size;
49062306a36Sopenharmony_ci	ecc_total = ecc_steps * ecc_bytes;
49162306a36Sopenharmony_ci	if ((ecc_total + 8) > mtd->oobsize)
49262306a36Sopenharmony_ci		return -ERANGE;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	chip->ecc.total = ecc_total;
49562306a36Sopenharmony_ci	pagesize = fls(writesize >> 11);
49662306a36Sopenharmony_ci	if (pagesize > HSNAND_PARA0_PAGE_V8192)
49762306a36Sopenharmony_ci		return -ERANGE;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	pg_per_blk = fls((blocksize / writesize) >> 6) / 8;
50062306a36Sopenharmony_ci	if (pg_per_blk > HSNAND_PARA0_PIB_V256)
50162306a36Sopenharmony_ci		return -ERANGE;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP |
50462306a36Sopenharmony_ci			     HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN |
50562306a36Sopenharmony_ci			     HSNAND_PARA0_TYPE_ONFI | (val << 29);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops);
50862306a36Sopenharmony_ci	chip->ecc.read_page = ebu_nand_read_page_hwecc;
50962306a36Sopenharmony_ci	chip->ecc.write_page = ebu_nand_write_page_hwecc;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int ebu_nand_exec_op(struct nand_chip *chip,
51562306a36Sopenharmony_ci			    const struct nand_operation *op, bool check_only)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	const struct nand_op_instr *instr = NULL;
51862306a36Sopenharmony_ci	unsigned int op_id;
51962306a36Sopenharmony_ci	int i, timeout_ms, ret = 0;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (check_only)
52262306a36Sopenharmony_ci		return 0;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	ebu_select_chip(chip);
52562306a36Sopenharmony_ci	for (op_id = 0; op_id < op->ninstrs; op_id++) {
52662306a36Sopenharmony_ci		instr = &op->instrs[op_id];
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		switch (instr->type) {
52962306a36Sopenharmony_ci		case NAND_OP_CMD_INSTR:
53062306a36Sopenharmony_ci			ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS,
53162306a36Sopenharmony_ci					instr->ctx.cmd.opcode);
53262306a36Sopenharmony_ci			break;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		case NAND_OP_ADDR_INSTR:
53562306a36Sopenharmony_ci			for (i = 0; i < instr->ctx.addr.naddrs; i++)
53662306a36Sopenharmony_ci				ebu_nand_writeb(chip,
53762306a36Sopenharmony_ci						HSNAND_ALE_OFFS | HSNAND_CS_OFFS,
53862306a36Sopenharmony_ci						instr->ctx.addr.addrs[i]);
53962306a36Sopenharmony_ci			break;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		case NAND_OP_DATA_IN_INSTR:
54262306a36Sopenharmony_ci			ebu_read_buf(chip, instr->ctx.data.buf.in,
54362306a36Sopenharmony_ci				     instr->ctx.data.len);
54462306a36Sopenharmony_ci			break;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		case NAND_OP_DATA_OUT_INSTR:
54762306a36Sopenharmony_ci			ebu_write_buf(chip, instr->ctx.data.buf.out,
54862306a36Sopenharmony_ci				      instr->ctx.data.len);
54962306a36Sopenharmony_ci			break;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		case NAND_OP_WAITRDY_INSTR:
55262306a36Sopenharmony_ci			timeout_ms = instr->ctx.waitrdy.timeout_ms * 1000;
55362306a36Sopenharmony_ci			ret = ebu_nand_waitrdy(chip, timeout_ms);
55462306a36Sopenharmony_ci			break;
55562306a36Sopenharmony_ci		}
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	return ret;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic const struct nand_controller_ops ebu_nand_controller_ops = {
56262306a36Sopenharmony_ci	.attach_chip = ebu_nand_attach_chip,
56362306a36Sopenharmony_ci	.setup_interface = ebu_nand_set_timings,
56462306a36Sopenharmony_ci	.exec_op = ebu_nand_exec_op,
56562306a36Sopenharmony_ci};
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	if (ebu_host->dma_rx)
57062306a36Sopenharmony_ci		dma_release_channel(ebu_host->dma_rx);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (ebu_host->dma_tx)
57362306a36Sopenharmony_ci		dma_release_channel(ebu_host->dma_tx);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic int ebu_nand_probe(struct platform_device *pdev)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
57962306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host;
58062306a36Sopenharmony_ci	struct device_node *chip_np;
58162306a36Sopenharmony_ci	struct nand_chip *nand;
58262306a36Sopenharmony_ci	struct mtd_info *mtd;
58362306a36Sopenharmony_ci	struct resource *res;
58462306a36Sopenharmony_ci	char *resname;
58562306a36Sopenharmony_ci	int ret;
58662306a36Sopenharmony_ci	u32 cs;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL);
58962306a36Sopenharmony_ci	if (!ebu_host)
59062306a36Sopenharmony_ci		return -ENOMEM;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	ebu_host->dev = dev;
59362306a36Sopenharmony_ci	nand_controller_init(&ebu_host->controller);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	ebu_host->ebu = devm_platform_ioremap_resource_byname(pdev, "ebunand");
59662306a36Sopenharmony_ci	if (IS_ERR(ebu_host->ebu))
59762306a36Sopenharmony_ci		return PTR_ERR(ebu_host->ebu);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	ebu_host->hsnand = devm_platform_ioremap_resource_byname(pdev, "hsnand");
60062306a36Sopenharmony_ci	if (IS_ERR(ebu_host->hsnand))
60162306a36Sopenharmony_ci		return PTR_ERR(ebu_host->hsnand);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	chip_np = of_get_next_child(dev->of_node, NULL);
60462306a36Sopenharmony_ci	if (!chip_np)
60562306a36Sopenharmony_ci		return dev_err_probe(dev, -EINVAL,
60662306a36Sopenharmony_ci				     "Could not find child node for the NAND chip\n");
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	ret = of_property_read_u32(chip_np, "reg", &cs);
60962306a36Sopenharmony_ci	if (ret) {
61062306a36Sopenharmony_ci		dev_err(dev, "failed to get chip select: %d\n", ret);
61162306a36Sopenharmony_ci		goto err_of_node_put;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci	if (cs >= MAX_CS) {
61462306a36Sopenharmony_ci		dev_err(dev, "got invalid chip select: %d\n", cs);
61562306a36Sopenharmony_ci		ret = -EINVAL;
61662306a36Sopenharmony_ci		goto err_of_node_put;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	ebu_host->cs_num = cs;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs);
62262306a36Sopenharmony_ci	if (!resname) {
62362306a36Sopenharmony_ci		ret = -ENOMEM;
62462306a36Sopenharmony_ci		goto err_of_node_put;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	ebu_host->cs[cs].chipaddr = devm_platform_ioremap_resource_byname(pdev,
62862306a36Sopenharmony_ci									  resname);
62962306a36Sopenharmony_ci	if (IS_ERR(ebu_host->cs[cs].chipaddr)) {
63062306a36Sopenharmony_ci		ret = PTR_ERR(ebu_host->cs[cs].chipaddr);
63162306a36Sopenharmony_ci		goto err_of_node_put;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ebu_host->clk = devm_clk_get_enabled(dev, NULL);
63562306a36Sopenharmony_ci	if (IS_ERR(ebu_host->clk)) {
63662306a36Sopenharmony_ci		ret = dev_err_probe(dev, PTR_ERR(ebu_host->clk),
63762306a36Sopenharmony_ci				    "failed to get and enable clock\n");
63862306a36Sopenharmony_ci		goto err_of_node_put;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	ebu_host->dma_tx = dma_request_chan(dev, "tx");
64262306a36Sopenharmony_ci	if (IS_ERR(ebu_host->dma_tx)) {
64362306a36Sopenharmony_ci		ret = dev_err_probe(dev, PTR_ERR(ebu_host->dma_tx),
64462306a36Sopenharmony_ci				    "failed to request DMA tx chan!.\n");
64562306a36Sopenharmony_ci		goto err_of_node_put;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	ebu_host->dma_rx = dma_request_chan(dev, "rx");
64962306a36Sopenharmony_ci	if (IS_ERR(ebu_host->dma_rx)) {
65062306a36Sopenharmony_ci		ret = dev_err_probe(dev, PTR_ERR(ebu_host->dma_rx),
65162306a36Sopenharmony_ci				    "failed to request DMA rx chan!.\n");
65262306a36Sopenharmony_ci		ebu_host->dma_rx = NULL;
65362306a36Sopenharmony_ci		goto err_cleanup_dma;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", cs);
65762306a36Sopenharmony_ci	if (!resname) {
65862306a36Sopenharmony_ci		ret = -ENOMEM;
65962306a36Sopenharmony_ci		goto err_cleanup_dma;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname);
66362306a36Sopenharmony_ci	if (!res) {
66462306a36Sopenharmony_ci		ret = -EINVAL;
66562306a36Sopenharmony_ci		goto err_cleanup_dma;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci	ebu_host->cs[cs].addr_sel = res->start;
66862306a36Sopenharmony_ci	writel(ebu_host->cs[cs].addr_sel | EBU_ADDR_MASK(5) | EBU_ADDR_SEL_REGEN,
66962306a36Sopenharmony_ci	       ebu_host->ebu + EBU_ADDR_SEL(cs));
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	nand_set_flash_node(&ebu_host->chip, chip_np);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	mtd = nand_to_mtd(&ebu_host->chip);
67462306a36Sopenharmony_ci	if (!mtd->name) {
67562306a36Sopenharmony_ci		dev_err(ebu_host->dev, "NAND label property is mandatory\n");
67662306a36Sopenharmony_ci		ret = -EINVAL;
67762306a36Sopenharmony_ci		goto err_cleanup_dma;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	mtd->dev.parent = dev;
68162306a36Sopenharmony_ci	ebu_host->dev = dev;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	platform_set_drvdata(pdev, ebu_host);
68462306a36Sopenharmony_ci	nand_set_controller_data(&ebu_host->chip, ebu_host);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	nand = &ebu_host->chip;
68762306a36Sopenharmony_ci	nand->controller = &ebu_host->controller;
68862306a36Sopenharmony_ci	nand->controller->ops = &ebu_nand_controller_ops;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* Scan to find existence of the device */
69162306a36Sopenharmony_ci	ret = nand_scan(&ebu_host->chip, 1);
69262306a36Sopenharmony_ci	if (ret)
69362306a36Sopenharmony_ci		goto err_cleanup_dma;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
69662306a36Sopenharmony_ci	if (ret)
69762306a36Sopenharmony_ci		goto err_clean_nand;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	return 0;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cierr_clean_nand:
70262306a36Sopenharmony_ci	nand_cleanup(&ebu_host->chip);
70362306a36Sopenharmony_cierr_cleanup_dma:
70462306a36Sopenharmony_ci	ebu_dma_cleanup(ebu_host);
70562306a36Sopenharmony_cierr_of_node_put:
70662306a36Sopenharmony_ci	of_node_put(chip_np);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return ret;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void ebu_nand_remove(struct platform_device *pdev)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev);
71462306a36Sopenharmony_ci	int ret;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(&ebu_host->chip));
71762306a36Sopenharmony_ci	WARN_ON(ret);
71862306a36Sopenharmony_ci	nand_cleanup(&ebu_host->chip);
71962306a36Sopenharmony_ci	ebu_nand_disable(&ebu_host->chip);
72062306a36Sopenharmony_ci	ebu_dma_cleanup(ebu_host);
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic const struct of_device_id ebu_nand_match[] = {
72462306a36Sopenharmony_ci	{ .compatible = "intel,lgm-ebunand" },
72562306a36Sopenharmony_ci	{}
72662306a36Sopenharmony_ci};
72762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ebu_nand_match);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cistatic struct platform_driver ebu_nand_driver = {
73062306a36Sopenharmony_ci	.probe = ebu_nand_probe,
73162306a36Sopenharmony_ci	.remove_new = ebu_nand_remove,
73262306a36Sopenharmony_ci	.driver = {
73362306a36Sopenharmony_ci		.name = "intel-nand-controller",
73462306a36Sopenharmony_ci		.of_match_table = ebu_nand_match,
73562306a36Sopenharmony_ci	},
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci};
73862306a36Sopenharmony_cimodule_platform_driver(ebu_nand_driver);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
74162306a36Sopenharmony_ciMODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>");
74262306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver");
743