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