162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SuperH FLCTL nand controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2008 Renesas Solutions Corp.
662306a36Sopenharmony_ci * Copyright (c) 2008 Atom Create Engineering Co., Ltd.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/completion.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/dmaengine.h>
1662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/platform_device.h>
2162306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2262306a36Sopenharmony_ci#include <linux/sh_dma.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/string.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2762306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
2862306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
2962306a36Sopenharmony_ci#include <linux/mtd/sh_flctl.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section,
3262306a36Sopenharmony_ci					struct mtd_oob_region *oobregion)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (section)
3762306a36Sopenharmony_ci		return -ERANGE;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	oobregion->offset = 0;
4062306a36Sopenharmony_ci	oobregion->length = chip->ecc.bytes;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section,
4662306a36Sopenharmony_ci					 struct mtd_oob_region *oobregion)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (section)
4962306a36Sopenharmony_ci		return -ERANGE;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	oobregion->offset = 12;
5262306a36Sopenharmony_ci	oobregion->length = 4;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = {
5862306a36Sopenharmony_ci	.ecc = flctl_4secc_ooblayout_sp_ecc,
5962306a36Sopenharmony_ci	.free = flctl_4secc_ooblayout_sp_free,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section,
6362306a36Sopenharmony_ci					struct mtd_oob_region *oobregion)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
6862306a36Sopenharmony_ci		return -ERANGE;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	oobregion->offset = (section * 16) + 6;
7162306a36Sopenharmony_ci	oobregion->length = chip->ecc.bytes;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section,
7762306a36Sopenharmony_ci					 struct mtd_oob_region *oobregion)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
8262306a36Sopenharmony_ci		return -ERANGE;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	oobregion->offset = section * 16;
8562306a36Sopenharmony_ci	oobregion->length = 6;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (!section) {
8862306a36Sopenharmony_ci		oobregion->offset += 2;
8962306a36Sopenharmony_ci		oobregion->length -= 2;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
9662306a36Sopenharmony_ci	.ecc = flctl_4secc_ooblayout_lp_ecc,
9762306a36Sopenharmony_ci	.free = flctl_4secc_ooblayout_lp_free,
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff, 0xff };
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct nand_bbt_descr flctl_4secc_smallpage = {
10362306a36Sopenharmony_ci	.offs = 11,
10462306a36Sopenharmony_ci	.len = 1,
10562306a36Sopenharmony_ci	.pattern = scan_ff_pattern,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic struct nand_bbt_descr flctl_4secc_largepage = {
10962306a36Sopenharmony_ci	.offs = 0,
11062306a36Sopenharmony_ci	.len = 2,
11162306a36Sopenharmony_ci	.pattern = scan_ff_pattern,
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic void empty_fifo(struct sh_flctl *flctl)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl));
11762306a36Sopenharmony_ci	writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void start_translation(struct sh_flctl *flctl)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	writeb(TRSTRT, FLTRCR(flctl));
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void timeout_error(struct sh_flctl *flctl, const char *str)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void wait_completion(struct sh_flctl *flctl)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	uint32_t timeout = LOOP_TIMEOUT_MAX;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	while (timeout--) {
13562306a36Sopenharmony_ci		if (readb(FLTRCR(flctl)) & TREND) {
13662306a36Sopenharmony_ci			writeb(0x0, FLTRCR(flctl));
13762306a36Sopenharmony_ci			return;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		udelay(1);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	timeout_error(flctl, __func__);
14362306a36Sopenharmony_ci	writeb(0x0, FLTRCR(flctl));
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void flctl_dma_complete(void *param)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct sh_flctl *flctl = param;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	complete(&flctl->dma_complete);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void flctl_release_dma(struct sh_flctl *flctl)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	if (flctl->chan_fifo0_rx) {
15662306a36Sopenharmony_ci		dma_release_channel(flctl->chan_fifo0_rx);
15762306a36Sopenharmony_ci		flctl->chan_fifo0_rx = NULL;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci	if (flctl->chan_fifo0_tx) {
16062306a36Sopenharmony_ci		dma_release_channel(flctl->chan_fifo0_tx);
16162306a36Sopenharmony_ci		flctl->chan_fifo0_tx = NULL;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic void flctl_setup_dma(struct sh_flctl *flctl)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	dma_cap_mask_t mask;
16862306a36Sopenharmony_ci	struct dma_slave_config cfg;
16962306a36Sopenharmony_ci	struct platform_device *pdev = flctl->pdev;
17062306a36Sopenharmony_ci	struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);
17162306a36Sopenharmony_ci	int ret;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!pdata)
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
17762306a36Sopenharmony_ci		return;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* We can only either use DMA for both Tx and Rx or not use it at all */
18062306a36Sopenharmony_ci	dma_cap_zero(mask);
18162306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, mask);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
18462306a36Sopenharmony_ci				(void *)(uintptr_t)pdata->slave_id_fifo0_tx);
18562306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
18662306a36Sopenharmony_ci		flctl->chan_fifo0_tx);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!flctl->chan_fifo0_tx)
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	memset(&cfg, 0, sizeof(cfg));
19262306a36Sopenharmony_ci	cfg.direction = DMA_MEM_TO_DEV;
19362306a36Sopenharmony_ci	cfg.dst_addr = flctl->fifo;
19462306a36Sopenharmony_ci	cfg.src_addr = 0;
19562306a36Sopenharmony_ci	ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
19662306a36Sopenharmony_ci	if (ret < 0)
19762306a36Sopenharmony_ci		goto err;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
20062306a36Sopenharmony_ci				(void *)(uintptr_t)pdata->slave_id_fifo0_rx);
20162306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
20262306a36Sopenharmony_ci		flctl->chan_fifo0_rx);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (!flctl->chan_fifo0_rx)
20562306a36Sopenharmony_ci		goto err;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	cfg.direction = DMA_DEV_TO_MEM;
20862306a36Sopenharmony_ci	cfg.dst_addr = 0;
20962306a36Sopenharmony_ci	cfg.src_addr = flctl->fifo;
21062306a36Sopenharmony_ci	ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
21162306a36Sopenharmony_ci	if (ret < 0)
21262306a36Sopenharmony_ci		goto err;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	init_completion(&flctl->dma_complete);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cierr:
21962306a36Sopenharmony_ci	flctl_release_dma(flctl);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
22562306a36Sopenharmony_ci	uint32_t addr = 0;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (column == -1) {
22862306a36Sopenharmony_ci		addr = page_addr;	/* ERASE1 */
22962306a36Sopenharmony_ci	} else if (page_addr != -1) {
23062306a36Sopenharmony_ci		/* SEQIN, READ0, etc.. */
23162306a36Sopenharmony_ci		if (flctl->chip.options & NAND_BUSWIDTH_16)
23262306a36Sopenharmony_ci			column >>= 1;
23362306a36Sopenharmony_ci		if (flctl->page_size) {
23462306a36Sopenharmony_ci			addr = column & 0x0FFF;
23562306a36Sopenharmony_ci			addr |= (page_addr & 0xff) << 16;
23662306a36Sopenharmony_ci			addr |= ((page_addr >> 8) & 0xff) << 24;
23762306a36Sopenharmony_ci			/* big than 128MB */
23862306a36Sopenharmony_ci			if (flctl->rw_ADRCNT == ADRCNT2_E) {
23962306a36Sopenharmony_ci				uint32_t 	addr2;
24062306a36Sopenharmony_ci				addr2 = (page_addr >> 16) & 0xff;
24162306a36Sopenharmony_ci				writel(addr2, FLADR2(flctl));
24262306a36Sopenharmony_ci			}
24362306a36Sopenharmony_ci		} else {
24462306a36Sopenharmony_ci			addr = column;
24562306a36Sopenharmony_ci			addr |= (page_addr & 0xff) << 8;
24662306a36Sopenharmony_ci			addr |= ((page_addr >> 8) & 0xff) << 16;
24762306a36Sopenharmony_ci			addr |= ((page_addr >> 16) & 0xff) << 24;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	writel(addr, FLADR(flctl));
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic void wait_rfifo_ready(struct sh_flctl *flctl)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	uint32_t timeout = LOOP_TIMEOUT_MAX;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	while (timeout--) {
25862306a36Sopenharmony_ci		uint32_t val;
25962306a36Sopenharmony_ci		/* check FIFO */
26062306a36Sopenharmony_ci		val = readl(FLDTCNTR(flctl)) >> 16;
26162306a36Sopenharmony_ci		if (val & 0xFF)
26262306a36Sopenharmony_ci			return;
26362306a36Sopenharmony_ci		udelay(1);
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci	timeout_error(flctl, __func__);
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic void wait_wfifo_ready(struct sh_flctl *flctl)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	uint32_t len, timeout = LOOP_TIMEOUT_MAX;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	while (timeout--) {
27362306a36Sopenharmony_ci		/* check FIFO */
27462306a36Sopenharmony_ci		len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF;
27562306a36Sopenharmony_ci		if (len >= 4)
27662306a36Sopenharmony_ci			return;
27762306a36Sopenharmony_ci		udelay(1);
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	timeout_error(flctl, __func__);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic enum flctl_ecc_res_t wait_recfifo_ready
28362306a36Sopenharmony_ci		(struct sh_flctl *flctl, int sector_number)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	uint32_t timeout = LOOP_TIMEOUT_MAX;
28662306a36Sopenharmony_ci	void __iomem *ecc_reg[4];
28762306a36Sopenharmony_ci	int i;
28862306a36Sopenharmony_ci	int state = FL_SUCCESS;
28962306a36Sopenharmony_ci	uint32_t data, size;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/*
29262306a36Sopenharmony_ci	 * First this loops checks in FLDTCNTR if we are ready to read out the
29362306a36Sopenharmony_ci	 * oob data. This is the case if either all went fine without errors or
29462306a36Sopenharmony_ci	 * if the bottom part of the loop corrected the errors or marked them as
29562306a36Sopenharmony_ci	 * uncorrectable and the controller is given time to push the data into
29662306a36Sopenharmony_ci	 * the FIFO.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	while (timeout--) {
29962306a36Sopenharmony_ci		/* check if all is ok and we can read out the OOB */
30062306a36Sopenharmony_ci		size = readl(FLDTCNTR(flctl)) >> 24;
30162306a36Sopenharmony_ci		if ((size & 0xFF) == 4)
30262306a36Sopenharmony_ci			return state;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		/* check if a correction code has been calculated */
30562306a36Sopenharmony_ci		if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) {
30662306a36Sopenharmony_ci			/*
30762306a36Sopenharmony_ci			 * either we wait for the fifo to be filled or a
30862306a36Sopenharmony_ci			 * correction pattern is being generated
30962306a36Sopenharmony_ci			 */
31062306a36Sopenharmony_ci			udelay(1);
31162306a36Sopenharmony_ci			continue;
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		/* check for an uncorrectable error */
31562306a36Sopenharmony_ci		if (readl(FL4ECCCR(flctl)) & _4ECCFA) {
31662306a36Sopenharmony_ci			/* check if we face a non-empty page */
31762306a36Sopenharmony_ci			for (i = 0; i < 512; i++) {
31862306a36Sopenharmony_ci				if (flctl->done_buff[i] != 0xff) {
31962306a36Sopenharmony_ci					state = FL_ERROR; /* can't correct */
32062306a36Sopenharmony_ci					break;
32162306a36Sopenharmony_ci				}
32262306a36Sopenharmony_ci			}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci			if (state == FL_SUCCESS)
32562306a36Sopenharmony_ci				dev_dbg(&flctl->pdev->dev,
32662306a36Sopenharmony_ci				"reading empty sector %d, ecc error ignored\n",
32762306a36Sopenharmony_ci				sector_number);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci			writel(0, FL4ECCCR(flctl));
33062306a36Sopenharmony_ci			continue;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		/* start error correction */
33462306a36Sopenharmony_ci		ecc_reg[0] = FL4ECCRESULT0(flctl);
33562306a36Sopenharmony_ci		ecc_reg[1] = FL4ECCRESULT1(flctl);
33662306a36Sopenharmony_ci		ecc_reg[2] = FL4ECCRESULT2(flctl);
33762306a36Sopenharmony_ci		ecc_reg[3] = FL4ECCRESULT3(flctl);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		for (i = 0; i < 3; i++) {
34062306a36Sopenharmony_ci			uint8_t org;
34162306a36Sopenharmony_ci			unsigned int index;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci			data = readl(ecc_reg[i]);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci			if (flctl->page_size)
34662306a36Sopenharmony_ci				index = (512 * sector_number) +
34762306a36Sopenharmony_ci					(data >> 16);
34862306a36Sopenharmony_ci			else
34962306a36Sopenharmony_ci				index = data >> 16;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci			org = flctl->done_buff[index];
35262306a36Sopenharmony_ci			flctl->done_buff[index] = org ^ (data & 0xFF);
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci		state = FL_REPAIRABLE;
35562306a36Sopenharmony_ci		writel(0, FL4ECCCR(flctl));
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	timeout_error(flctl, __func__);
35962306a36Sopenharmony_ci	return FL_TIMEOUT;	/* timeout */
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void wait_wecfifo_ready(struct sh_flctl *flctl)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	uint32_t timeout = LOOP_TIMEOUT_MAX;
36562306a36Sopenharmony_ci	uint32_t len;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	while (timeout--) {
36862306a36Sopenharmony_ci		/* check FLECFIFO */
36962306a36Sopenharmony_ci		len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF;
37062306a36Sopenharmony_ci		if (len >= 4)
37162306a36Sopenharmony_ci			return;
37262306a36Sopenharmony_ci		udelay(1);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	timeout_error(flctl, __func__);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
37862306a36Sopenharmony_ci					int len, enum dma_data_direction dir)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc = NULL;
38162306a36Sopenharmony_ci	struct dma_chan *chan;
38262306a36Sopenharmony_ci	enum dma_transfer_direction tr_dir;
38362306a36Sopenharmony_ci	dma_addr_t dma_addr;
38462306a36Sopenharmony_ci	dma_cookie_t cookie;
38562306a36Sopenharmony_ci	uint32_t reg;
38662306a36Sopenharmony_ci	int ret = 0;
38762306a36Sopenharmony_ci	unsigned long time_left;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (dir == DMA_FROM_DEVICE) {
39062306a36Sopenharmony_ci		chan = flctl->chan_fifo0_rx;
39162306a36Sopenharmony_ci		tr_dir = DMA_DEV_TO_MEM;
39262306a36Sopenharmony_ci	} else {
39362306a36Sopenharmony_ci		chan = flctl->chan_fifo0_tx;
39462306a36Sopenharmony_ci		tr_dir = DMA_MEM_TO_DEV;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (!dma_mapping_error(chan->device->dev, dma_addr))
40062306a36Sopenharmony_ci		desc = dmaengine_prep_slave_single(chan, dma_addr, len,
40162306a36Sopenharmony_ci			tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (desc) {
40462306a36Sopenharmony_ci		reg = readl(FLINTDMACR(flctl));
40562306a36Sopenharmony_ci		reg |= DREQ0EN;
40662306a36Sopenharmony_ci		writel(reg, FLINTDMACR(flctl));
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		desc->callback = flctl_dma_complete;
40962306a36Sopenharmony_ci		desc->callback_param = flctl;
41062306a36Sopenharmony_ci		cookie = dmaengine_submit(desc);
41162306a36Sopenharmony_ci		if (dma_submit_error(cookie)) {
41262306a36Sopenharmony_ci			ret = dma_submit_error(cookie);
41362306a36Sopenharmony_ci			dev_warn(&flctl->pdev->dev,
41462306a36Sopenharmony_ci				 "DMA submit failed, falling back to PIO\n");
41562306a36Sopenharmony_ci			goto out;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		dma_async_issue_pending(chan);
41962306a36Sopenharmony_ci	} else {
42062306a36Sopenharmony_ci		/* DMA failed, fall back to PIO */
42162306a36Sopenharmony_ci		flctl_release_dma(flctl);
42262306a36Sopenharmony_ci		dev_warn(&flctl->pdev->dev,
42362306a36Sopenharmony_ci			 "DMA failed, falling back to PIO\n");
42462306a36Sopenharmony_ci		ret = -EIO;
42562306a36Sopenharmony_ci		goto out;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	time_left =
42962306a36Sopenharmony_ci	wait_for_completion_timeout(&flctl->dma_complete,
43062306a36Sopenharmony_ci				msecs_to_jiffies(3000));
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (time_left == 0) {
43362306a36Sopenharmony_ci		dmaengine_terminate_all(chan);
43462306a36Sopenharmony_ci		dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
43562306a36Sopenharmony_ci		ret = -ETIMEDOUT;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciout:
43962306a36Sopenharmony_ci	reg = readl(FLINTDMACR(flctl));
44062306a36Sopenharmony_ci	reg &= ~DREQ0EN;
44162306a36Sopenharmony_ci	writel(reg, FLINTDMACR(flctl));
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	dma_unmap_single(chan->device->dev, dma_addr, len, dir);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* ret == 0 is success */
44662306a36Sopenharmony_ci	return ret;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void read_datareg(struct sh_flctl *flctl, int offset)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	unsigned long data;
45262306a36Sopenharmony_ci	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	wait_completion(flctl);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	data = readl(FLDATAR(flctl));
45762306a36Sopenharmony_ci	*buf = le32_to_cpu(data);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	int i, len_4align;
46362306a36Sopenharmony_ci	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	len_4align = (rlen + 3) / 4;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* initiate DMA transfer */
46862306a36Sopenharmony_ci	if (flctl->chan_fifo0_rx && rlen >= 32 &&
46962306a36Sopenharmony_ci		!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE))
47062306a36Sopenharmony_ci			goto convert;	/* DMA success */
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* do polling transfer */
47362306a36Sopenharmony_ci	for (i = 0; i < len_4align; i++) {
47462306a36Sopenharmony_ci		wait_rfifo_ready(flctl);
47562306a36Sopenharmony_ci		buf[i] = readl(FLDTFIFO(flctl));
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ciconvert:
47962306a36Sopenharmony_ci	for (i = 0; i < len_4align; i++)
48062306a36Sopenharmony_ci		buf[i] = be32_to_cpu(buf[i]);
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic enum flctl_ecc_res_t read_ecfiforeg
48462306a36Sopenharmony_ci		(struct sh_flctl *flctl, uint8_t *buff, int sector)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	int i;
48762306a36Sopenharmony_ci	enum flctl_ecc_res_t res;
48862306a36Sopenharmony_ci	unsigned long *ecc_buf = (unsigned long *)buff;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	res = wait_recfifo_ready(flctl , sector);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (res != FL_ERROR) {
49362306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
49462306a36Sopenharmony_ci			ecc_buf[i] = readl(FLECFIFO(flctl));
49562306a36Sopenharmony_ci			ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return res;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void write_fiforeg(struct sh_flctl *flctl, int rlen,
50362306a36Sopenharmony_ci						unsigned int offset)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	int i, len_4align;
50662306a36Sopenharmony_ci	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	len_4align = (rlen + 3) / 4;
50962306a36Sopenharmony_ci	for (i = 0; i < len_4align; i++) {
51062306a36Sopenharmony_ci		wait_wfifo_ready(flctl);
51162306a36Sopenharmony_ci		writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl));
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
51662306a36Sopenharmony_ci						unsigned int offset)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	int i, len_4align;
51962306a36Sopenharmony_ci	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	len_4align = (rlen + 3) / 4;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	for (i = 0; i < len_4align; i++)
52462306a36Sopenharmony_ci		buf[i] = cpu_to_be32(buf[i]);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* initiate DMA transfer */
52762306a36Sopenharmony_ci	if (flctl->chan_fifo0_tx && rlen >= 32 &&
52862306a36Sopenharmony_ci		!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE))
52962306a36Sopenharmony_ci			return;	/* DMA success */
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	/* do polling transfer */
53262306a36Sopenharmony_ci	for (i = 0; i < len_4align; i++) {
53362306a36Sopenharmony_ci		wait_wecfifo_ready(flctl);
53462306a36Sopenharmony_ci		writel(buf[i], FLECFIFO(flctl));
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
54162306a36Sopenharmony_ci	uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
54262306a36Sopenharmony_ci	uint32_t flcmdcr_val, addr_len_bytes = 0;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/* Set SNAND bit if page size is 2048byte */
54562306a36Sopenharmony_ci	if (flctl->page_size)
54662306a36Sopenharmony_ci		flcmncr_val |= SNAND_E;
54762306a36Sopenharmony_ci	else
54862306a36Sopenharmony_ci		flcmncr_val &= ~SNAND_E;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* default FLCMDCR val */
55162306a36Sopenharmony_ci	flcmdcr_val = DOCMD1_E | DOADR_E;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/* Set for FLCMDCR */
55462306a36Sopenharmony_ci	switch (cmd) {
55562306a36Sopenharmony_ci	case NAND_CMD_ERASE1:
55662306a36Sopenharmony_ci		addr_len_bytes = flctl->erase_ADRCNT;
55762306a36Sopenharmony_ci		flcmdcr_val |= DOCMD2_E;
55862306a36Sopenharmony_ci		break;
55962306a36Sopenharmony_ci	case NAND_CMD_READ0:
56062306a36Sopenharmony_ci	case NAND_CMD_READOOB:
56162306a36Sopenharmony_ci	case NAND_CMD_RNDOUT:
56262306a36Sopenharmony_ci		addr_len_bytes = flctl->rw_ADRCNT;
56362306a36Sopenharmony_ci		flcmdcr_val |= CDSRC_E;
56462306a36Sopenharmony_ci		if (flctl->chip.options & NAND_BUSWIDTH_16)
56562306a36Sopenharmony_ci			flcmncr_val |= SEL_16BIT;
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci	case NAND_CMD_SEQIN:
56862306a36Sopenharmony_ci		/* This case is that cmd is READ0 or READ1 or READ00 */
56962306a36Sopenharmony_ci		flcmdcr_val &= ~DOADR_E;	/* ONLY execute 1st cmd */
57062306a36Sopenharmony_ci		break;
57162306a36Sopenharmony_ci	case NAND_CMD_PAGEPROG:
57262306a36Sopenharmony_ci		addr_len_bytes = flctl->rw_ADRCNT;
57362306a36Sopenharmony_ci		flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW;
57462306a36Sopenharmony_ci		if (flctl->chip.options & NAND_BUSWIDTH_16)
57562306a36Sopenharmony_ci			flcmncr_val |= SEL_16BIT;
57662306a36Sopenharmony_ci		break;
57762306a36Sopenharmony_ci	case NAND_CMD_READID:
57862306a36Sopenharmony_ci		flcmncr_val &= ~SNAND_E;
57962306a36Sopenharmony_ci		flcmdcr_val |= CDSRC_E;
58062306a36Sopenharmony_ci		addr_len_bytes = ADRCNT_1;
58162306a36Sopenharmony_ci		break;
58262306a36Sopenharmony_ci	case NAND_CMD_STATUS:
58362306a36Sopenharmony_ci	case NAND_CMD_RESET:
58462306a36Sopenharmony_ci		flcmncr_val &= ~SNAND_E;
58562306a36Sopenharmony_ci		flcmdcr_val &= ~(DOADR_E | DOSR_E);
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	default:
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci	}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* Set address bytes parameter */
59262306a36Sopenharmony_ci	flcmdcr_val |= addr_len_bytes;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* Now actually write */
59562306a36Sopenharmony_ci	writel(flcmncr_val, FLCMNCR(flctl));
59662306a36Sopenharmony_ci	writel(flcmdcr_val, FLCMDCR(flctl));
59762306a36Sopenharmony_ci	writel(flcmcdr_val, FLCMCDR(flctl));
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic int flctl_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
60162306a36Sopenharmony_ci				 int oob_required, int page)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
60662306a36Sopenharmony_ci	if (oob_required)
60762306a36Sopenharmony_ci		chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
60862306a36Sopenharmony_ci	return 0;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic int flctl_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf,
61262306a36Sopenharmony_ci				  int oob_required, int page)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
61762306a36Sopenharmony_ci	chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
61862306a36Sopenharmony_ci	return nand_prog_page_end_op(chip);
61962306a36Sopenharmony_ci}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
62262306a36Sopenharmony_ci{
62362306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
62462306a36Sopenharmony_ci	int sector, page_sectors;
62562306a36Sopenharmony_ci	enum flctl_ecc_res_t ecc_result;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	page_sectors = flctl->page_size ? 4 : 1;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	set_cmd_regs(mtd, NAND_CMD_READ0,
63062306a36Sopenharmony_ci		(NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT,
63362306a36Sopenharmony_ci		 FLCMNCR(flctl));
63462306a36Sopenharmony_ci	writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
63562306a36Sopenharmony_ci	writel(page_addr << 2, FLADR(flctl));
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	empty_fifo(flctl);
63862306a36Sopenharmony_ci	start_translation(flctl);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	for (sector = 0; sector < page_sectors; sector++) {
64162306a36Sopenharmony_ci		read_fiforeg(flctl, 512, 512 * sector);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		ecc_result = read_ecfiforeg(flctl,
64462306a36Sopenharmony_ci			&flctl->done_buff[mtd->writesize + 16 * sector],
64562306a36Sopenharmony_ci			sector);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		switch (ecc_result) {
64862306a36Sopenharmony_ci		case FL_REPAIRABLE:
64962306a36Sopenharmony_ci			dev_info(&flctl->pdev->dev,
65062306a36Sopenharmony_ci				"applied ecc on page 0x%x", page_addr);
65162306a36Sopenharmony_ci			mtd->ecc_stats.corrected++;
65262306a36Sopenharmony_ci			break;
65362306a36Sopenharmony_ci		case FL_ERROR:
65462306a36Sopenharmony_ci			dev_warn(&flctl->pdev->dev,
65562306a36Sopenharmony_ci				"page 0x%x contains corrupted data\n",
65662306a36Sopenharmony_ci				page_addr);
65762306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
65862306a36Sopenharmony_ci			break;
65962306a36Sopenharmony_ci		default:
66062306a36Sopenharmony_ci			;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci	}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	wait_completion(flctl);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT),
66762306a36Sopenharmony_ci			FLCMNCR(flctl));
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic void execmd_read_oob(struct mtd_info *mtd, int page_addr)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
67362306a36Sopenharmony_ci	int page_sectors = flctl->page_size ? 4 : 1;
67462306a36Sopenharmony_ci	int i;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	set_cmd_regs(mtd, NAND_CMD_READ0,
67762306a36Sopenharmony_ci		(NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	empty_fifo(flctl);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	for (i = 0; i < page_sectors; i++) {
68262306a36Sopenharmony_ci		set_addr(mtd, (512 + 16) * i + 512 , page_addr);
68362306a36Sopenharmony_ci		writel(16, FLDTCNTR(flctl));
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		start_translation(flctl);
68662306a36Sopenharmony_ci		read_fiforeg(flctl, 16, 16 * i);
68762306a36Sopenharmony_ci		wait_completion(flctl);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic void execmd_write_page_sector(struct mtd_info *mtd)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
69462306a36Sopenharmony_ci	int page_addr = flctl->seqin_page_addr;
69562306a36Sopenharmony_ci	int sector, page_sectors;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	page_sectors = flctl->page_size ? 4 : 1;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
70062306a36Sopenharmony_ci			(NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	empty_fifo(flctl);
70362306a36Sopenharmony_ci	writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl));
70462306a36Sopenharmony_ci	writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
70562306a36Sopenharmony_ci	writel(page_addr << 2, FLADR(flctl));
70662306a36Sopenharmony_ci	start_translation(flctl);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	for (sector = 0; sector < page_sectors; sector++) {
70962306a36Sopenharmony_ci		write_fiforeg(flctl, 512, 512 * sector);
71062306a36Sopenharmony_ci		write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector);
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	wait_completion(flctl);
71462306a36Sopenharmony_ci	writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl));
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic void execmd_write_oob(struct mtd_info *mtd)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
72062306a36Sopenharmony_ci	int page_addr = flctl->seqin_page_addr;
72162306a36Sopenharmony_ci	int sector, page_sectors;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	page_sectors = flctl->page_size ? 4 : 1;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
72662306a36Sopenharmony_ci			(NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	for (sector = 0; sector < page_sectors; sector++) {
72962306a36Sopenharmony_ci		empty_fifo(flctl);
73062306a36Sopenharmony_ci		set_addr(mtd, sector * 528 + 512, page_addr);
73162306a36Sopenharmony_ci		writel(16, FLDTCNTR(flctl));	/* set read size */
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		start_translation(flctl);
73462306a36Sopenharmony_ci		write_fiforeg(flctl, 16, 16 * sector);
73562306a36Sopenharmony_ci		wait_completion(flctl);
73662306a36Sopenharmony_ci	}
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic void flctl_cmdfunc(struct nand_chip *chip, unsigned int command,
74062306a36Sopenharmony_ci			int column, int page_addr)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
74362306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
74462306a36Sopenharmony_ci	uint32_t read_cmd = 0;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	pm_runtime_get_sync(&flctl->pdev->dev);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	flctl->read_bytes = 0;
74962306a36Sopenharmony_ci	if (command != NAND_CMD_PAGEPROG)
75062306a36Sopenharmony_ci		flctl->index = 0;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	switch (command) {
75362306a36Sopenharmony_ci	case NAND_CMD_READ1:
75462306a36Sopenharmony_ci	case NAND_CMD_READ0:
75562306a36Sopenharmony_ci		if (flctl->hwecc) {
75662306a36Sopenharmony_ci			/* read page with hwecc */
75762306a36Sopenharmony_ci			execmd_read_page_sector(mtd, page_addr);
75862306a36Sopenharmony_ci			break;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci		if (flctl->page_size)
76162306a36Sopenharmony_ci			set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
76262306a36Sopenharmony_ci				| command);
76362306a36Sopenharmony_ci		else
76462306a36Sopenharmony_ci			set_cmd_regs(mtd, command, command);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		set_addr(mtd, 0, page_addr);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		flctl->read_bytes = mtd->writesize + mtd->oobsize;
76962306a36Sopenharmony_ci		if (flctl->chip.options & NAND_BUSWIDTH_16)
77062306a36Sopenharmony_ci			column >>= 1;
77162306a36Sopenharmony_ci		flctl->index += column;
77262306a36Sopenharmony_ci		goto read_normal_exit;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	case NAND_CMD_READOOB:
77562306a36Sopenharmony_ci		if (flctl->hwecc) {
77662306a36Sopenharmony_ci			/* read page with hwecc */
77762306a36Sopenharmony_ci			execmd_read_oob(mtd, page_addr);
77862306a36Sopenharmony_ci			break;
77962306a36Sopenharmony_ci		}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		if (flctl->page_size) {
78262306a36Sopenharmony_ci			set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
78362306a36Sopenharmony_ci				| NAND_CMD_READ0);
78462306a36Sopenharmony_ci			set_addr(mtd, mtd->writesize, page_addr);
78562306a36Sopenharmony_ci		} else {
78662306a36Sopenharmony_ci			set_cmd_regs(mtd, command, command);
78762306a36Sopenharmony_ci			set_addr(mtd, 0, page_addr);
78862306a36Sopenharmony_ci		}
78962306a36Sopenharmony_ci		flctl->read_bytes = mtd->oobsize;
79062306a36Sopenharmony_ci		goto read_normal_exit;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	case NAND_CMD_RNDOUT:
79362306a36Sopenharmony_ci		if (flctl->hwecc)
79462306a36Sopenharmony_ci			break;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		if (flctl->page_size)
79762306a36Sopenharmony_ci			set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
79862306a36Sopenharmony_ci				| command);
79962306a36Sopenharmony_ci		else
80062306a36Sopenharmony_ci			set_cmd_regs(mtd, command, command);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		set_addr(mtd, column, 0);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci		flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
80562306a36Sopenharmony_ci		goto read_normal_exit;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	case NAND_CMD_READID:
80862306a36Sopenharmony_ci		set_cmd_regs(mtd, command, command);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		/* READID is always performed using an 8-bit bus */
81162306a36Sopenharmony_ci		if (flctl->chip.options & NAND_BUSWIDTH_16)
81262306a36Sopenharmony_ci			column <<= 1;
81362306a36Sopenharmony_ci		set_addr(mtd, column, 0);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		flctl->read_bytes = 8;
81662306a36Sopenharmony_ci		writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
81762306a36Sopenharmony_ci		empty_fifo(flctl);
81862306a36Sopenharmony_ci		start_translation(flctl);
81962306a36Sopenharmony_ci		read_fiforeg(flctl, flctl->read_bytes, 0);
82062306a36Sopenharmony_ci		wait_completion(flctl);
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	case NAND_CMD_ERASE1:
82462306a36Sopenharmony_ci		flctl->erase1_page_addr = page_addr;
82562306a36Sopenharmony_ci		break;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	case NAND_CMD_ERASE2:
82862306a36Sopenharmony_ci		set_cmd_regs(mtd, NAND_CMD_ERASE1,
82962306a36Sopenharmony_ci			(command << 8) | NAND_CMD_ERASE1);
83062306a36Sopenharmony_ci		set_addr(mtd, -1, flctl->erase1_page_addr);
83162306a36Sopenharmony_ci		start_translation(flctl);
83262306a36Sopenharmony_ci		wait_completion(flctl);
83362306a36Sopenharmony_ci		break;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	case NAND_CMD_SEQIN:
83662306a36Sopenharmony_ci		if (!flctl->page_size) {
83762306a36Sopenharmony_ci			/* output read command */
83862306a36Sopenharmony_ci			if (column >= mtd->writesize) {
83962306a36Sopenharmony_ci				column -= mtd->writesize;
84062306a36Sopenharmony_ci				read_cmd = NAND_CMD_READOOB;
84162306a36Sopenharmony_ci			} else if (column < 256) {
84262306a36Sopenharmony_ci				read_cmd = NAND_CMD_READ0;
84362306a36Sopenharmony_ci			} else {
84462306a36Sopenharmony_ci				column -= 256;
84562306a36Sopenharmony_ci				read_cmd = NAND_CMD_READ1;
84662306a36Sopenharmony_ci			}
84762306a36Sopenharmony_ci		}
84862306a36Sopenharmony_ci		flctl->seqin_column = column;
84962306a36Sopenharmony_ci		flctl->seqin_page_addr = page_addr;
85062306a36Sopenharmony_ci		flctl->seqin_read_cmd = read_cmd;
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	case NAND_CMD_PAGEPROG:
85462306a36Sopenharmony_ci		empty_fifo(flctl);
85562306a36Sopenharmony_ci		if (!flctl->page_size) {
85662306a36Sopenharmony_ci			set_cmd_regs(mtd, NAND_CMD_SEQIN,
85762306a36Sopenharmony_ci					flctl->seqin_read_cmd);
85862306a36Sopenharmony_ci			set_addr(mtd, -1, -1);
85962306a36Sopenharmony_ci			writel(0, FLDTCNTR(flctl));	/* set 0 size */
86062306a36Sopenharmony_ci			start_translation(flctl);
86162306a36Sopenharmony_ci			wait_completion(flctl);
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci		if (flctl->hwecc) {
86462306a36Sopenharmony_ci			/* write page with hwecc */
86562306a36Sopenharmony_ci			if (flctl->seqin_column == mtd->writesize)
86662306a36Sopenharmony_ci				execmd_write_oob(mtd);
86762306a36Sopenharmony_ci			else if (!flctl->seqin_column)
86862306a36Sopenharmony_ci				execmd_write_page_sector(mtd);
86962306a36Sopenharmony_ci			else
87062306a36Sopenharmony_ci				pr_err("Invalid address !?\n");
87162306a36Sopenharmony_ci			break;
87262306a36Sopenharmony_ci		}
87362306a36Sopenharmony_ci		set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN);
87462306a36Sopenharmony_ci		set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr);
87562306a36Sopenharmony_ci		writel(flctl->index, FLDTCNTR(flctl));	/* set write size */
87662306a36Sopenharmony_ci		start_translation(flctl);
87762306a36Sopenharmony_ci		write_fiforeg(flctl, flctl->index, 0);
87862306a36Sopenharmony_ci		wait_completion(flctl);
87962306a36Sopenharmony_ci		break;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	case NAND_CMD_STATUS:
88262306a36Sopenharmony_ci		set_cmd_regs(mtd, command, command);
88362306a36Sopenharmony_ci		set_addr(mtd, -1, -1);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		flctl->read_bytes = 1;
88662306a36Sopenharmony_ci		writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
88762306a36Sopenharmony_ci		start_translation(flctl);
88862306a36Sopenharmony_ci		read_datareg(flctl, 0); /* read and end */
88962306a36Sopenharmony_ci		break;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	case NAND_CMD_RESET:
89262306a36Sopenharmony_ci		set_cmd_regs(mtd, command, command);
89362306a36Sopenharmony_ci		set_addr(mtd, -1, -1);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		writel(0, FLDTCNTR(flctl));	/* set 0 size */
89662306a36Sopenharmony_ci		start_translation(flctl);
89762306a36Sopenharmony_ci		wait_completion(flctl);
89862306a36Sopenharmony_ci		break;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	default:
90162306a36Sopenharmony_ci		break;
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci	goto runtime_exit;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ciread_normal_exit:
90662306a36Sopenharmony_ci	writel(flctl->read_bytes, FLDTCNTR(flctl));	/* set read size */
90762306a36Sopenharmony_ci	empty_fifo(flctl);
90862306a36Sopenharmony_ci	start_translation(flctl);
90962306a36Sopenharmony_ci	read_fiforeg(flctl, flctl->read_bytes, 0);
91062306a36Sopenharmony_ci	wait_completion(flctl);
91162306a36Sopenharmony_ciruntime_exit:
91262306a36Sopenharmony_ci	pm_runtime_put_sync(&flctl->pdev->dev);
91362306a36Sopenharmony_ci	return;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic void flctl_select_chip(struct nand_chip *chip, int chipnr)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
91962306a36Sopenharmony_ci	int ret;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	switch (chipnr) {
92262306a36Sopenharmony_ci	case -1:
92362306a36Sopenharmony_ci		flctl->flcmncr_base &= ~CE0_ENABLE;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		pm_runtime_get_sync(&flctl->pdev->dev);
92662306a36Sopenharmony_ci		writel(flctl->flcmncr_base, FLCMNCR(flctl));
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		if (flctl->qos_request) {
92962306a36Sopenharmony_ci			dev_pm_qos_remove_request(&flctl->pm_qos);
93062306a36Sopenharmony_ci			flctl->qos_request = 0;
93162306a36Sopenharmony_ci		}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci		pm_runtime_put_sync(&flctl->pdev->dev);
93462306a36Sopenharmony_ci		break;
93562306a36Sopenharmony_ci	case 0:
93662306a36Sopenharmony_ci		flctl->flcmncr_base |= CE0_ENABLE;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		if (!flctl->qos_request) {
93962306a36Sopenharmony_ci			ret = dev_pm_qos_add_request(&flctl->pdev->dev,
94062306a36Sopenharmony_ci							&flctl->pm_qos,
94162306a36Sopenharmony_ci							DEV_PM_QOS_RESUME_LATENCY,
94262306a36Sopenharmony_ci							100);
94362306a36Sopenharmony_ci			if (ret < 0)
94462306a36Sopenharmony_ci				dev_err(&flctl->pdev->dev,
94562306a36Sopenharmony_ci					"PM QoS request failed: %d\n", ret);
94662306a36Sopenharmony_ci			flctl->qos_request = 1;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci		if (flctl->holden) {
95062306a36Sopenharmony_ci			pm_runtime_get_sync(&flctl->pdev->dev);
95162306a36Sopenharmony_ci			writel(HOLDEN, FLHOLDCR(flctl));
95262306a36Sopenharmony_ci			pm_runtime_put_sync(&flctl->pdev->dev);
95362306a36Sopenharmony_ci		}
95462306a36Sopenharmony_ci		break;
95562306a36Sopenharmony_ci	default:
95662306a36Sopenharmony_ci		BUG();
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic void flctl_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	memcpy(&flctl->done_buff[flctl->index], buf, len);
96562306a36Sopenharmony_ci	flctl->index += len;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic uint8_t flctl_read_byte(struct nand_chip *chip)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
97162306a36Sopenharmony_ci	uint8_t data;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	data = flctl->done_buff[flctl->index];
97462306a36Sopenharmony_ci	flctl->index++;
97562306a36Sopenharmony_ci	return data;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void flctl_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	memcpy(buf, &flctl->done_buff[flctl->index], len);
98362306a36Sopenharmony_ci	flctl->index += len;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic int flctl_chip_attach_chip(struct nand_chip *chip)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	u64 targetsize = nanddev_target_size(&chip->base);
98962306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
99062306a36Sopenharmony_ci	struct sh_flctl *flctl = mtd_to_flctl(mtd);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	/*
99362306a36Sopenharmony_ci	 * NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
99462306a36Sopenharmony_ci	 * Add the SEL_16BIT flag in flctl->flcmncr_base.
99562306a36Sopenharmony_ci	 */
99662306a36Sopenharmony_ci	if (chip->options & NAND_BUSWIDTH_16)
99762306a36Sopenharmony_ci		flctl->flcmncr_base |= SEL_16BIT;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (mtd->writesize == 512) {
100062306a36Sopenharmony_ci		flctl->page_size = 0;
100162306a36Sopenharmony_ci		if (targetsize > (32 << 20)) {
100262306a36Sopenharmony_ci			/* big than 32MB */
100362306a36Sopenharmony_ci			flctl->rw_ADRCNT = ADRCNT_4;
100462306a36Sopenharmony_ci			flctl->erase_ADRCNT = ADRCNT_3;
100562306a36Sopenharmony_ci		} else if (targetsize > (2 << 16)) {
100662306a36Sopenharmony_ci			/* big than 128KB */
100762306a36Sopenharmony_ci			flctl->rw_ADRCNT = ADRCNT_3;
100862306a36Sopenharmony_ci			flctl->erase_ADRCNT = ADRCNT_2;
100962306a36Sopenharmony_ci		} else {
101062306a36Sopenharmony_ci			flctl->rw_ADRCNT = ADRCNT_2;
101162306a36Sopenharmony_ci			flctl->erase_ADRCNT = ADRCNT_1;
101262306a36Sopenharmony_ci		}
101362306a36Sopenharmony_ci	} else {
101462306a36Sopenharmony_ci		flctl->page_size = 1;
101562306a36Sopenharmony_ci		if (targetsize > (128 << 20)) {
101662306a36Sopenharmony_ci			/* big than 128MB */
101762306a36Sopenharmony_ci			flctl->rw_ADRCNT = ADRCNT2_E;
101862306a36Sopenharmony_ci			flctl->erase_ADRCNT = ADRCNT_3;
101962306a36Sopenharmony_ci		} else if (targetsize > (8 << 16)) {
102062306a36Sopenharmony_ci			/* big than 512KB */
102162306a36Sopenharmony_ci			flctl->rw_ADRCNT = ADRCNT_4;
102262306a36Sopenharmony_ci			flctl->erase_ADRCNT = ADRCNT_2;
102362306a36Sopenharmony_ci		} else {
102462306a36Sopenharmony_ci			flctl->rw_ADRCNT = ADRCNT_3;
102562306a36Sopenharmony_ci			flctl->erase_ADRCNT = ADRCNT_1;
102662306a36Sopenharmony_ci		}
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (flctl->hwecc) {
103062306a36Sopenharmony_ci		if (mtd->writesize == 512) {
103162306a36Sopenharmony_ci			mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops);
103262306a36Sopenharmony_ci			chip->badblock_pattern = &flctl_4secc_smallpage;
103362306a36Sopenharmony_ci		} else {
103462306a36Sopenharmony_ci			mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops);
103562306a36Sopenharmony_ci			chip->badblock_pattern = &flctl_4secc_largepage;
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		chip->ecc.size = 512;
103962306a36Sopenharmony_ci		chip->ecc.bytes = 10;
104062306a36Sopenharmony_ci		chip->ecc.strength = 4;
104162306a36Sopenharmony_ci		chip->ecc.read_page = flctl_read_page_hwecc;
104262306a36Sopenharmony_ci		chip->ecc.write_page = flctl_write_page_hwecc;
104362306a36Sopenharmony_ci		chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		/* 4 symbols ECC enabled */
104662306a36Sopenharmony_ci		flctl->flcmncr_base |= _4ECCEN;
104762306a36Sopenharmony_ci	} else {
104862306a36Sopenharmony_ci		chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
104962306a36Sopenharmony_ci		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	return 0;
105362306a36Sopenharmony_ci}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_cistatic const struct nand_controller_ops flctl_nand_controller_ops = {
105662306a36Sopenharmony_ci	.attach_chip = flctl_chip_attach_chip,
105762306a36Sopenharmony_ci};
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic irqreturn_t flctl_handle_flste(int irq, void *dev_id)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct sh_flctl *flctl = dev_id;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl)));
106462306a36Sopenharmony_ci	writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return IRQ_HANDLED;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistruct flctl_soc_config {
107062306a36Sopenharmony_ci	unsigned long flcmncr_val;
107162306a36Sopenharmony_ci	unsigned has_hwecc:1;
107262306a36Sopenharmony_ci	unsigned use_holden:1;
107362306a36Sopenharmony_ci};
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic struct flctl_soc_config flctl_sh7372_config = {
107662306a36Sopenharmony_ci	.flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL,
107762306a36Sopenharmony_ci	.has_hwecc = 1,
107862306a36Sopenharmony_ci	.use_holden = 1,
107962306a36Sopenharmony_ci};
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic const struct of_device_id of_flctl_match[] = {
108262306a36Sopenharmony_ci	{ .compatible = "renesas,shmobile-flctl-sh7372",
108362306a36Sopenharmony_ci				.data = &flctl_sh7372_config },
108462306a36Sopenharmony_ci	{},
108562306a36Sopenharmony_ci};
108662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_flctl_match);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	const struct flctl_soc_config *config;
109162306a36Sopenharmony_ci	struct sh_flctl_platform_data *pdata;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	config = of_device_get_match_data(dev);
109462306a36Sopenharmony_ci	if (!config) {
109562306a36Sopenharmony_ci		dev_err(dev, "%s: no OF configuration attached\n", __func__);
109662306a36Sopenharmony_ci		return NULL;
109762306a36Sopenharmony_ci	}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data),
110062306a36Sopenharmony_ci								GFP_KERNEL);
110162306a36Sopenharmony_ci	if (!pdata)
110262306a36Sopenharmony_ci		return NULL;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	/* set SoC specific options */
110562306a36Sopenharmony_ci	pdata->flcmncr_val = config->flcmncr_val;
110662306a36Sopenharmony_ci	pdata->has_hwecc = config->has_hwecc;
110762306a36Sopenharmony_ci	pdata->use_holden = config->use_holden;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return pdata;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic int flctl_probe(struct platform_device *pdev)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	struct resource *res;
111562306a36Sopenharmony_ci	struct sh_flctl *flctl;
111662306a36Sopenharmony_ci	struct mtd_info *flctl_mtd;
111762306a36Sopenharmony_ci	struct nand_chip *nand;
111862306a36Sopenharmony_ci	struct sh_flctl_platform_data *pdata;
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci	int irq;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL);
112362306a36Sopenharmony_ci	if (!flctl)
112462306a36Sopenharmony_ci		return -ENOMEM;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	flctl->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
112762306a36Sopenharmony_ci	if (IS_ERR(flctl->reg))
112862306a36Sopenharmony_ci		return PTR_ERR(flctl->reg);
112962306a36Sopenharmony_ci	flctl->fifo = res->start + 0x24; /* FLDTFIFO */
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
113262306a36Sopenharmony_ci	if (irq < 0)
113362306a36Sopenharmony_ci		return irq;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED,
113662306a36Sopenharmony_ci			       "flste", flctl);
113762306a36Sopenharmony_ci	if (ret) {
113862306a36Sopenharmony_ci		dev_err(&pdev->dev, "request interrupt failed.\n");
113962306a36Sopenharmony_ci		return ret;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if (pdev->dev.of_node)
114362306a36Sopenharmony_ci		pdata = flctl_parse_dt(&pdev->dev);
114462306a36Sopenharmony_ci	else
114562306a36Sopenharmony_ci		pdata = dev_get_platdata(&pdev->dev);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (!pdata) {
114862306a36Sopenharmony_ci		dev_err(&pdev->dev, "no setup data defined\n");
114962306a36Sopenharmony_ci		return -EINVAL;
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	platform_set_drvdata(pdev, flctl);
115362306a36Sopenharmony_ci	nand = &flctl->chip;
115462306a36Sopenharmony_ci	flctl_mtd = nand_to_mtd(nand);
115562306a36Sopenharmony_ci	nand_set_flash_node(nand, pdev->dev.of_node);
115662306a36Sopenharmony_ci	flctl_mtd->dev.parent = &pdev->dev;
115762306a36Sopenharmony_ci	flctl->pdev = pdev;
115862306a36Sopenharmony_ci	flctl->hwecc = pdata->has_hwecc;
115962306a36Sopenharmony_ci	flctl->holden = pdata->use_holden;
116062306a36Sopenharmony_ci	flctl->flcmncr_base = pdata->flcmncr_val;
116162306a36Sopenharmony_ci	flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* Set address of hardware control function */
116462306a36Sopenharmony_ci	/* 20 us command delay time */
116562306a36Sopenharmony_ci	nand->legacy.chip_delay = 20;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	nand->legacy.read_byte = flctl_read_byte;
116862306a36Sopenharmony_ci	nand->legacy.write_buf = flctl_write_buf;
116962306a36Sopenharmony_ci	nand->legacy.read_buf = flctl_read_buf;
117062306a36Sopenharmony_ci	nand->legacy.select_chip = flctl_select_chip;
117162306a36Sopenharmony_ci	nand->legacy.cmdfunc = flctl_cmdfunc;
117262306a36Sopenharmony_ci	nand->legacy.set_features = nand_get_set_features_notsupp;
117362306a36Sopenharmony_ci	nand->legacy.get_features = nand_get_set_features_notsupp;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	if (pdata->flcmncr_val & SEL_16BIT)
117662306a36Sopenharmony_ci		nand->options |= NAND_BUSWIDTH_16;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	nand->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
118162306a36Sopenharmony_ci	pm_runtime_resume(&pdev->dev);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	flctl_setup_dma(flctl);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops;
118662306a36Sopenharmony_ci	ret = nand_scan(nand, 1);
118762306a36Sopenharmony_ci	if (ret)
118862306a36Sopenharmony_ci		goto err_chip;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
119162306a36Sopenharmony_ci	if (ret)
119262306a36Sopenharmony_ci		goto cleanup_nand;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	return 0;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cicleanup_nand:
119762306a36Sopenharmony_ci	nand_cleanup(nand);
119862306a36Sopenharmony_cierr_chip:
119962306a36Sopenharmony_ci	flctl_release_dma(flctl);
120062306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
120162306a36Sopenharmony_ci	return ret;
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic void flctl_remove(struct platform_device *pdev)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct sh_flctl *flctl = platform_get_drvdata(pdev);
120762306a36Sopenharmony_ci	struct nand_chip *chip = &flctl->chip;
120862306a36Sopenharmony_ci	int ret;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	flctl_release_dma(flctl);
121162306a36Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
121262306a36Sopenharmony_ci	WARN_ON(ret);
121362306a36Sopenharmony_ci	nand_cleanup(chip);
121462306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic struct platform_driver flctl_driver = {
121862306a36Sopenharmony_ci	.remove_new	= flctl_remove,
121962306a36Sopenharmony_ci	.driver = {
122062306a36Sopenharmony_ci		.name	= "sh_flctl",
122162306a36Sopenharmony_ci		.of_match_table = of_flctl_match,
122262306a36Sopenharmony_ci	},
122362306a36Sopenharmony_ci};
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cimodule_platform_driver_probe(flctl_driver, flctl_probe);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
122862306a36Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda");
122962306a36Sopenharmony_ciMODULE_DESCRIPTION("SuperH FLCTL driver");
123062306a36Sopenharmony_ciMODULE_ALIAS("platform:sh_flctl");
1231