xref: /openbmc/linux/drivers/mtd/nand/raw/sh_flctl.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1cfd74017SKuninori Morimoto // SPDX-License-Identifier: GPL-2.0
293db446aSBoris Brezillon /*
393db446aSBoris Brezillon  * SuperH FLCTL nand controller
493db446aSBoris Brezillon  *
593db446aSBoris Brezillon  * Copyright (c) 2008 Renesas Solutions Corp.
693db446aSBoris Brezillon  * Copyright (c) 2008 Atom Create Engineering Co., Ltd.
793db446aSBoris Brezillon  *
893db446aSBoris Brezillon  * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor
993db446aSBoris Brezillon  */
1093db446aSBoris Brezillon 
1193db446aSBoris Brezillon #include <linux/module.h>
1293db446aSBoris Brezillon #include <linux/kernel.h>
1393db446aSBoris Brezillon #include <linux/completion.h>
1493db446aSBoris Brezillon #include <linux/delay.h>
1593db446aSBoris Brezillon #include <linux/dmaengine.h>
1693db446aSBoris Brezillon #include <linux/dma-mapping.h>
1793db446aSBoris Brezillon #include <linux/interrupt.h>
1893db446aSBoris Brezillon #include <linux/io.h>
1993db446aSBoris Brezillon #include <linux/of.h>
2093db446aSBoris Brezillon #include <linux/platform_device.h>
2193db446aSBoris Brezillon #include <linux/pm_runtime.h>
2293db446aSBoris Brezillon #include <linux/sh_dma.h>
2393db446aSBoris Brezillon #include <linux/slab.h>
2493db446aSBoris Brezillon #include <linux/string.h>
2593db446aSBoris Brezillon 
2693db446aSBoris Brezillon #include <linux/mtd/mtd.h>
2793db446aSBoris Brezillon #include <linux/mtd/rawnand.h>
2893db446aSBoris Brezillon #include <linux/mtd/partitions.h>
2993db446aSBoris Brezillon #include <linux/mtd/sh_flctl.h>
3093db446aSBoris Brezillon 
flctl_4secc_ooblayout_sp_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)3193db446aSBoris Brezillon static int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section,
3293db446aSBoris Brezillon 					struct mtd_oob_region *oobregion)
3393db446aSBoris Brezillon {
3493db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
3593db446aSBoris Brezillon 
3693db446aSBoris Brezillon 	if (section)
3793db446aSBoris Brezillon 		return -ERANGE;
3893db446aSBoris Brezillon 
3993db446aSBoris Brezillon 	oobregion->offset = 0;
4093db446aSBoris Brezillon 	oobregion->length = chip->ecc.bytes;
4193db446aSBoris Brezillon 
4293db446aSBoris Brezillon 	return 0;
4393db446aSBoris Brezillon }
4493db446aSBoris Brezillon 
flctl_4secc_ooblayout_sp_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)4593db446aSBoris Brezillon static int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section,
4693db446aSBoris Brezillon 					 struct mtd_oob_region *oobregion)
4793db446aSBoris Brezillon {
4893db446aSBoris Brezillon 	if (section)
4993db446aSBoris Brezillon 		return -ERANGE;
5093db446aSBoris Brezillon 
5193db446aSBoris Brezillon 	oobregion->offset = 12;
5293db446aSBoris Brezillon 	oobregion->length = 4;
5393db446aSBoris Brezillon 
5493db446aSBoris Brezillon 	return 0;
5593db446aSBoris Brezillon }
5693db446aSBoris Brezillon 
5793db446aSBoris Brezillon static const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = {
5893db446aSBoris Brezillon 	.ecc = flctl_4secc_ooblayout_sp_ecc,
5993db446aSBoris Brezillon 	.free = flctl_4secc_ooblayout_sp_free,
6093db446aSBoris Brezillon };
6193db446aSBoris Brezillon 
flctl_4secc_ooblayout_lp_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)6293db446aSBoris Brezillon static int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section,
6393db446aSBoris Brezillon 					struct mtd_oob_region *oobregion)
6493db446aSBoris Brezillon {
6593db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
6693db446aSBoris Brezillon 
6793db446aSBoris Brezillon 	if (section >= chip->ecc.steps)
6893db446aSBoris Brezillon 		return -ERANGE;
6993db446aSBoris Brezillon 
7093db446aSBoris Brezillon 	oobregion->offset = (section * 16) + 6;
7193db446aSBoris Brezillon 	oobregion->length = chip->ecc.bytes;
7293db446aSBoris Brezillon 
7393db446aSBoris Brezillon 	return 0;
7493db446aSBoris Brezillon }
7593db446aSBoris Brezillon 
flctl_4secc_ooblayout_lp_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)7693db446aSBoris Brezillon static int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section,
7793db446aSBoris Brezillon 					 struct mtd_oob_region *oobregion)
7893db446aSBoris Brezillon {
7993db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
8093db446aSBoris Brezillon 
8193db446aSBoris Brezillon 	if (section >= chip->ecc.steps)
8293db446aSBoris Brezillon 		return -ERANGE;
8393db446aSBoris Brezillon 
8493db446aSBoris Brezillon 	oobregion->offset = section * 16;
8593db446aSBoris Brezillon 	oobregion->length = 6;
8693db446aSBoris Brezillon 
8793db446aSBoris Brezillon 	if (!section) {
8893db446aSBoris Brezillon 		oobregion->offset += 2;
8993db446aSBoris Brezillon 		oobregion->length -= 2;
9093db446aSBoris Brezillon 	}
9193db446aSBoris Brezillon 
9293db446aSBoris Brezillon 	return 0;
9393db446aSBoris Brezillon }
9493db446aSBoris Brezillon 
9593db446aSBoris Brezillon static const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = {
9693db446aSBoris Brezillon 	.ecc = flctl_4secc_ooblayout_lp_ecc,
9793db446aSBoris Brezillon 	.free = flctl_4secc_ooblayout_lp_free,
9893db446aSBoris Brezillon };
9993db446aSBoris Brezillon 
10093db446aSBoris Brezillon static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
10193db446aSBoris Brezillon 
10293db446aSBoris Brezillon static struct nand_bbt_descr flctl_4secc_smallpage = {
10393db446aSBoris Brezillon 	.offs = 11,
10493db446aSBoris Brezillon 	.len = 1,
10593db446aSBoris Brezillon 	.pattern = scan_ff_pattern,
10693db446aSBoris Brezillon };
10793db446aSBoris Brezillon 
10893db446aSBoris Brezillon static struct nand_bbt_descr flctl_4secc_largepage = {
10993db446aSBoris Brezillon 	.offs = 0,
11093db446aSBoris Brezillon 	.len = 2,
11193db446aSBoris Brezillon 	.pattern = scan_ff_pattern,
11293db446aSBoris Brezillon };
11393db446aSBoris Brezillon 
empty_fifo(struct sh_flctl * flctl)11493db446aSBoris Brezillon static void empty_fifo(struct sh_flctl *flctl)
11593db446aSBoris Brezillon {
11693db446aSBoris Brezillon 	writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl));
11793db446aSBoris Brezillon 	writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
11893db446aSBoris Brezillon }
11993db446aSBoris Brezillon 
start_translation(struct sh_flctl * flctl)12093db446aSBoris Brezillon static void start_translation(struct sh_flctl *flctl)
12193db446aSBoris Brezillon {
12293db446aSBoris Brezillon 	writeb(TRSTRT, FLTRCR(flctl));
12393db446aSBoris Brezillon }
12493db446aSBoris Brezillon 
timeout_error(struct sh_flctl * flctl,const char * str)12593db446aSBoris Brezillon static void timeout_error(struct sh_flctl *flctl, const char *str)
12693db446aSBoris Brezillon {
12793db446aSBoris Brezillon 	dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str);
12893db446aSBoris Brezillon }
12993db446aSBoris Brezillon 
wait_completion(struct sh_flctl * flctl)13093db446aSBoris Brezillon static void wait_completion(struct sh_flctl *flctl)
13193db446aSBoris Brezillon {
13293db446aSBoris Brezillon 	uint32_t timeout = LOOP_TIMEOUT_MAX;
13393db446aSBoris Brezillon 
13493db446aSBoris Brezillon 	while (timeout--) {
13593db446aSBoris Brezillon 		if (readb(FLTRCR(flctl)) & TREND) {
13693db446aSBoris Brezillon 			writeb(0x0, FLTRCR(flctl));
13793db446aSBoris Brezillon 			return;
13893db446aSBoris Brezillon 		}
13993db446aSBoris Brezillon 		udelay(1);
14093db446aSBoris Brezillon 	}
14193db446aSBoris Brezillon 
14293db446aSBoris Brezillon 	timeout_error(flctl, __func__);
14393db446aSBoris Brezillon 	writeb(0x0, FLTRCR(flctl));
14493db446aSBoris Brezillon }
14593db446aSBoris Brezillon 
flctl_dma_complete(void * param)14693db446aSBoris Brezillon static void flctl_dma_complete(void *param)
14793db446aSBoris Brezillon {
14893db446aSBoris Brezillon 	struct sh_flctl *flctl = param;
14993db446aSBoris Brezillon 
15093db446aSBoris Brezillon 	complete(&flctl->dma_complete);
15193db446aSBoris Brezillon }
15293db446aSBoris Brezillon 
flctl_release_dma(struct sh_flctl * flctl)15393db446aSBoris Brezillon static void flctl_release_dma(struct sh_flctl *flctl)
15493db446aSBoris Brezillon {
15593db446aSBoris Brezillon 	if (flctl->chan_fifo0_rx) {
15693db446aSBoris Brezillon 		dma_release_channel(flctl->chan_fifo0_rx);
15793db446aSBoris Brezillon 		flctl->chan_fifo0_rx = NULL;
15893db446aSBoris Brezillon 	}
15993db446aSBoris Brezillon 	if (flctl->chan_fifo0_tx) {
16093db446aSBoris Brezillon 		dma_release_channel(flctl->chan_fifo0_tx);
16193db446aSBoris Brezillon 		flctl->chan_fifo0_tx = NULL;
16293db446aSBoris Brezillon 	}
16393db446aSBoris Brezillon }
16493db446aSBoris Brezillon 
flctl_setup_dma(struct sh_flctl * flctl)16593db446aSBoris Brezillon static void flctl_setup_dma(struct sh_flctl *flctl)
16693db446aSBoris Brezillon {
16793db446aSBoris Brezillon 	dma_cap_mask_t mask;
16893db446aSBoris Brezillon 	struct dma_slave_config cfg;
16993db446aSBoris Brezillon 	struct platform_device *pdev = flctl->pdev;
17093db446aSBoris Brezillon 	struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev);
17193db446aSBoris Brezillon 	int ret;
17293db446aSBoris Brezillon 
17393db446aSBoris Brezillon 	if (!pdata)
17493db446aSBoris Brezillon 		return;
17593db446aSBoris Brezillon 
17693db446aSBoris Brezillon 	if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
17793db446aSBoris Brezillon 		return;
17893db446aSBoris Brezillon 
17993db446aSBoris Brezillon 	/* We can only either use DMA for both Tx and Rx or not use it at all */
18093db446aSBoris Brezillon 	dma_cap_zero(mask);
18193db446aSBoris Brezillon 	dma_cap_set(DMA_SLAVE, mask);
18293db446aSBoris Brezillon 
18393db446aSBoris Brezillon 	flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
18493db446aSBoris Brezillon 				(void *)(uintptr_t)pdata->slave_id_fifo0_tx);
18593db446aSBoris Brezillon 	dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
18693db446aSBoris Brezillon 		flctl->chan_fifo0_tx);
18793db446aSBoris Brezillon 
18893db446aSBoris Brezillon 	if (!flctl->chan_fifo0_tx)
18993db446aSBoris Brezillon 		return;
19093db446aSBoris Brezillon 
19193db446aSBoris Brezillon 	memset(&cfg, 0, sizeof(cfg));
19293db446aSBoris Brezillon 	cfg.direction = DMA_MEM_TO_DEV;
19393db446aSBoris Brezillon 	cfg.dst_addr = flctl->fifo;
19493db446aSBoris Brezillon 	cfg.src_addr = 0;
19593db446aSBoris Brezillon 	ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
19693db446aSBoris Brezillon 	if (ret < 0)
19793db446aSBoris Brezillon 		goto err;
19893db446aSBoris Brezillon 
19993db446aSBoris Brezillon 	flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
20093db446aSBoris Brezillon 				(void *)(uintptr_t)pdata->slave_id_fifo0_rx);
20193db446aSBoris Brezillon 	dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
20293db446aSBoris Brezillon 		flctl->chan_fifo0_rx);
20393db446aSBoris Brezillon 
20493db446aSBoris Brezillon 	if (!flctl->chan_fifo0_rx)
20593db446aSBoris Brezillon 		goto err;
20693db446aSBoris Brezillon 
20793db446aSBoris Brezillon 	cfg.direction = DMA_DEV_TO_MEM;
20893db446aSBoris Brezillon 	cfg.dst_addr = 0;
20993db446aSBoris Brezillon 	cfg.src_addr = flctl->fifo;
21093db446aSBoris Brezillon 	ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
21193db446aSBoris Brezillon 	if (ret < 0)
21293db446aSBoris Brezillon 		goto err;
21393db446aSBoris Brezillon 
21493db446aSBoris Brezillon 	init_completion(&flctl->dma_complete);
21593db446aSBoris Brezillon 
21693db446aSBoris Brezillon 	return;
21793db446aSBoris Brezillon 
21893db446aSBoris Brezillon err:
21993db446aSBoris Brezillon 	flctl_release_dma(flctl);
22093db446aSBoris Brezillon }
22193db446aSBoris Brezillon 
set_addr(struct mtd_info * mtd,int column,int page_addr)22293db446aSBoris Brezillon static void set_addr(struct mtd_info *mtd, int column, int page_addr)
22393db446aSBoris Brezillon {
22493db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
22593db446aSBoris Brezillon 	uint32_t addr = 0;
22693db446aSBoris Brezillon 
22793db446aSBoris Brezillon 	if (column == -1) {
22893db446aSBoris Brezillon 		addr = page_addr;	/* ERASE1 */
22993db446aSBoris Brezillon 	} else if (page_addr != -1) {
23093db446aSBoris Brezillon 		/* SEQIN, READ0, etc.. */
23193db446aSBoris Brezillon 		if (flctl->chip.options & NAND_BUSWIDTH_16)
23293db446aSBoris Brezillon 			column >>= 1;
23393db446aSBoris Brezillon 		if (flctl->page_size) {
23493db446aSBoris Brezillon 			addr = column & 0x0FFF;
23593db446aSBoris Brezillon 			addr |= (page_addr & 0xff) << 16;
23693db446aSBoris Brezillon 			addr |= ((page_addr >> 8) & 0xff) << 24;
23793db446aSBoris Brezillon 			/* big than 128MB */
23893db446aSBoris Brezillon 			if (flctl->rw_ADRCNT == ADRCNT2_E) {
23993db446aSBoris Brezillon 				uint32_t 	addr2;
24093db446aSBoris Brezillon 				addr2 = (page_addr >> 16) & 0xff;
24193db446aSBoris Brezillon 				writel(addr2, FLADR2(flctl));
24293db446aSBoris Brezillon 			}
24393db446aSBoris Brezillon 		} else {
24493db446aSBoris Brezillon 			addr = column;
24593db446aSBoris Brezillon 			addr |= (page_addr & 0xff) << 8;
24693db446aSBoris Brezillon 			addr |= ((page_addr >> 8) & 0xff) << 16;
24793db446aSBoris Brezillon 			addr |= ((page_addr >> 16) & 0xff) << 24;
24893db446aSBoris Brezillon 		}
24993db446aSBoris Brezillon 	}
25093db446aSBoris Brezillon 	writel(addr, FLADR(flctl));
25193db446aSBoris Brezillon }
25293db446aSBoris Brezillon 
wait_rfifo_ready(struct sh_flctl * flctl)25393db446aSBoris Brezillon static void wait_rfifo_ready(struct sh_flctl *flctl)
25493db446aSBoris Brezillon {
25593db446aSBoris Brezillon 	uint32_t timeout = LOOP_TIMEOUT_MAX;
25693db446aSBoris Brezillon 
25793db446aSBoris Brezillon 	while (timeout--) {
25893db446aSBoris Brezillon 		uint32_t val;
25993db446aSBoris Brezillon 		/* check FIFO */
26093db446aSBoris Brezillon 		val = readl(FLDTCNTR(flctl)) >> 16;
26193db446aSBoris Brezillon 		if (val & 0xFF)
26293db446aSBoris Brezillon 			return;
26393db446aSBoris Brezillon 		udelay(1);
26493db446aSBoris Brezillon 	}
26593db446aSBoris Brezillon 	timeout_error(flctl, __func__);
26693db446aSBoris Brezillon }
26793db446aSBoris Brezillon 
wait_wfifo_ready(struct sh_flctl * flctl)26893db446aSBoris Brezillon static void wait_wfifo_ready(struct sh_flctl *flctl)
26993db446aSBoris Brezillon {
27093db446aSBoris Brezillon 	uint32_t len, timeout = LOOP_TIMEOUT_MAX;
27193db446aSBoris Brezillon 
27293db446aSBoris Brezillon 	while (timeout--) {
27393db446aSBoris Brezillon 		/* check FIFO */
27493db446aSBoris Brezillon 		len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF;
27593db446aSBoris Brezillon 		if (len >= 4)
27693db446aSBoris Brezillon 			return;
27793db446aSBoris Brezillon 		udelay(1);
27893db446aSBoris Brezillon 	}
27993db446aSBoris Brezillon 	timeout_error(flctl, __func__);
28093db446aSBoris Brezillon }
28193db446aSBoris Brezillon 
wait_recfifo_ready(struct sh_flctl * flctl,int sector_number)28293db446aSBoris Brezillon static enum flctl_ecc_res_t wait_recfifo_ready
28393db446aSBoris Brezillon 		(struct sh_flctl *flctl, int sector_number)
28493db446aSBoris Brezillon {
28593db446aSBoris Brezillon 	uint32_t timeout = LOOP_TIMEOUT_MAX;
28693db446aSBoris Brezillon 	void __iomem *ecc_reg[4];
28793db446aSBoris Brezillon 	int i;
28893db446aSBoris Brezillon 	int state = FL_SUCCESS;
28993db446aSBoris Brezillon 	uint32_t data, size;
29093db446aSBoris Brezillon 
29193db446aSBoris Brezillon 	/*
29293db446aSBoris Brezillon 	 * First this loops checks in FLDTCNTR if we are ready to read out the
29393db446aSBoris Brezillon 	 * oob data. This is the case if either all went fine without errors or
29493db446aSBoris Brezillon 	 * if the bottom part of the loop corrected the errors or marked them as
29593db446aSBoris Brezillon 	 * uncorrectable and the controller is given time to push the data into
29693db446aSBoris Brezillon 	 * the FIFO.
29793db446aSBoris Brezillon 	 */
29893db446aSBoris Brezillon 	while (timeout--) {
29993db446aSBoris Brezillon 		/* check if all is ok and we can read out the OOB */
30093db446aSBoris Brezillon 		size = readl(FLDTCNTR(flctl)) >> 24;
30193db446aSBoris Brezillon 		if ((size & 0xFF) == 4)
30293db446aSBoris Brezillon 			return state;
30393db446aSBoris Brezillon 
30493db446aSBoris Brezillon 		/* check if a correction code has been calculated */
30593db446aSBoris Brezillon 		if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) {
30693db446aSBoris Brezillon 			/*
30793db446aSBoris Brezillon 			 * either we wait for the fifo to be filled or a
30893db446aSBoris Brezillon 			 * correction pattern is being generated
30993db446aSBoris Brezillon 			 */
31093db446aSBoris Brezillon 			udelay(1);
31193db446aSBoris Brezillon 			continue;
31293db446aSBoris Brezillon 		}
31393db446aSBoris Brezillon 
31493db446aSBoris Brezillon 		/* check for an uncorrectable error */
31593db446aSBoris Brezillon 		if (readl(FL4ECCCR(flctl)) & _4ECCFA) {
31693db446aSBoris Brezillon 			/* check if we face a non-empty page */
31793db446aSBoris Brezillon 			for (i = 0; i < 512; i++) {
31893db446aSBoris Brezillon 				if (flctl->done_buff[i] != 0xff) {
31993db446aSBoris Brezillon 					state = FL_ERROR; /* can't correct */
32093db446aSBoris Brezillon 					break;
32193db446aSBoris Brezillon 				}
32293db446aSBoris Brezillon 			}
32393db446aSBoris Brezillon 
32493db446aSBoris Brezillon 			if (state == FL_SUCCESS)
32593db446aSBoris Brezillon 				dev_dbg(&flctl->pdev->dev,
32693db446aSBoris Brezillon 				"reading empty sector %d, ecc error ignored\n",
32793db446aSBoris Brezillon 				sector_number);
32893db446aSBoris Brezillon 
32993db446aSBoris Brezillon 			writel(0, FL4ECCCR(flctl));
33093db446aSBoris Brezillon 			continue;
33193db446aSBoris Brezillon 		}
33293db446aSBoris Brezillon 
33393db446aSBoris Brezillon 		/* start error correction */
33493db446aSBoris Brezillon 		ecc_reg[0] = FL4ECCRESULT0(flctl);
33593db446aSBoris Brezillon 		ecc_reg[1] = FL4ECCRESULT1(flctl);
33693db446aSBoris Brezillon 		ecc_reg[2] = FL4ECCRESULT2(flctl);
33793db446aSBoris Brezillon 		ecc_reg[3] = FL4ECCRESULT3(flctl);
33893db446aSBoris Brezillon 
33993db446aSBoris Brezillon 		for (i = 0; i < 3; i++) {
34093db446aSBoris Brezillon 			uint8_t org;
34193db446aSBoris Brezillon 			unsigned int index;
34293db446aSBoris Brezillon 
34393db446aSBoris Brezillon 			data = readl(ecc_reg[i]);
34493db446aSBoris Brezillon 
34593db446aSBoris Brezillon 			if (flctl->page_size)
34693db446aSBoris Brezillon 				index = (512 * sector_number) +
34793db446aSBoris Brezillon 					(data >> 16);
34893db446aSBoris Brezillon 			else
34993db446aSBoris Brezillon 				index = data >> 16;
35093db446aSBoris Brezillon 
35193db446aSBoris Brezillon 			org = flctl->done_buff[index];
35293db446aSBoris Brezillon 			flctl->done_buff[index] = org ^ (data & 0xFF);
35393db446aSBoris Brezillon 		}
35493db446aSBoris Brezillon 		state = FL_REPAIRABLE;
35593db446aSBoris Brezillon 		writel(0, FL4ECCCR(flctl));
35693db446aSBoris Brezillon 	}
35793db446aSBoris Brezillon 
35893db446aSBoris Brezillon 	timeout_error(flctl, __func__);
35993db446aSBoris Brezillon 	return FL_TIMEOUT;	/* timeout */
36093db446aSBoris Brezillon }
36193db446aSBoris Brezillon 
wait_wecfifo_ready(struct sh_flctl * flctl)36293db446aSBoris Brezillon static void wait_wecfifo_ready(struct sh_flctl *flctl)
36393db446aSBoris Brezillon {
36493db446aSBoris Brezillon 	uint32_t timeout = LOOP_TIMEOUT_MAX;
36593db446aSBoris Brezillon 	uint32_t len;
36693db446aSBoris Brezillon 
36793db446aSBoris Brezillon 	while (timeout--) {
36893db446aSBoris Brezillon 		/* check FLECFIFO */
36993db446aSBoris Brezillon 		len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF;
37093db446aSBoris Brezillon 		if (len >= 4)
37193db446aSBoris Brezillon 			return;
37293db446aSBoris Brezillon 		udelay(1);
37393db446aSBoris Brezillon 	}
37493db446aSBoris Brezillon 	timeout_error(flctl, __func__);
37593db446aSBoris Brezillon }
37693db446aSBoris Brezillon 
flctl_dma_fifo0_transfer(struct sh_flctl * flctl,unsigned long * buf,int len,enum dma_data_direction dir)37793db446aSBoris Brezillon static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
37893db446aSBoris Brezillon 					int len, enum dma_data_direction dir)
37993db446aSBoris Brezillon {
38093db446aSBoris Brezillon 	struct dma_async_tx_descriptor *desc = NULL;
38193db446aSBoris Brezillon 	struct dma_chan *chan;
38293db446aSBoris Brezillon 	enum dma_transfer_direction tr_dir;
38393db446aSBoris Brezillon 	dma_addr_t dma_addr;
38493db446aSBoris Brezillon 	dma_cookie_t cookie;
38593db446aSBoris Brezillon 	uint32_t reg;
386084c16abSMiaoqian Lin 	int ret = 0;
387084c16abSMiaoqian Lin 	unsigned long time_left;
38893db446aSBoris Brezillon 
38993db446aSBoris Brezillon 	if (dir == DMA_FROM_DEVICE) {
39093db446aSBoris Brezillon 		chan = flctl->chan_fifo0_rx;
39193db446aSBoris Brezillon 		tr_dir = DMA_DEV_TO_MEM;
39293db446aSBoris Brezillon 	} else {
39393db446aSBoris Brezillon 		chan = flctl->chan_fifo0_tx;
39493db446aSBoris Brezillon 		tr_dir = DMA_MEM_TO_DEV;
39593db446aSBoris Brezillon 	}
39693db446aSBoris Brezillon 
39793db446aSBoris Brezillon 	dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
39893db446aSBoris Brezillon 
39993db446aSBoris Brezillon 	if (!dma_mapping_error(chan->device->dev, dma_addr))
40093db446aSBoris Brezillon 		desc = dmaengine_prep_slave_single(chan, dma_addr, len,
40193db446aSBoris Brezillon 			tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
40293db446aSBoris Brezillon 
40393db446aSBoris Brezillon 	if (desc) {
40493db446aSBoris Brezillon 		reg = readl(FLINTDMACR(flctl));
40593db446aSBoris Brezillon 		reg |= DREQ0EN;
40693db446aSBoris Brezillon 		writel(reg, FLINTDMACR(flctl));
40793db446aSBoris Brezillon 
40893db446aSBoris Brezillon 		desc->callback = flctl_dma_complete;
40993db446aSBoris Brezillon 		desc->callback_param = flctl;
41093db446aSBoris Brezillon 		cookie = dmaengine_submit(desc);
41193db446aSBoris Brezillon 		if (dma_submit_error(cookie)) {
41293db446aSBoris Brezillon 			ret = dma_submit_error(cookie);
41393db446aSBoris Brezillon 			dev_warn(&flctl->pdev->dev,
41493db446aSBoris Brezillon 				 "DMA submit failed, falling back to PIO\n");
41593db446aSBoris Brezillon 			goto out;
41693db446aSBoris Brezillon 		}
41793db446aSBoris Brezillon 
41893db446aSBoris Brezillon 		dma_async_issue_pending(chan);
41993db446aSBoris Brezillon 	} else {
42093db446aSBoris Brezillon 		/* DMA failed, fall back to PIO */
42193db446aSBoris Brezillon 		flctl_release_dma(flctl);
42293db446aSBoris Brezillon 		dev_warn(&flctl->pdev->dev,
42393db446aSBoris Brezillon 			 "DMA failed, falling back to PIO\n");
42493db446aSBoris Brezillon 		ret = -EIO;
42593db446aSBoris Brezillon 		goto out;
42693db446aSBoris Brezillon 	}
42793db446aSBoris Brezillon 
428084c16abSMiaoqian Lin 	time_left =
42993db446aSBoris Brezillon 	wait_for_completion_timeout(&flctl->dma_complete,
43093db446aSBoris Brezillon 				msecs_to_jiffies(3000));
43193db446aSBoris Brezillon 
432084c16abSMiaoqian Lin 	if (time_left == 0) {
43393db446aSBoris Brezillon 		dmaengine_terminate_all(chan);
43493db446aSBoris Brezillon 		dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
435084c16abSMiaoqian Lin 		ret = -ETIMEDOUT;
43693db446aSBoris Brezillon 	}
43793db446aSBoris Brezillon 
43893db446aSBoris Brezillon out:
43993db446aSBoris Brezillon 	reg = readl(FLINTDMACR(flctl));
44093db446aSBoris Brezillon 	reg &= ~DREQ0EN;
44193db446aSBoris Brezillon 	writel(reg, FLINTDMACR(flctl));
44293db446aSBoris Brezillon 
44393db446aSBoris Brezillon 	dma_unmap_single(chan->device->dev, dma_addr, len, dir);
44493db446aSBoris Brezillon 
445084c16abSMiaoqian Lin 	/* ret == 0 is success */
44693db446aSBoris Brezillon 	return ret;
44793db446aSBoris Brezillon }
44893db446aSBoris Brezillon 
read_datareg(struct sh_flctl * flctl,int offset)44993db446aSBoris Brezillon static void read_datareg(struct sh_flctl *flctl, int offset)
45093db446aSBoris Brezillon {
45193db446aSBoris Brezillon 	unsigned long data;
45293db446aSBoris Brezillon 	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
45393db446aSBoris Brezillon 
45493db446aSBoris Brezillon 	wait_completion(flctl);
45593db446aSBoris Brezillon 
45693db446aSBoris Brezillon 	data = readl(FLDATAR(flctl));
45793db446aSBoris Brezillon 	*buf = le32_to_cpu(data);
45893db446aSBoris Brezillon }
45993db446aSBoris Brezillon 
read_fiforeg(struct sh_flctl * flctl,int rlen,int offset)46093db446aSBoris Brezillon static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
46193db446aSBoris Brezillon {
46293db446aSBoris Brezillon 	int i, len_4align;
46393db446aSBoris Brezillon 	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
46493db446aSBoris Brezillon 
46593db446aSBoris Brezillon 	len_4align = (rlen + 3) / 4;
46693db446aSBoris Brezillon 
46793db446aSBoris Brezillon 	/* initiate DMA transfer */
46893db446aSBoris Brezillon 	if (flctl->chan_fifo0_rx && rlen >= 32 &&
469084c16abSMiaoqian Lin 		!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE))
47093db446aSBoris Brezillon 			goto convert;	/* DMA success */
47193db446aSBoris Brezillon 
47293db446aSBoris Brezillon 	/* do polling transfer */
47393db446aSBoris Brezillon 	for (i = 0; i < len_4align; i++) {
47493db446aSBoris Brezillon 		wait_rfifo_ready(flctl);
47593db446aSBoris Brezillon 		buf[i] = readl(FLDTFIFO(flctl));
47693db446aSBoris Brezillon 	}
47793db446aSBoris Brezillon 
47893db446aSBoris Brezillon convert:
47993db446aSBoris Brezillon 	for (i = 0; i < len_4align; i++)
48093db446aSBoris Brezillon 		buf[i] = be32_to_cpu(buf[i]);
48193db446aSBoris Brezillon }
48293db446aSBoris Brezillon 
read_ecfiforeg(struct sh_flctl * flctl,uint8_t * buff,int sector)48393db446aSBoris Brezillon static enum flctl_ecc_res_t read_ecfiforeg
48493db446aSBoris Brezillon 		(struct sh_flctl *flctl, uint8_t *buff, int sector)
48593db446aSBoris Brezillon {
48693db446aSBoris Brezillon 	int i;
48793db446aSBoris Brezillon 	enum flctl_ecc_res_t res;
48893db446aSBoris Brezillon 	unsigned long *ecc_buf = (unsigned long *)buff;
48993db446aSBoris Brezillon 
49093db446aSBoris Brezillon 	res = wait_recfifo_ready(flctl , sector);
49193db446aSBoris Brezillon 
49293db446aSBoris Brezillon 	if (res != FL_ERROR) {
49393db446aSBoris Brezillon 		for (i = 0; i < 4; i++) {
49493db446aSBoris Brezillon 			ecc_buf[i] = readl(FLECFIFO(flctl));
49593db446aSBoris Brezillon 			ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
49693db446aSBoris Brezillon 		}
49793db446aSBoris Brezillon 	}
49893db446aSBoris Brezillon 
49993db446aSBoris Brezillon 	return res;
50093db446aSBoris Brezillon }
50193db446aSBoris Brezillon 
write_fiforeg(struct sh_flctl * flctl,int rlen,unsigned int offset)50293db446aSBoris Brezillon static void write_fiforeg(struct sh_flctl *flctl, int rlen,
50393db446aSBoris Brezillon 						unsigned int offset)
50493db446aSBoris Brezillon {
50593db446aSBoris Brezillon 	int i, len_4align;
50693db446aSBoris Brezillon 	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
50793db446aSBoris Brezillon 
50893db446aSBoris Brezillon 	len_4align = (rlen + 3) / 4;
50993db446aSBoris Brezillon 	for (i = 0; i < len_4align; i++) {
51093db446aSBoris Brezillon 		wait_wfifo_ready(flctl);
51193db446aSBoris Brezillon 		writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl));
51293db446aSBoris Brezillon 	}
51393db446aSBoris Brezillon }
51493db446aSBoris Brezillon 
write_ec_fiforeg(struct sh_flctl * flctl,int rlen,unsigned int offset)51593db446aSBoris Brezillon static void write_ec_fiforeg(struct sh_flctl *flctl, int rlen,
51693db446aSBoris Brezillon 						unsigned int offset)
51793db446aSBoris Brezillon {
51893db446aSBoris Brezillon 	int i, len_4align;
51993db446aSBoris Brezillon 	unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
52093db446aSBoris Brezillon 
52193db446aSBoris Brezillon 	len_4align = (rlen + 3) / 4;
52293db446aSBoris Brezillon 
52393db446aSBoris Brezillon 	for (i = 0; i < len_4align; i++)
52493db446aSBoris Brezillon 		buf[i] = cpu_to_be32(buf[i]);
52593db446aSBoris Brezillon 
52693db446aSBoris Brezillon 	/* initiate DMA transfer */
52793db446aSBoris Brezillon 	if (flctl->chan_fifo0_tx && rlen >= 32 &&
528084c16abSMiaoqian Lin 		!flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE))
52993db446aSBoris Brezillon 			return;	/* DMA success */
53093db446aSBoris Brezillon 
53193db446aSBoris Brezillon 	/* do polling transfer */
53293db446aSBoris Brezillon 	for (i = 0; i < len_4align; i++) {
53393db446aSBoris Brezillon 		wait_wecfifo_ready(flctl);
53493db446aSBoris Brezillon 		writel(buf[i], FLECFIFO(flctl));
53593db446aSBoris Brezillon 	}
53693db446aSBoris Brezillon }
53793db446aSBoris Brezillon 
set_cmd_regs(struct mtd_info * mtd,uint32_t cmd,uint32_t flcmcdr_val)53893db446aSBoris Brezillon static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val)
53993db446aSBoris Brezillon {
54093db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
54193db446aSBoris Brezillon 	uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT;
54293db446aSBoris Brezillon 	uint32_t flcmdcr_val, addr_len_bytes = 0;
54393db446aSBoris Brezillon 
54493db446aSBoris Brezillon 	/* Set SNAND bit if page size is 2048byte */
54593db446aSBoris Brezillon 	if (flctl->page_size)
54693db446aSBoris Brezillon 		flcmncr_val |= SNAND_E;
54793db446aSBoris Brezillon 	else
54893db446aSBoris Brezillon 		flcmncr_val &= ~SNAND_E;
54993db446aSBoris Brezillon 
55093db446aSBoris Brezillon 	/* default FLCMDCR val */
55193db446aSBoris Brezillon 	flcmdcr_val = DOCMD1_E | DOADR_E;
55293db446aSBoris Brezillon 
55393db446aSBoris Brezillon 	/* Set for FLCMDCR */
55493db446aSBoris Brezillon 	switch (cmd) {
55593db446aSBoris Brezillon 	case NAND_CMD_ERASE1:
55693db446aSBoris Brezillon 		addr_len_bytes = flctl->erase_ADRCNT;
55793db446aSBoris Brezillon 		flcmdcr_val |= DOCMD2_E;
55893db446aSBoris Brezillon 		break;
55993db446aSBoris Brezillon 	case NAND_CMD_READ0:
56093db446aSBoris Brezillon 	case NAND_CMD_READOOB:
56193db446aSBoris Brezillon 	case NAND_CMD_RNDOUT:
56293db446aSBoris Brezillon 		addr_len_bytes = flctl->rw_ADRCNT;
56393db446aSBoris Brezillon 		flcmdcr_val |= CDSRC_E;
56493db446aSBoris Brezillon 		if (flctl->chip.options & NAND_BUSWIDTH_16)
56593db446aSBoris Brezillon 			flcmncr_val |= SEL_16BIT;
56693db446aSBoris Brezillon 		break;
56793db446aSBoris Brezillon 	case NAND_CMD_SEQIN:
56893db446aSBoris Brezillon 		/* This case is that cmd is READ0 or READ1 or READ00 */
56993db446aSBoris Brezillon 		flcmdcr_val &= ~DOADR_E;	/* ONLY execute 1st cmd */
57093db446aSBoris Brezillon 		break;
57193db446aSBoris Brezillon 	case NAND_CMD_PAGEPROG:
57293db446aSBoris Brezillon 		addr_len_bytes = flctl->rw_ADRCNT;
57393db446aSBoris Brezillon 		flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW;
57493db446aSBoris Brezillon 		if (flctl->chip.options & NAND_BUSWIDTH_16)
57593db446aSBoris Brezillon 			flcmncr_val |= SEL_16BIT;
57693db446aSBoris Brezillon 		break;
57793db446aSBoris Brezillon 	case NAND_CMD_READID:
57893db446aSBoris Brezillon 		flcmncr_val &= ~SNAND_E;
57993db446aSBoris Brezillon 		flcmdcr_val |= CDSRC_E;
58093db446aSBoris Brezillon 		addr_len_bytes = ADRCNT_1;
58193db446aSBoris Brezillon 		break;
58293db446aSBoris Brezillon 	case NAND_CMD_STATUS:
58393db446aSBoris Brezillon 	case NAND_CMD_RESET:
58493db446aSBoris Brezillon 		flcmncr_val &= ~SNAND_E;
58593db446aSBoris Brezillon 		flcmdcr_val &= ~(DOADR_E | DOSR_E);
58693db446aSBoris Brezillon 		break;
58793db446aSBoris Brezillon 	default:
58893db446aSBoris Brezillon 		break;
58993db446aSBoris Brezillon 	}
59093db446aSBoris Brezillon 
59193db446aSBoris Brezillon 	/* Set address bytes parameter */
59293db446aSBoris Brezillon 	flcmdcr_val |= addr_len_bytes;
59393db446aSBoris Brezillon 
59493db446aSBoris Brezillon 	/* Now actually write */
59593db446aSBoris Brezillon 	writel(flcmncr_val, FLCMNCR(flctl));
59693db446aSBoris Brezillon 	writel(flcmdcr_val, FLCMDCR(flctl));
59793db446aSBoris Brezillon 	writel(flcmcdr_val, FLCMCDR(flctl));
59893db446aSBoris Brezillon }
59993db446aSBoris Brezillon 
flctl_read_page_hwecc(struct nand_chip * chip,uint8_t * buf,int oob_required,int page)600b9761687SBoris Brezillon static int flctl_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
601b9761687SBoris Brezillon 				 int oob_required, int page)
60293db446aSBoris Brezillon {
603b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
604b9761687SBoris Brezillon 
60593db446aSBoris Brezillon 	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
60693db446aSBoris Brezillon 	if (oob_required)
607716bbbabSBoris Brezillon 		chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
60893db446aSBoris Brezillon 	return 0;
60993db446aSBoris Brezillon }
61093db446aSBoris Brezillon 
flctl_write_page_hwecc(struct nand_chip * chip,const uint8_t * buf,int oob_required,int page)611767eb6fbSBoris Brezillon static int flctl_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf,
612767eb6fbSBoris Brezillon 				  int oob_required, int page)
61393db446aSBoris Brezillon {
614767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
615767eb6fbSBoris Brezillon 
61693db446aSBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
617716bbbabSBoris Brezillon 	chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
61893db446aSBoris Brezillon 	return nand_prog_page_end_op(chip);
61993db446aSBoris Brezillon }
62093db446aSBoris Brezillon 
execmd_read_page_sector(struct mtd_info * mtd,int page_addr)62193db446aSBoris Brezillon static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
62293db446aSBoris Brezillon {
62393db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
62493db446aSBoris Brezillon 	int sector, page_sectors;
62593db446aSBoris Brezillon 	enum flctl_ecc_res_t ecc_result;
62693db446aSBoris Brezillon 
62793db446aSBoris Brezillon 	page_sectors = flctl->page_size ? 4 : 1;
62893db446aSBoris Brezillon 
62993db446aSBoris Brezillon 	set_cmd_regs(mtd, NAND_CMD_READ0,
63093db446aSBoris Brezillon 		(NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
63193db446aSBoris Brezillon 
63293db446aSBoris Brezillon 	writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT,
63393db446aSBoris Brezillon 		 FLCMNCR(flctl));
63493db446aSBoris Brezillon 	writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
63593db446aSBoris Brezillon 	writel(page_addr << 2, FLADR(flctl));
63693db446aSBoris Brezillon 
63793db446aSBoris Brezillon 	empty_fifo(flctl);
63893db446aSBoris Brezillon 	start_translation(flctl);
63993db446aSBoris Brezillon 
64093db446aSBoris Brezillon 	for (sector = 0; sector < page_sectors; sector++) {
64193db446aSBoris Brezillon 		read_fiforeg(flctl, 512, 512 * sector);
64293db446aSBoris Brezillon 
64393db446aSBoris Brezillon 		ecc_result = read_ecfiforeg(flctl,
64493db446aSBoris Brezillon 			&flctl->done_buff[mtd->writesize + 16 * sector],
64593db446aSBoris Brezillon 			sector);
64693db446aSBoris Brezillon 
64793db446aSBoris Brezillon 		switch (ecc_result) {
64893db446aSBoris Brezillon 		case FL_REPAIRABLE:
64993db446aSBoris Brezillon 			dev_info(&flctl->pdev->dev,
65093db446aSBoris Brezillon 				"applied ecc on page 0x%x", page_addr);
65193db446aSBoris Brezillon 			mtd->ecc_stats.corrected++;
65293db446aSBoris Brezillon 			break;
65393db446aSBoris Brezillon 		case FL_ERROR:
65493db446aSBoris Brezillon 			dev_warn(&flctl->pdev->dev,
65593db446aSBoris Brezillon 				"page 0x%x contains corrupted data\n",
65693db446aSBoris Brezillon 				page_addr);
65793db446aSBoris Brezillon 			mtd->ecc_stats.failed++;
65893db446aSBoris Brezillon 			break;
65993db446aSBoris Brezillon 		default:
66093db446aSBoris Brezillon 			;
66193db446aSBoris Brezillon 		}
66293db446aSBoris Brezillon 	}
66393db446aSBoris Brezillon 
66493db446aSBoris Brezillon 	wait_completion(flctl);
66593db446aSBoris Brezillon 
66693db446aSBoris Brezillon 	writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT),
66793db446aSBoris Brezillon 			FLCMNCR(flctl));
66893db446aSBoris Brezillon }
66993db446aSBoris Brezillon 
execmd_read_oob(struct mtd_info * mtd,int page_addr)67093db446aSBoris Brezillon static void execmd_read_oob(struct mtd_info *mtd, int page_addr)
67193db446aSBoris Brezillon {
67293db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
67393db446aSBoris Brezillon 	int page_sectors = flctl->page_size ? 4 : 1;
67493db446aSBoris Brezillon 	int i;
67593db446aSBoris Brezillon 
67693db446aSBoris Brezillon 	set_cmd_regs(mtd, NAND_CMD_READ0,
67793db446aSBoris Brezillon 		(NAND_CMD_READSTART << 8) | NAND_CMD_READ0);
67893db446aSBoris Brezillon 
67993db446aSBoris Brezillon 	empty_fifo(flctl);
68093db446aSBoris Brezillon 
68193db446aSBoris Brezillon 	for (i = 0; i < page_sectors; i++) {
68293db446aSBoris Brezillon 		set_addr(mtd, (512 + 16) * i + 512 , page_addr);
68393db446aSBoris Brezillon 		writel(16, FLDTCNTR(flctl));
68493db446aSBoris Brezillon 
68593db446aSBoris Brezillon 		start_translation(flctl);
68693db446aSBoris Brezillon 		read_fiforeg(flctl, 16, 16 * i);
68793db446aSBoris Brezillon 		wait_completion(flctl);
68893db446aSBoris Brezillon 	}
68993db446aSBoris Brezillon }
69093db446aSBoris Brezillon 
execmd_write_page_sector(struct mtd_info * mtd)69193db446aSBoris Brezillon static void execmd_write_page_sector(struct mtd_info *mtd)
69293db446aSBoris Brezillon {
69393db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
69493db446aSBoris Brezillon 	int page_addr = flctl->seqin_page_addr;
69593db446aSBoris Brezillon 	int sector, page_sectors;
69693db446aSBoris Brezillon 
69793db446aSBoris Brezillon 	page_sectors = flctl->page_size ? 4 : 1;
69893db446aSBoris Brezillon 
69993db446aSBoris Brezillon 	set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
70093db446aSBoris Brezillon 			(NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
70193db446aSBoris Brezillon 
70293db446aSBoris Brezillon 	empty_fifo(flctl);
70393db446aSBoris Brezillon 	writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl));
70493db446aSBoris Brezillon 	writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl));
70593db446aSBoris Brezillon 	writel(page_addr << 2, FLADR(flctl));
70693db446aSBoris Brezillon 	start_translation(flctl);
70793db446aSBoris Brezillon 
70893db446aSBoris Brezillon 	for (sector = 0; sector < page_sectors; sector++) {
70993db446aSBoris Brezillon 		write_fiforeg(flctl, 512, 512 * sector);
71093db446aSBoris Brezillon 		write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector);
71193db446aSBoris Brezillon 	}
71293db446aSBoris Brezillon 
71393db446aSBoris Brezillon 	wait_completion(flctl);
71493db446aSBoris Brezillon 	writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl));
71593db446aSBoris Brezillon }
71693db446aSBoris Brezillon 
execmd_write_oob(struct mtd_info * mtd)71793db446aSBoris Brezillon static void execmd_write_oob(struct mtd_info *mtd)
71893db446aSBoris Brezillon {
71993db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
72093db446aSBoris Brezillon 	int page_addr = flctl->seqin_page_addr;
72193db446aSBoris Brezillon 	int sector, page_sectors;
72293db446aSBoris Brezillon 
72393db446aSBoris Brezillon 	page_sectors = flctl->page_size ? 4 : 1;
72493db446aSBoris Brezillon 
72593db446aSBoris Brezillon 	set_cmd_regs(mtd, NAND_CMD_PAGEPROG,
72693db446aSBoris Brezillon 			(NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN);
72793db446aSBoris Brezillon 
72893db446aSBoris Brezillon 	for (sector = 0; sector < page_sectors; sector++) {
72993db446aSBoris Brezillon 		empty_fifo(flctl);
73093db446aSBoris Brezillon 		set_addr(mtd, sector * 528 + 512, page_addr);
73193db446aSBoris Brezillon 		writel(16, FLDTCNTR(flctl));	/* set read size */
73293db446aSBoris Brezillon 
73393db446aSBoris Brezillon 		start_translation(flctl);
73493db446aSBoris Brezillon 		write_fiforeg(flctl, 16, 16 * sector);
73593db446aSBoris Brezillon 		wait_completion(flctl);
73693db446aSBoris Brezillon 	}
73793db446aSBoris Brezillon }
73893db446aSBoris Brezillon 
flctl_cmdfunc(struct nand_chip * chip,unsigned int command,int column,int page_addr)7395295cf2eSBoris Brezillon static void flctl_cmdfunc(struct nand_chip *chip, unsigned int command,
74093db446aSBoris Brezillon 			int column, int page_addr)
74193db446aSBoris Brezillon {
7425295cf2eSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
74393db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
74493db446aSBoris Brezillon 	uint32_t read_cmd = 0;
74593db446aSBoris Brezillon 
74693db446aSBoris Brezillon 	pm_runtime_get_sync(&flctl->pdev->dev);
74793db446aSBoris Brezillon 
74893db446aSBoris Brezillon 	flctl->read_bytes = 0;
74993db446aSBoris Brezillon 	if (command != NAND_CMD_PAGEPROG)
75093db446aSBoris Brezillon 		flctl->index = 0;
75193db446aSBoris Brezillon 
75293db446aSBoris Brezillon 	switch (command) {
75393db446aSBoris Brezillon 	case NAND_CMD_READ1:
75493db446aSBoris Brezillon 	case NAND_CMD_READ0:
75593db446aSBoris Brezillon 		if (flctl->hwecc) {
75693db446aSBoris Brezillon 			/* read page with hwecc */
75793db446aSBoris Brezillon 			execmd_read_page_sector(mtd, page_addr);
75893db446aSBoris Brezillon 			break;
75993db446aSBoris Brezillon 		}
76093db446aSBoris Brezillon 		if (flctl->page_size)
76193db446aSBoris Brezillon 			set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
76293db446aSBoris Brezillon 				| command);
76393db446aSBoris Brezillon 		else
76493db446aSBoris Brezillon 			set_cmd_regs(mtd, command, command);
76593db446aSBoris Brezillon 
76693db446aSBoris Brezillon 		set_addr(mtd, 0, page_addr);
76793db446aSBoris Brezillon 
76893db446aSBoris Brezillon 		flctl->read_bytes = mtd->writesize + mtd->oobsize;
76993db446aSBoris Brezillon 		if (flctl->chip.options & NAND_BUSWIDTH_16)
77093db446aSBoris Brezillon 			column >>= 1;
77193db446aSBoris Brezillon 		flctl->index += column;
77293db446aSBoris Brezillon 		goto read_normal_exit;
77393db446aSBoris Brezillon 
77493db446aSBoris Brezillon 	case NAND_CMD_READOOB:
77593db446aSBoris Brezillon 		if (flctl->hwecc) {
77693db446aSBoris Brezillon 			/* read page with hwecc */
77793db446aSBoris Brezillon 			execmd_read_oob(mtd, page_addr);
77893db446aSBoris Brezillon 			break;
77993db446aSBoris Brezillon 		}
78093db446aSBoris Brezillon 
78193db446aSBoris Brezillon 		if (flctl->page_size) {
78293db446aSBoris Brezillon 			set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8)
78393db446aSBoris Brezillon 				| NAND_CMD_READ0);
78493db446aSBoris Brezillon 			set_addr(mtd, mtd->writesize, page_addr);
78593db446aSBoris Brezillon 		} else {
78693db446aSBoris Brezillon 			set_cmd_regs(mtd, command, command);
78793db446aSBoris Brezillon 			set_addr(mtd, 0, page_addr);
78893db446aSBoris Brezillon 		}
78993db446aSBoris Brezillon 		flctl->read_bytes = mtd->oobsize;
79093db446aSBoris Brezillon 		goto read_normal_exit;
79193db446aSBoris Brezillon 
79293db446aSBoris Brezillon 	case NAND_CMD_RNDOUT:
79393db446aSBoris Brezillon 		if (flctl->hwecc)
79493db446aSBoris Brezillon 			break;
79593db446aSBoris Brezillon 
79693db446aSBoris Brezillon 		if (flctl->page_size)
79793db446aSBoris Brezillon 			set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8)
79893db446aSBoris Brezillon 				| command);
79993db446aSBoris Brezillon 		else
80093db446aSBoris Brezillon 			set_cmd_regs(mtd, command, command);
80193db446aSBoris Brezillon 
80293db446aSBoris Brezillon 		set_addr(mtd, column, 0);
80393db446aSBoris Brezillon 
80493db446aSBoris Brezillon 		flctl->read_bytes = mtd->writesize + mtd->oobsize - column;
80593db446aSBoris Brezillon 		goto read_normal_exit;
80693db446aSBoris Brezillon 
80793db446aSBoris Brezillon 	case NAND_CMD_READID:
80893db446aSBoris Brezillon 		set_cmd_regs(mtd, command, command);
80993db446aSBoris Brezillon 
81093db446aSBoris Brezillon 		/* READID is always performed using an 8-bit bus */
81193db446aSBoris Brezillon 		if (flctl->chip.options & NAND_BUSWIDTH_16)
81293db446aSBoris Brezillon 			column <<= 1;
81393db446aSBoris Brezillon 		set_addr(mtd, column, 0);
81493db446aSBoris Brezillon 
81593db446aSBoris Brezillon 		flctl->read_bytes = 8;
81693db446aSBoris Brezillon 		writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
81793db446aSBoris Brezillon 		empty_fifo(flctl);
81893db446aSBoris Brezillon 		start_translation(flctl);
81993db446aSBoris Brezillon 		read_fiforeg(flctl, flctl->read_bytes, 0);
82093db446aSBoris Brezillon 		wait_completion(flctl);
82193db446aSBoris Brezillon 		break;
82293db446aSBoris Brezillon 
82393db446aSBoris Brezillon 	case NAND_CMD_ERASE1:
82493db446aSBoris Brezillon 		flctl->erase1_page_addr = page_addr;
82593db446aSBoris Brezillon 		break;
82693db446aSBoris Brezillon 
82793db446aSBoris Brezillon 	case NAND_CMD_ERASE2:
82893db446aSBoris Brezillon 		set_cmd_regs(mtd, NAND_CMD_ERASE1,
82993db446aSBoris Brezillon 			(command << 8) | NAND_CMD_ERASE1);
83093db446aSBoris Brezillon 		set_addr(mtd, -1, flctl->erase1_page_addr);
83193db446aSBoris Brezillon 		start_translation(flctl);
83293db446aSBoris Brezillon 		wait_completion(flctl);
83393db446aSBoris Brezillon 		break;
83493db446aSBoris Brezillon 
83593db446aSBoris Brezillon 	case NAND_CMD_SEQIN:
83693db446aSBoris Brezillon 		if (!flctl->page_size) {
83793db446aSBoris Brezillon 			/* output read command */
83893db446aSBoris Brezillon 			if (column >= mtd->writesize) {
83993db446aSBoris Brezillon 				column -= mtd->writesize;
84093db446aSBoris Brezillon 				read_cmd = NAND_CMD_READOOB;
84193db446aSBoris Brezillon 			} else if (column < 256) {
84293db446aSBoris Brezillon 				read_cmd = NAND_CMD_READ0;
84393db446aSBoris Brezillon 			} else {
84493db446aSBoris Brezillon 				column -= 256;
84593db446aSBoris Brezillon 				read_cmd = NAND_CMD_READ1;
84693db446aSBoris Brezillon 			}
84793db446aSBoris Brezillon 		}
84893db446aSBoris Brezillon 		flctl->seqin_column = column;
84993db446aSBoris Brezillon 		flctl->seqin_page_addr = page_addr;
85093db446aSBoris Brezillon 		flctl->seqin_read_cmd = read_cmd;
85193db446aSBoris Brezillon 		break;
85293db446aSBoris Brezillon 
85393db446aSBoris Brezillon 	case NAND_CMD_PAGEPROG:
85493db446aSBoris Brezillon 		empty_fifo(flctl);
85593db446aSBoris Brezillon 		if (!flctl->page_size) {
85693db446aSBoris Brezillon 			set_cmd_regs(mtd, NAND_CMD_SEQIN,
85793db446aSBoris Brezillon 					flctl->seqin_read_cmd);
85893db446aSBoris Brezillon 			set_addr(mtd, -1, -1);
85993db446aSBoris Brezillon 			writel(0, FLDTCNTR(flctl));	/* set 0 size */
86093db446aSBoris Brezillon 			start_translation(flctl);
86193db446aSBoris Brezillon 			wait_completion(flctl);
86293db446aSBoris Brezillon 		}
86393db446aSBoris Brezillon 		if (flctl->hwecc) {
86493db446aSBoris Brezillon 			/* write page with hwecc */
86593db446aSBoris Brezillon 			if (flctl->seqin_column == mtd->writesize)
86693db446aSBoris Brezillon 				execmd_write_oob(mtd);
86793db446aSBoris Brezillon 			else if (!flctl->seqin_column)
86893db446aSBoris Brezillon 				execmd_write_page_sector(mtd);
86993db446aSBoris Brezillon 			else
87063fa37f0SShreeya Patel 				pr_err("Invalid address !?\n");
87193db446aSBoris Brezillon 			break;
87293db446aSBoris Brezillon 		}
87393db446aSBoris Brezillon 		set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN);
87493db446aSBoris Brezillon 		set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr);
87593db446aSBoris Brezillon 		writel(flctl->index, FLDTCNTR(flctl));	/* set write size */
87693db446aSBoris Brezillon 		start_translation(flctl);
87793db446aSBoris Brezillon 		write_fiforeg(flctl, flctl->index, 0);
87893db446aSBoris Brezillon 		wait_completion(flctl);
87993db446aSBoris Brezillon 		break;
88093db446aSBoris Brezillon 
88193db446aSBoris Brezillon 	case NAND_CMD_STATUS:
88293db446aSBoris Brezillon 		set_cmd_regs(mtd, command, command);
88393db446aSBoris Brezillon 		set_addr(mtd, -1, -1);
88493db446aSBoris Brezillon 
88593db446aSBoris Brezillon 		flctl->read_bytes = 1;
88693db446aSBoris Brezillon 		writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */
88793db446aSBoris Brezillon 		start_translation(flctl);
88893db446aSBoris Brezillon 		read_datareg(flctl, 0); /* read and end */
88993db446aSBoris Brezillon 		break;
89093db446aSBoris Brezillon 
89193db446aSBoris Brezillon 	case NAND_CMD_RESET:
89293db446aSBoris Brezillon 		set_cmd_regs(mtd, command, command);
89393db446aSBoris Brezillon 		set_addr(mtd, -1, -1);
89493db446aSBoris Brezillon 
89593db446aSBoris Brezillon 		writel(0, FLDTCNTR(flctl));	/* set 0 size */
89693db446aSBoris Brezillon 		start_translation(flctl);
89793db446aSBoris Brezillon 		wait_completion(flctl);
89893db446aSBoris Brezillon 		break;
89993db446aSBoris Brezillon 
90093db446aSBoris Brezillon 	default:
90193db446aSBoris Brezillon 		break;
90293db446aSBoris Brezillon 	}
90393db446aSBoris Brezillon 	goto runtime_exit;
90493db446aSBoris Brezillon 
90593db446aSBoris Brezillon read_normal_exit:
90693db446aSBoris Brezillon 	writel(flctl->read_bytes, FLDTCNTR(flctl));	/* set read size */
90793db446aSBoris Brezillon 	empty_fifo(flctl);
90893db446aSBoris Brezillon 	start_translation(flctl);
90993db446aSBoris Brezillon 	read_fiforeg(flctl, flctl->read_bytes, 0);
91093db446aSBoris Brezillon 	wait_completion(flctl);
91193db446aSBoris Brezillon runtime_exit:
91293db446aSBoris Brezillon 	pm_runtime_put_sync(&flctl->pdev->dev);
91393db446aSBoris Brezillon 	return;
91493db446aSBoris Brezillon }
91593db446aSBoris Brezillon 
flctl_select_chip(struct nand_chip * chip,int chipnr)916758b56f5SBoris Brezillon static void flctl_select_chip(struct nand_chip *chip, int chipnr)
91793db446aSBoris Brezillon {
918758b56f5SBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
91993db446aSBoris Brezillon 	int ret;
92093db446aSBoris Brezillon 
92193db446aSBoris Brezillon 	switch (chipnr) {
92293db446aSBoris Brezillon 	case -1:
92393db446aSBoris Brezillon 		flctl->flcmncr_base &= ~CE0_ENABLE;
92493db446aSBoris Brezillon 
92593db446aSBoris Brezillon 		pm_runtime_get_sync(&flctl->pdev->dev);
92693db446aSBoris Brezillon 		writel(flctl->flcmncr_base, FLCMNCR(flctl));
92793db446aSBoris Brezillon 
92893db446aSBoris Brezillon 		if (flctl->qos_request) {
92993db446aSBoris Brezillon 			dev_pm_qos_remove_request(&flctl->pm_qos);
93093db446aSBoris Brezillon 			flctl->qos_request = 0;
93193db446aSBoris Brezillon 		}
93293db446aSBoris Brezillon 
93393db446aSBoris Brezillon 		pm_runtime_put_sync(&flctl->pdev->dev);
93493db446aSBoris Brezillon 		break;
93593db446aSBoris Brezillon 	case 0:
93693db446aSBoris Brezillon 		flctl->flcmncr_base |= CE0_ENABLE;
93793db446aSBoris Brezillon 
93893db446aSBoris Brezillon 		if (!flctl->qos_request) {
93993db446aSBoris Brezillon 			ret = dev_pm_qos_add_request(&flctl->pdev->dev,
94093db446aSBoris Brezillon 							&flctl->pm_qos,
94193db446aSBoris Brezillon 							DEV_PM_QOS_RESUME_LATENCY,
94293db446aSBoris Brezillon 							100);
94393db446aSBoris Brezillon 			if (ret < 0)
94493db446aSBoris Brezillon 				dev_err(&flctl->pdev->dev,
94593db446aSBoris Brezillon 					"PM QoS request failed: %d\n", ret);
94693db446aSBoris Brezillon 			flctl->qos_request = 1;
94793db446aSBoris Brezillon 		}
94893db446aSBoris Brezillon 
94993db446aSBoris Brezillon 		if (flctl->holden) {
95093db446aSBoris Brezillon 			pm_runtime_get_sync(&flctl->pdev->dev);
95193db446aSBoris Brezillon 			writel(HOLDEN, FLHOLDCR(flctl));
95293db446aSBoris Brezillon 			pm_runtime_put_sync(&flctl->pdev->dev);
95393db446aSBoris Brezillon 		}
95493db446aSBoris Brezillon 		break;
95593db446aSBoris Brezillon 	default:
95693db446aSBoris Brezillon 		BUG();
95793db446aSBoris Brezillon 	}
95893db446aSBoris Brezillon }
95993db446aSBoris Brezillon 
flctl_write_buf(struct nand_chip * chip,const uint8_t * buf,int len)960c0739d85SBoris Brezillon static void flctl_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
96193db446aSBoris Brezillon {
962c0739d85SBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
96393db446aSBoris Brezillon 
96493db446aSBoris Brezillon 	memcpy(&flctl->done_buff[flctl->index], buf, len);
96593db446aSBoris Brezillon 	flctl->index += len;
96693db446aSBoris Brezillon }
96793db446aSBoris Brezillon 
flctl_read_byte(struct nand_chip * chip)9687e534323SBoris Brezillon static uint8_t flctl_read_byte(struct nand_chip *chip)
96993db446aSBoris Brezillon {
9707e534323SBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
97193db446aSBoris Brezillon 	uint8_t data;
97293db446aSBoris Brezillon 
97393db446aSBoris Brezillon 	data = flctl->done_buff[flctl->index];
97493db446aSBoris Brezillon 	flctl->index++;
97593db446aSBoris Brezillon 	return data;
97693db446aSBoris Brezillon }
97793db446aSBoris Brezillon 
flctl_read_buf(struct nand_chip * chip,uint8_t * buf,int len)9787e534323SBoris Brezillon static void flctl_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
97993db446aSBoris Brezillon {
9807e534323SBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip));
98193db446aSBoris Brezillon 
98293db446aSBoris Brezillon 	memcpy(buf, &flctl->done_buff[flctl->index], len);
98393db446aSBoris Brezillon 	flctl->index += len;
98493db446aSBoris Brezillon }
98593db446aSBoris Brezillon 
flctl_chip_attach_chip(struct nand_chip * chip)9865f20da0fSMiquel Raynal static int flctl_chip_attach_chip(struct nand_chip *chip)
98793db446aSBoris Brezillon {
9886c836d51SBoris Brezillon 	u64 targetsize = nanddev_target_size(&chip->base);
9895f20da0fSMiquel Raynal 	struct mtd_info *mtd = nand_to_mtd(chip);
99093db446aSBoris Brezillon 	struct sh_flctl *flctl = mtd_to_flctl(mtd);
9915f20da0fSMiquel Raynal 
9925f20da0fSMiquel Raynal 	/*
9935f20da0fSMiquel Raynal 	 * NAND_BUSWIDTH_16 may have been set by nand_scan_ident().
9945f20da0fSMiquel Raynal 	 * Add the SEL_16BIT flag in flctl->flcmncr_base.
9955f20da0fSMiquel Raynal 	 */
9965f20da0fSMiquel Raynal 	if (chip->options & NAND_BUSWIDTH_16)
9975f20da0fSMiquel Raynal 		flctl->flcmncr_base |= SEL_16BIT;
99893db446aSBoris Brezillon 
99993db446aSBoris Brezillon 	if (mtd->writesize == 512) {
100093db446aSBoris Brezillon 		flctl->page_size = 0;
10016c836d51SBoris Brezillon 		if (targetsize > (32 << 20)) {
100293db446aSBoris Brezillon 			/* big than 32MB */
100393db446aSBoris Brezillon 			flctl->rw_ADRCNT = ADRCNT_4;
100493db446aSBoris Brezillon 			flctl->erase_ADRCNT = ADRCNT_3;
10056c836d51SBoris Brezillon 		} else if (targetsize > (2 << 16)) {
100693db446aSBoris Brezillon 			/* big than 128KB */
100793db446aSBoris Brezillon 			flctl->rw_ADRCNT = ADRCNT_3;
100893db446aSBoris Brezillon 			flctl->erase_ADRCNT = ADRCNT_2;
100993db446aSBoris Brezillon 		} else {
101093db446aSBoris Brezillon 			flctl->rw_ADRCNT = ADRCNT_2;
101193db446aSBoris Brezillon 			flctl->erase_ADRCNT = ADRCNT_1;
101293db446aSBoris Brezillon 		}
101393db446aSBoris Brezillon 	} else {
101493db446aSBoris Brezillon 		flctl->page_size = 1;
10156c836d51SBoris Brezillon 		if (targetsize > (128 << 20)) {
101693db446aSBoris Brezillon 			/* big than 128MB */
101793db446aSBoris Brezillon 			flctl->rw_ADRCNT = ADRCNT2_E;
101893db446aSBoris Brezillon 			flctl->erase_ADRCNT = ADRCNT_3;
10196c836d51SBoris Brezillon 		} else if (targetsize > (8 << 16)) {
102093db446aSBoris Brezillon 			/* big than 512KB */
102193db446aSBoris Brezillon 			flctl->rw_ADRCNT = ADRCNT_4;
102293db446aSBoris Brezillon 			flctl->erase_ADRCNT = ADRCNT_2;
102393db446aSBoris Brezillon 		} else {
102493db446aSBoris Brezillon 			flctl->rw_ADRCNT = ADRCNT_3;
102593db446aSBoris Brezillon 			flctl->erase_ADRCNT = ADRCNT_1;
102693db446aSBoris Brezillon 		}
102793db446aSBoris Brezillon 	}
102893db446aSBoris Brezillon 
102993db446aSBoris Brezillon 	if (flctl->hwecc) {
103093db446aSBoris Brezillon 		if (mtd->writesize == 512) {
103193db446aSBoris Brezillon 			mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops);
103293db446aSBoris Brezillon 			chip->badblock_pattern = &flctl_4secc_smallpage;
103393db446aSBoris Brezillon 		} else {
103493db446aSBoris Brezillon 			mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops);
103593db446aSBoris Brezillon 			chip->badblock_pattern = &flctl_4secc_largepage;
103693db446aSBoris Brezillon 		}
103793db446aSBoris Brezillon 
103893db446aSBoris Brezillon 		chip->ecc.size = 512;
103993db446aSBoris Brezillon 		chip->ecc.bytes = 10;
104093db446aSBoris Brezillon 		chip->ecc.strength = 4;
104193db446aSBoris Brezillon 		chip->ecc.read_page = flctl_read_page_hwecc;
104293db446aSBoris Brezillon 		chip->ecc.write_page = flctl_write_page_hwecc;
1043bace41f8SMiquel Raynal 		chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
104493db446aSBoris Brezillon 
104593db446aSBoris Brezillon 		/* 4 symbols ECC enabled */
104693db446aSBoris Brezillon 		flctl->flcmncr_base |= _4ECCEN;
104793db446aSBoris Brezillon 	} else {
1048bace41f8SMiquel Raynal 		chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
1049e0a564aeSMiquel Raynal 		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
105093db446aSBoris Brezillon 	}
105193db446aSBoris Brezillon 
105293db446aSBoris Brezillon 	return 0;
105393db446aSBoris Brezillon }
105493db446aSBoris Brezillon 
10555f20da0fSMiquel Raynal static const struct nand_controller_ops flctl_nand_controller_ops = {
10565f20da0fSMiquel Raynal 	.attach_chip = flctl_chip_attach_chip,
10575f20da0fSMiquel Raynal };
10585f20da0fSMiquel Raynal 
flctl_handle_flste(int irq,void * dev_id)105993db446aSBoris Brezillon static irqreturn_t flctl_handle_flste(int irq, void *dev_id)
106093db446aSBoris Brezillon {
106193db446aSBoris Brezillon 	struct sh_flctl *flctl = dev_id;
106293db446aSBoris Brezillon 
106393db446aSBoris Brezillon 	dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl)));
106493db446aSBoris Brezillon 	writel(flctl->flintdmacr_base, FLINTDMACR(flctl));
106593db446aSBoris Brezillon 
106693db446aSBoris Brezillon 	return IRQ_HANDLED;
106793db446aSBoris Brezillon }
106893db446aSBoris Brezillon 
106993db446aSBoris Brezillon struct flctl_soc_config {
107093db446aSBoris Brezillon 	unsigned long flcmncr_val;
107193db446aSBoris Brezillon 	unsigned has_hwecc:1;
107293db446aSBoris Brezillon 	unsigned use_holden:1;
107393db446aSBoris Brezillon };
107493db446aSBoris Brezillon 
107593db446aSBoris Brezillon static struct flctl_soc_config flctl_sh7372_config = {
107693db446aSBoris Brezillon 	.flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL,
107793db446aSBoris Brezillon 	.has_hwecc = 1,
107893db446aSBoris Brezillon 	.use_holden = 1,
107993db446aSBoris Brezillon };
108093db446aSBoris Brezillon 
108193db446aSBoris Brezillon static const struct of_device_id of_flctl_match[] = {
108293db446aSBoris Brezillon 	{ .compatible = "renesas,shmobile-flctl-sh7372",
108393db446aSBoris Brezillon 				.data = &flctl_sh7372_config },
108493db446aSBoris Brezillon 	{},
108593db446aSBoris Brezillon };
108693db446aSBoris Brezillon MODULE_DEVICE_TABLE(of, of_flctl_match);
108793db446aSBoris Brezillon 
flctl_parse_dt(struct device * dev)108893db446aSBoris Brezillon static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
108993db446aSBoris Brezillon {
109093db446aSBoris Brezillon 	const struct flctl_soc_config *config;
109193db446aSBoris Brezillon 	struct sh_flctl_platform_data *pdata;
109293db446aSBoris Brezillon 
109393db446aSBoris Brezillon 	config = of_device_get_match_data(dev);
109493db446aSBoris Brezillon 	if (!config) {
109593db446aSBoris Brezillon 		dev_err(dev, "%s: no OF configuration attached\n", __func__);
109693db446aSBoris Brezillon 		return NULL;
109793db446aSBoris Brezillon 	}
109893db446aSBoris Brezillon 
109993db446aSBoris Brezillon 	pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data),
110093db446aSBoris Brezillon 								GFP_KERNEL);
110193db446aSBoris Brezillon 	if (!pdata)
110293db446aSBoris Brezillon 		return NULL;
110393db446aSBoris Brezillon 
110493db446aSBoris Brezillon 	/* set SoC specific options */
110593db446aSBoris Brezillon 	pdata->flcmncr_val = config->flcmncr_val;
110693db446aSBoris Brezillon 	pdata->has_hwecc = config->has_hwecc;
110793db446aSBoris Brezillon 	pdata->use_holden = config->use_holden;
110893db446aSBoris Brezillon 
110993db446aSBoris Brezillon 	return pdata;
111093db446aSBoris Brezillon }
111193db446aSBoris Brezillon 
flctl_probe(struct platform_device * pdev)111293db446aSBoris Brezillon static int flctl_probe(struct platform_device *pdev)
111393db446aSBoris Brezillon {
111493db446aSBoris Brezillon 	struct resource *res;
111593db446aSBoris Brezillon 	struct sh_flctl *flctl;
111693db446aSBoris Brezillon 	struct mtd_info *flctl_mtd;
111793db446aSBoris Brezillon 	struct nand_chip *nand;
111893db446aSBoris Brezillon 	struct sh_flctl_platform_data *pdata;
111993db446aSBoris Brezillon 	int ret;
112093db446aSBoris Brezillon 	int irq;
112193db446aSBoris Brezillon 
112293db446aSBoris Brezillon 	flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL);
112393db446aSBoris Brezillon 	if (!flctl)
112493db446aSBoris Brezillon 		return -ENOMEM;
112593db446aSBoris Brezillon 
1126*4eef841dSYangtao Li 	flctl->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
112793db446aSBoris Brezillon 	if (IS_ERR(flctl->reg))
112893db446aSBoris Brezillon 		return PTR_ERR(flctl->reg);
112993db446aSBoris Brezillon 	flctl->fifo = res->start + 0x24; /* FLDTFIFO */
113093db446aSBoris Brezillon 
113193db446aSBoris Brezillon 	irq = platform_get_irq(pdev, 0);
1132aab478caSStephen Boyd 	if (irq < 0)
113393db446aSBoris Brezillon 		return irq;
113493db446aSBoris Brezillon 
113593db446aSBoris Brezillon 	ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED,
113693db446aSBoris Brezillon 			       "flste", flctl);
113793db446aSBoris Brezillon 	if (ret) {
113893db446aSBoris Brezillon 		dev_err(&pdev->dev, "request interrupt failed.\n");
113993db446aSBoris Brezillon 		return ret;
114093db446aSBoris Brezillon 	}
114193db446aSBoris Brezillon 
114293db446aSBoris Brezillon 	if (pdev->dev.of_node)
114393db446aSBoris Brezillon 		pdata = flctl_parse_dt(&pdev->dev);
114493db446aSBoris Brezillon 	else
114593db446aSBoris Brezillon 		pdata = dev_get_platdata(&pdev->dev);
114693db446aSBoris Brezillon 
114793db446aSBoris Brezillon 	if (!pdata) {
114893db446aSBoris Brezillon 		dev_err(&pdev->dev, "no setup data defined\n");
114993db446aSBoris Brezillon 		return -EINVAL;
115093db446aSBoris Brezillon 	}
115193db446aSBoris Brezillon 
115293db446aSBoris Brezillon 	platform_set_drvdata(pdev, flctl);
115393db446aSBoris Brezillon 	nand = &flctl->chip;
115493db446aSBoris Brezillon 	flctl_mtd = nand_to_mtd(nand);
115593db446aSBoris Brezillon 	nand_set_flash_node(nand, pdev->dev.of_node);
115693db446aSBoris Brezillon 	flctl_mtd->dev.parent = &pdev->dev;
115793db446aSBoris Brezillon 	flctl->pdev = pdev;
115893db446aSBoris Brezillon 	flctl->hwecc = pdata->has_hwecc;
115993db446aSBoris Brezillon 	flctl->holden = pdata->use_holden;
116093db446aSBoris Brezillon 	flctl->flcmncr_base = pdata->flcmncr_val;
116193db446aSBoris Brezillon 	flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE;
116293db446aSBoris Brezillon 
116393db446aSBoris Brezillon 	/* Set address of hardware control function */
116493db446aSBoris Brezillon 	/* 20 us command delay time */
11653cece3abSBoris Brezillon 	nand->legacy.chip_delay = 20;
116693db446aSBoris Brezillon 
1167716bbbabSBoris Brezillon 	nand->legacy.read_byte = flctl_read_byte;
1168716bbbabSBoris Brezillon 	nand->legacy.write_buf = flctl_write_buf;
1169716bbbabSBoris Brezillon 	nand->legacy.read_buf = flctl_read_buf;
11707d6c37e9SBoris Brezillon 	nand->legacy.select_chip = flctl_select_chip;
1171bf6065c6SBoris Brezillon 	nand->legacy.cmdfunc = flctl_cmdfunc;
117245240367SBoris Brezillon 	nand->legacy.set_features = nand_get_set_features_notsupp;
117345240367SBoris Brezillon 	nand->legacy.get_features = nand_get_set_features_notsupp;
117493db446aSBoris Brezillon 
117593db446aSBoris Brezillon 	if (pdata->flcmncr_val & SEL_16BIT)
117693db446aSBoris Brezillon 		nand->options |= NAND_BUSWIDTH_16;
117793db446aSBoris Brezillon 
1178bb592548SFrieder Schrempf 	nand->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE;
117904649ec1SFrieder Schrempf 
118093db446aSBoris Brezillon 	pm_runtime_enable(&pdev->dev);
118193db446aSBoris Brezillon 	pm_runtime_resume(&pdev->dev);
118293db446aSBoris Brezillon 
118393db446aSBoris Brezillon 	flctl_setup_dma(flctl);
118493db446aSBoris Brezillon 
11857b6a9b28SBoris Brezillon 	nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops;
118600ad378fSBoris Brezillon 	ret = nand_scan(nand, 1);
118793db446aSBoris Brezillon 	if (ret)
118893db446aSBoris Brezillon 		goto err_chip;
118993db446aSBoris Brezillon 
119093db446aSBoris Brezillon 	ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts);
11915a3e8cdeSMiquel Raynal 	if (ret)
11925a3e8cdeSMiquel Raynal 		goto cleanup_nand;
119393db446aSBoris Brezillon 
119493db446aSBoris Brezillon 	return 0;
119593db446aSBoris Brezillon 
11965a3e8cdeSMiquel Raynal cleanup_nand:
11975a3e8cdeSMiquel Raynal 	nand_cleanup(nand);
119893db446aSBoris Brezillon err_chip:
119993db446aSBoris Brezillon 	flctl_release_dma(flctl);
120093db446aSBoris Brezillon 	pm_runtime_disable(&pdev->dev);
120193db446aSBoris Brezillon 	return ret;
120293db446aSBoris Brezillon }
120393db446aSBoris Brezillon 
flctl_remove(struct platform_device * pdev)1204ec185b18SUwe Kleine-König static void flctl_remove(struct platform_device *pdev)
120593db446aSBoris Brezillon {
120693db446aSBoris Brezillon 	struct sh_flctl *flctl = platform_get_drvdata(pdev);
120750abacbbSMiquel Raynal 	struct nand_chip *chip = &flctl->chip;
120850abacbbSMiquel Raynal 	int ret;
120993db446aSBoris Brezillon 
121093db446aSBoris Brezillon 	flctl_release_dma(flctl);
121150abacbbSMiquel Raynal 	ret = mtd_device_unregister(nand_to_mtd(chip));
121250abacbbSMiquel Raynal 	WARN_ON(ret);
121350abacbbSMiquel Raynal 	nand_cleanup(chip);
121493db446aSBoris Brezillon 	pm_runtime_disable(&pdev->dev);
121593db446aSBoris Brezillon }
121693db446aSBoris Brezillon 
121793db446aSBoris Brezillon static struct platform_driver flctl_driver = {
1218ec185b18SUwe Kleine-König 	.remove_new	= flctl_remove,
121993db446aSBoris Brezillon 	.driver = {
122093db446aSBoris Brezillon 		.name	= "sh_flctl",
12213f26d1bfSMiquel Raynal 		.of_match_table = of_flctl_match,
122293db446aSBoris Brezillon 	},
122393db446aSBoris Brezillon };
122493db446aSBoris Brezillon 
122593db446aSBoris Brezillon module_platform_driver_probe(flctl_driver, flctl_probe);
122693db446aSBoris Brezillon 
1227cfd74017SKuninori Morimoto MODULE_LICENSE("GPL v2");
122893db446aSBoris Brezillon MODULE_AUTHOR("Yoshihiro Shimoda");
122993db446aSBoris Brezillon MODULE_DESCRIPTION("SuperH FLCTL driver");
123093db446aSBoris Brezillon MODULE_ALIAS("platform:sh_flctl");
1231