xref: /linux/drivers/mtd/nand/raw/hisi504_nand.c (revision 353b7a55dcaf5fb8758e09ebe2ddf5f3adbac7c5)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
254f531f6SZhou Wang /*
354f531f6SZhou Wang  * Hisilicon NAND Flash controller driver
454f531f6SZhou Wang  *
554f531f6SZhou Wang  * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd.
654f531f6SZhou Wang  *              http://www.hisilicon.com
754f531f6SZhou Wang  *
854f531f6SZhou Wang  * Author: Zhou Wang <wangzhou.bry@gmail.com>
954f531f6SZhou Wang  * The initial developer of the original code is Zhiyong Cai
1054f531f6SZhou Wang  * <caizhiyong@huawei.com>
1154f531f6SZhou Wang  */
1254f531f6SZhou Wang #include <linux/of.h>
1354f531f6SZhou Wang #include <linux/mtd/mtd.h>
1454f531f6SZhou Wang #include <linux/sizes.h>
1554f531f6SZhou Wang #include <linux/clk.h>
1654f531f6SZhou Wang #include <linux/slab.h>
1754f531f6SZhou Wang #include <linux/module.h>
1854f531f6SZhou Wang #include <linux/delay.h>
1954f531f6SZhou Wang #include <linux/interrupt.h>
20d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
2154f531f6SZhou Wang #include <linux/dma-mapping.h>
2254f531f6SZhou Wang #include <linux/platform_device.h>
2354f531f6SZhou Wang #include <linux/mtd/partitions.h>
2454f531f6SZhou Wang 
2554f531f6SZhou Wang #define HINFC504_MAX_CHIP                               (4)
2654f531f6SZhou Wang #define HINFC504_W_LATCH                                (5)
2754f531f6SZhou Wang #define HINFC504_R_LATCH                                (7)
2854f531f6SZhou Wang #define HINFC504_RW_LATCH                               (3)
2954f531f6SZhou Wang 
3054f531f6SZhou Wang #define HINFC504_NFC_TIMEOUT				(2 * HZ)
3154f531f6SZhou Wang #define HINFC504_NFC_PM_TIMEOUT				(1 * HZ)
3254f531f6SZhou Wang #define HINFC504_NFC_DMA_TIMEOUT			(5 * HZ)
3354f531f6SZhou Wang #define HINFC504_CHIP_DELAY				(25)
3454f531f6SZhou Wang 
3554f531f6SZhou Wang #define HINFC504_REG_BASE_ADDRESS_LEN			(0x100)
3654f531f6SZhou Wang #define HINFC504_BUFFER_BASE_ADDRESS_LEN		(2048 + 128)
3754f531f6SZhou Wang 
3854f531f6SZhou Wang #define HINFC504_ADDR_CYCLE_MASK			0x4
3954f531f6SZhou Wang 
4054f531f6SZhou Wang #define HINFC504_CON					0x00
4154f531f6SZhou Wang #define HINFC504_CON_OP_MODE_NORMAL			BIT(0)
4254f531f6SZhou Wang #define HINFC504_CON_PAGEISZE_SHIFT			(1)
4354f531f6SZhou Wang #define HINFC504_CON_PAGESIZE_MASK			(0x07)
4454f531f6SZhou Wang #define HINFC504_CON_BUS_WIDTH				BIT(4)
4554f531f6SZhou Wang #define HINFC504_CON_READY_BUSY_SEL			BIT(8)
4654f531f6SZhou Wang #define HINFC504_CON_ECCTYPE_SHIFT			(9)
4754f531f6SZhou Wang #define HINFC504_CON_ECCTYPE_MASK			(0x07)
4854f531f6SZhou Wang 
4954f531f6SZhou Wang #define HINFC504_PWIDTH					0x04
5054f531f6SZhou Wang #define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
5154f531f6SZhou Wang 	((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
5254f531f6SZhou Wang 
5354f531f6SZhou Wang #define HINFC504_CMD					0x0C
5454f531f6SZhou Wang #define HINFC504_ADDRL					0x10
5554f531f6SZhou Wang #define HINFC504_ADDRH					0x14
5654f531f6SZhou Wang #define HINFC504_DATA_NUM				0x18
5754f531f6SZhou Wang 
5854f531f6SZhou Wang #define HINFC504_OP					0x1C
5954f531f6SZhou Wang #define HINFC504_OP_READ_DATA_EN			BIT(1)
6054f531f6SZhou Wang #define HINFC504_OP_WAIT_READY_EN			BIT(2)
6154f531f6SZhou Wang #define HINFC504_OP_CMD2_EN				BIT(3)
6254f531f6SZhou Wang #define HINFC504_OP_WRITE_DATA_EN			BIT(4)
6354f531f6SZhou Wang #define HINFC504_OP_ADDR_EN				BIT(5)
6454f531f6SZhou Wang #define HINFC504_OP_CMD1_EN				BIT(6)
6554f531f6SZhou Wang #define HINFC504_OP_NF_CS_SHIFT                         (7)
6654f531f6SZhou Wang #define HINFC504_OP_NF_CS_MASK				(3)
6754f531f6SZhou Wang #define HINFC504_OP_ADDR_CYCLE_SHIFT			(9)
6854f531f6SZhou Wang #define HINFC504_OP_ADDR_CYCLE_MASK			(7)
6954f531f6SZhou Wang 
7054f531f6SZhou Wang #define HINFC504_STATUS                                 0x20
7154f531f6SZhou Wang #define HINFC504_READY					BIT(0)
7254f531f6SZhou Wang 
7354f531f6SZhou Wang #define HINFC504_INTEN					0x24
7454f531f6SZhou Wang #define HINFC504_INTEN_DMA				BIT(9)
7554f531f6SZhou Wang #define HINFC504_INTEN_UE				BIT(6)
7654f531f6SZhou Wang #define HINFC504_INTEN_CE				BIT(5)
7754f531f6SZhou Wang 
7854f531f6SZhou Wang #define HINFC504_INTS					0x28
7954f531f6SZhou Wang #define HINFC504_INTS_DMA				BIT(9)
8054f531f6SZhou Wang #define HINFC504_INTS_UE				BIT(6)
8154f531f6SZhou Wang #define HINFC504_INTS_CE				BIT(5)
8254f531f6SZhou Wang 
8354f531f6SZhou Wang #define HINFC504_INTCLR                                 0x2C
8454f531f6SZhou Wang #define HINFC504_INTCLR_DMA				BIT(9)
8554f531f6SZhou Wang #define HINFC504_INTCLR_UE				BIT(6)
8654f531f6SZhou Wang #define HINFC504_INTCLR_CE				BIT(5)
8754f531f6SZhou Wang 
8854f531f6SZhou Wang #define HINFC504_ECC_STATUS                             0x5C
8954f531f6SZhou Wang #define HINFC504_ECC_16_BIT_SHIFT                       12
9054f531f6SZhou Wang 
9154f531f6SZhou Wang #define HINFC504_DMA_CTRL				0x60
9254f531f6SZhou Wang #define HINFC504_DMA_CTRL_DMA_START			BIT(0)
9354f531f6SZhou Wang #define HINFC504_DMA_CTRL_WE				BIT(1)
9454f531f6SZhou Wang #define HINFC504_DMA_CTRL_DATA_AREA_EN			BIT(2)
9554f531f6SZhou Wang #define HINFC504_DMA_CTRL_OOB_AREA_EN			BIT(3)
9654f531f6SZhou Wang #define HINFC504_DMA_CTRL_BURST4_EN			BIT(4)
9754f531f6SZhou Wang #define HINFC504_DMA_CTRL_BURST8_EN			BIT(5)
9854f531f6SZhou Wang #define HINFC504_DMA_CTRL_BURST16_EN			BIT(6)
9954f531f6SZhou Wang #define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT		(7)
10054f531f6SZhou Wang #define HINFC504_DMA_CTRL_ADDR_NUM_MASK                 (1)
10154f531f6SZhou Wang #define HINFC504_DMA_CTRL_CS_SHIFT			(8)
10254f531f6SZhou Wang #define HINFC504_DMA_CTRL_CS_MASK			(0x03)
10354f531f6SZhou Wang 
10454f531f6SZhou Wang #define HINFC504_DMA_ADDR_DATA				0x64
10554f531f6SZhou Wang #define HINFC504_DMA_ADDR_OOB				0x68
10654f531f6SZhou Wang 
10754f531f6SZhou Wang #define HINFC504_DMA_LEN				0x6C
10854f531f6SZhou Wang #define HINFC504_DMA_LEN_OOB_SHIFT			(16)
10954f531f6SZhou Wang #define HINFC504_DMA_LEN_OOB_MASK			(0xFFF)
11054f531f6SZhou Wang 
11154f531f6SZhou Wang #define HINFC504_DMA_PARA				0x70
11254f531f6SZhou Wang #define HINFC504_DMA_PARA_DATA_RW_EN			BIT(0)
11354f531f6SZhou Wang #define HINFC504_DMA_PARA_OOB_RW_EN			BIT(1)
11454f531f6SZhou Wang #define HINFC504_DMA_PARA_DATA_EDC_EN			BIT(2)
11554f531f6SZhou Wang #define HINFC504_DMA_PARA_OOB_EDC_EN			BIT(3)
11654f531f6SZhou Wang #define HINFC504_DMA_PARA_DATA_ECC_EN			BIT(4)
11754f531f6SZhou Wang #define HINFC504_DMA_PARA_OOB_ECC_EN			BIT(5)
11854f531f6SZhou Wang 
11954f531f6SZhou Wang #define HINFC_VERSION                                   0x74
12054f531f6SZhou Wang #define HINFC504_LOG_READ_ADDR				0x7C
12154f531f6SZhou Wang #define HINFC504_LOG_READ_LEN				0x80
12254f531f6SZhou Wang 
12354f531f6SZhou Wang #define HINFC504_NANDINFO_LEN				0x10
12454f531f6SZhou Wang 
12554f531f6SZhou Wang struct hinfc_host {
12654f531f6SZhou Wang 	struct nand_chip	chip;
12754f531f6SZhou Wang 	struct device		*dev;
12854f531f6SZhou Wang 	void __iomem		*iobase;
12954f531f6SZhou Wang 	void __iomem		*mmio;
13054f531f6SZhou Wang 	struct completion       cmd_complete;
13154f531f6SZhou Wang 	unsigned int		offset;
13254f531f6SZhou Wang 	unsigned int		command;
13354f531f6SZhou Wang 	int			chipselect;
13454f531f6SZhou Wang 	unsigned int		addr_cycle;
13554f531f6SZhou Wang 	u32                     addr_value[2];
13654f531f6SZhou Wang 	u32                     cache_addr_value[2];
13754f531f6SZhou Wang 	char			*buffer;
13854f531f6SZhou Wang 	dma_addr_t		dma_buffer;
13954f531f6SZhou Wang 	dma_addr_t		dma_oob;
14054f531f6SZhou Wang 	int			version;
14154f531f6SZhou Wang 	unsigned int            irq_status; /* interrupt status */
14254f531f6SZhou Wang };
14354f531f6SZhou Wang 
14454f531f6SZhou Wang static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg)
14554f531f6SZhou Wang {
14654f531f6SZhou Wang 	return readl(host->iobase + reg);
14754f531f6SZhou Wang }
14854f531f6SZhou Wang 
14954f531f6SZhou Wang static inline void hinfc_write(struct hinfc_host *host, unsigned int value,
15054f531f6SZhou Wang 			       unsigned int reg)
15154f531f6SZhou Wang {
15254f531f6SZhou Wang 	writel(value, host->iobase + reg);
15354f531f6SZhou Wang }
15454f531f6SZhou Wang 
15554f531f6SZhou Wang static void wait_controller_finished(struct hinfc_host *host)
15654f531f6SZhou Wang {
15754f531f6SZhou Wang 	unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
15854f531f6SZhou Wang 	int val;
15954f531f6SZhou Wang 
16054f531f6SZhou Wang 	while (time_before(jiffies, timeout)) {
16154f531f6SZhou Wang 		val = hinfc_read(host, HINFC504_STATUS);
16254f531f6SZhou Wang 		if (host->command == NAND_CMD_ERASE2) {
16354f531f6SZhou Wang 			/* nfc is ready */
16454f531f6SZhou Wang 			while (!(val & HINFC504_READY))	{
16554f531f6SZhou Wang 				usleep_range(500, 1000);
16654f531f6SZhou Wang 				val = hinfc_read(host, HINFC504_STATUS);
16754f531f6SZhou Wang 			}
16854f531f6SZhou Wang 			return;
16954f531f6SZhou Wang 		}
17054f531f6SZhou Wang 
17154f531f6SZhou Wang 		if (val & HINFC504_READY)
17254f531f6SZhou Wang 			return;
17354f531f6SZhou Wang 	}
17454f531f6SZhou Wang 
17554f531f6SZhou Wang 	/* wait cmd timeout */
17654f531f6SZhou Wang 	dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n");
17754f531f6SZhou Wang }
17854f531f6SZhou Wang 
17954f531f6SZhou Wang static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
18054f531f6SZhou Wang {
181fa100163SBoris BREZILLON 	struct nand_chip *chip = &host->chip;
182fa100163SBoris BREZILLON 	struct mtd_info	*mtd = nand_to_mtd(chip);
18354f531f6SZhou Wang 	unsigned long val;
18454f531f6SZhou Wang 	int ret;
18554f531f6SZhou Wang 
18654f531f6SZhou Wang 	hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA);
18754f531f6SZhou Wang 	hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
18854f531f6SZhou Wang 
189bace41f8SMiquel Raynal 	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_NONE) {
19054f531f6SZhou Wang 		hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK)
19154f531f6SZhou Wang 			<< HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN);
19254f531f6SZhou Wang 
19354f531f6SZhou Wang 		hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
19454f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA);
19554f531f6SZhou Wang 	} else {
19654f531f6SZhou Wang 		if (host->command == NAND_CMD_READOOB)
19754f531f6SZhou Wang 			hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN
19854f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_EDC_EN
19954f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
20054f531f6SZhou Wang 		else
20154f531f6SZhou Wang 			hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
20254f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_RW_EN
20354f531f6SZhou Wang 			| HINFC504_DMA_PARA_DATA_EDC_EN
20454f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_EDC_EN
20554f531f6SZhou Wang 			| HINFC504_DMA_PARA_DATA_ECC_EN
20654f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
20754f531f6SZhou Wang 
20854f531f6SZhou Wang 	}
20954f531f6SZhou Wang 
21054f531f6SZhou Wang 	val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN
21154f531f6SZhou Wang 		| HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN
21254f531f6SZhou Wang 		| HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN
21354f531f6SZhou Wang 		| ((host->addr_cycle == 4 ? 1 : 0)
21454f531f6SZhou Wang 			<< HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
21554f531f6SZhou Wang 		| ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
21654f531f6SZhou Wang 			<< HINFC504_DMA_CTRL_CS_SHIFT));
21754f531f6SZhou Wang 
21854f531f6SZhou Wang 	if (todev)
21954f531f6SZhou Wang 		val |= HINFC504_DMA_CTRL_WE;
22054f531f6SZhou Wang 
22154f531f6SZhou Wang 	init_completion(&host->cmd_complete);
22254f531f6SZhou Wang 
22354f531f6SZhou Wang 	hinfc_write(host, val, HINFC504_DMA_CTRL);
22454f531f6SZhou Wang 	ret = wait_for_completion_timeout(&host->cmd_complete,
22554f531f6SZhou Wang 			HINFC504_NFC_DMA_TIMEOUT);
22654f531f6SZhou Wang 
22754f531f6SZhou Wang 	if (!ret) {
22854f531f6SZhou Wang 		dev_err(host->dev, "DMA operation(irq) timeout!\n");
22954f531f6SZhou Wang 		/* sanity check */
23054f531f6SZhou Wang 		val = hinfc_read(host, HINFC504_DMA_CTRL);
23154f531f6SZhou Wang 		if (!(val & HINFC504_DMA_CTRL_DMA_START))
23254f531f6SZhou Wang 			dev_err(host->dev, "DMA is already done but without irq ACK!\n");
23354f531f6SZhou Wang 		else
23454f531f6SZhou Wang 			dev_err(host->dev, "DMA is really timeout!\n");
23554f531f6SZhou Wang 	}
23654f531f6SZhou Wang }
23754f531f6SZhou Wang 
23854f531f6SZhou Wang static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
23954f531f6SZhou Wang {
24054f531f6SZhou Wang 	host->addr_value[0] &= 0xffff0000;
24154f531f6SZhou Wang 
24254f531f6SZhou Wang 	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
24354f531f6SZhou Wang 	hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
24454f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
24554f531f6SZhou Wang 		    HINFC504_CMD);
24654f531f6SZhou Wang 
24754f531f6SZhou Wang 	hisi_nfc_dma_transfer(host, 1);
24854f531f6SZhou Wang 
24954f531f6SZhou Wang 	return 0;
25054f531f6SZhou Wang }
25154f531f6SZhou Wang 
25254f531f6SZhou Wang static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
25354f531f6SZhou Wang {
254fa100163SBoris BREZILLON 	struct mtd_info	*mtd = nand_to_mtd(&host->chip);
25554f531f6SZhou Wang 
25654f531f6SZhou Wang 	if ((host->addr_value[0] == host->cache_addr_value[0]) &&
25754f531f6SZhou Wang 	    (host->addr_value[1] == host->cache_addr_value[1]))
25854f531f6SZhou Wang 		return 0;
25954f531f6SZhou Wang 
26054f531f6SZhou Wang 	host->addr_value[0] &= 0xffff0000;
26154f531f6SZhou Wang 
26254f531f6SZhou Wang 	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
26354f531f6SZhou Wang 	hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
26454f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
26554f531f6SZhou Wang 		    HINFC504_CMD);
26654f531f6SZhou Wang 
26754f531f6SZhou Wang 	hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
26854f531f6SZhou Wang 	hinfc_write(host, mtd->writesize + mtd->oobsize,
26954f531f6SZhou Wang 		    HINFC504_LOG_READ_LEN);
27054f531f6SZhou Wang 
27154f531f6SZhou Wang 	hisi_nfc_dma_transfer(host, 0);
27254f531f6SZhou Wang 
27354f531f6SZhou Wang 	host->cache_addr_value[0] = host->addr_value[0];
27454f531f6SZhou Wang 	host->cache_addr_value[1] = host->addr_value[1];
27554f531f6SZhou Wang 
27654f531f6SZhou Wang 	return 0;
27754f531f6SZhou Wang }
27854f531f6SZhou Wang 
27954f531f6SZhou Wang static int hisi_nfc_send_cmd_erase(struct hinfc_host *host)
28054f531f6SZhou Wang {
28154f531f6SZhou Wang 	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
28254f531f6SZhou Wang 	hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
28354f531f6SZhou Wang 		    HINFC504_CMD);
28454f531f6SZhou Wang 
28554f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_WAIT_READY_EN
28654f531f6SZhou Wang 		| HINFC504_OP_CMD2_EN
28754f531f6SZhou Wang 		| HINFC504_OP_CMD1_EN
28854f531f6SZhou Wang 		| HINFC504_OP_ADDR_EN
28954f531f6SZhou Wang 		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
29054f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT)
29154f531f6SZhou Wang 		| ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
29254f531f6SZhou Wang 			<< HINFC504_OP_ADDR_CYCLE_SHIFT),
29354f531f6SZhou Wang 		HINFC504_OP);
29454f531f6SZhou Wang 
29554f531f6SZhou Wang 	wait_controller_finished(host);
29654f531f6SZhou Wang 
29754f531f6SZhou Wang 	return 0;
29854f531f6SZhou Wang }
29954f531f6SZhou Wang 
30054f531f6SZhou Wang static int hisi_nfc_send_cmd_readid(struct hinfc_host *host)
30154f531f6SZhou Wang {
30254f531f6SZhou Wang 	hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
30354f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
30454f531f6SZhou Wang 	hinfc_write(host, 0, HINFC504_ADDRL);
30554f531f6SZhou Wang 
30654f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN
30754f531f6SZhou Wang 		| HINFC504_OP_READ_DATA_EN
30854f531f6SZhou Wang 		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
30954f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT)
31054f531f6SZhou Wang 		| 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP);
31154f531f6SZhou Wang 
31254f531f6SZhou Wang 	wait_controller_finished(host);
31354f531f6SZhou Wang 
31454f531f6SZhou Wang 	return 0;
31554f531f6SZhou Wang }
31654f531f6SZhou Wang 
31754f531f6SZhou Wang static int hisi_nfc_send_cmd_status(struct hinfc_host *host)
31854f531f6SZhou Wang {
31954f531f6SZhou Wang 	hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
32054f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
32154f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_CMD1_EN
32254f531f6SZhou Wang 		| HINFC504_OP_READ_DATA_EN
32354f531f6SZhou Wang 		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
32454f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT),
32554f531f6SZhou Wang 		HINFC504_OP);
32654f531f6SZhou Wang 
32754f531f6SZhou Wang 	wait_controller_finished(host);
32854f531f6SZhou Wang 
32954f531f6SZhou Wang 	return 0;
33054f531f6SZhou Wang }
33154f531f6SZhou Wang 
33254f531f6SZhou Wang static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
33354f531f6SZhou Wang {
33454f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
33554f531f6SZhou Wang 
33654f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_CMD1_EN
33754f531f6SZhou Wang 		| ((chipselect & HINFC504_OP_NF_CS_MASK)
33854f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT)
33954f531f6SZhou Wang 		| HINFC504_OP_WAIT_READY_EN,
34054f531f6SZhou Wang 		HINFC504_OP);
34154f531f6SZhou Wang 
34254f531f6SZhou Wang 	wait_controller_finished(host);
34354f531f6SZhou Wang 
34454f531f6SZhou Wang 	return 0;
34554f531f6SZhou Wang }
34654f531f6SZhou Wang 
347758b56f5SBoris Brezillon static void hisi_nfc_select_chip(struct nand_chip *chip, int chipselect)
34854f531f6SZhou Wang {
349d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
35054f531f6SZhou Wang 
35154f531f6SZhou Wang 	if (chipselect < 0)
35254f531f6SZhou Wang 		return;
35354f531f6SZhou Wang 
35454f531f6SZhou Wang 	host->chipselect = chipselect;
35554f531f6SZhou Wang }
35654f531f6SZhou Wang 
3577e534323SBoris Brezillon static uint8_t hisi_nfc_read_byte(struct nand_chip *chip)
35854f531f6SZhou Wang {
359d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
36054f531f6SZhou Wang 
36154f531f6SZhou Wang 	if (host->command == NAND_CMD_STATUS)
36254f531f6SZhou Wang 		return *(uint8_t *)(host->mmio);
36354f531f6SZhou Wang 
36454f531f6SZhou Wang 	host->offset++;
36554f531f6SZhou Wang 
36654f531f6SZhou Wang 	if (host->command == NAND_CMD_READID)
36754f531f6SZhou Wang 		return *(uint8_t *)(host->mmio + host->offset - 1);
36854f531f6SZhou Wang 
36954f531f6SZhou Wang 	return *(uint8_t *)(host->buffer + host->offset - 1);
37054f531f6SZhou Wang }
37154f531f6SZhou Wang 
37254f531f6SZhou Wang static void
373c0739d85SBoris Brezillon hisi_nfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
37454f531f6SZhou Wang {
375d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
37654f531f6SZhou Wang 
37754f531f6SZhou Wang 	memcpy(host->buffer + host->offset, buf, len);
37854f531f6SZhou Wang 	host->offset += len;
37954f531f6SZhou Wang }
38054f531f6SZhou Wang 
3817e534323SBoris Brezillon static void hisi_nfc_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
38254f531f6SZhou Wang {
383d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
38454f531f6SZhou Wang 
38554f531f6SZhou Wang 	memcpy(buf, host->buffer + host->offset, len);
38654f531f6SZhou Wang 	host->offset += len;
38754f531f6SZhou Wang }
38854f531f6SZhou Wang 
38954f531f6SZhou Wang static void set_addr(struct mtd_info *mtd, int column, int page_addr)
39054f531f6SZhou Wang {
3914bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
392d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
39354f531f6SZhou Wang 	unsigned int command = host->command;
39454f531f6SZhou Wang 
39554f531f6SZhou Wang 	host->addr_cycle    = 0;
39654f531f6SZhou Wang 	host->addr_value[0] = 0;
39754f531f6SZhou Wang 	host->addr_value[1] = 0;
39854f531f6SZhou Wang 
39954f531f6SZhou Wang 	/* Serially input address */
40054f531f6SZhou Wang 	if (column != -1) {
40154f531f6SZhou Wang 		/* Adjust columns for 16 bit buswidth */
40254f531f6SZhou Wang 		if (chip->options & NAND_BUSWIDTH_16 &&
40354f531f6SZhou Wang 				!nand_opcode_8bits(command))
40454f531f6SZhou Wang 			column >>= 1;
40554f531f6SZhou Wang 
40654f531f6SZhou Wang 		host->addr_value[0] = column & 0xffff;
40754f531f6SZhou Wang 		host->addr_cycle    = 2;
40854f531f6SZhou Wang 	}
40954f531f6SZhou Wang 	if (page_addr != -1) {
41054f531f6SZhou Wang 		host->addr_value[0] |= (page_addr & 0xffff)
41154f531f6SZhou Wang 			<< (host->addr_cycle * 8);
41254f531f6SZhou Wang 		host->addr_cycle    += 2;
41314157f86SMasahiro Yamada 		if (chip->options & NAND_ROW_ADDR_3) {
41454f531f6SZhou Wang 			host->addr_cycle += 1;
41554f531f6SZhou Wang 			if (host->command == NAND_CMD_ERASE1)
41654f531f6SZhou Wang 				host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16;
41754f531f6SZhou Wang 			else
41854f531f6SZhou Wang 				host->addr_value[1] |= ((page_addr >> 16) & 0xff);
41954f531f6SZhou Wang 		}
42054f531f6SZhou Wang 	}
42154f531f6SZhou Wang }
42254f531f6SZhou Wang 
4235295cf2eSBoris Brezillon static void hisi_nfc_cmdfunc(struct nand_chip *chip, unsigned command,
4245295cf2eSBoris Brezillon 			     int column, int page_addr)
42554f531f6SZhou Wang {
4265295cf2eSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
427d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
42854f531f6SZhou Wang 	int is_cache_invalid = 1;
42954f531f6SZhou Wang 	unsigned int flag = 0;
43054f531f6SZhou Wang 
43154f531f6SZhou Wang 	host->command =  command;
43254f531f6SZhou Wang 
43354f531f6SZhou Wang 	switch (command) {
43454f531f6SZhou Wang 	case NAND_CMD_READ0:
43554f531f6SZhou Wang 	case NAND_CMD_READOOB:
43654f531f6SZhou Wang 		if (command == NAND_CMD_READ0)
43754f531f6SZhou Wang 			host->offset = column;
43854f531f6SZhou Wang 		else
43954f531f6SZhou Wang 			host->offset = column + mtd->writesize;
44054f531f6SZhou Wang 
44154f531f6SZhou Wang 		is_cache_invalid = 0;
44254f531f6SZhou Wang 		set_addr(mtd, column, page_addr);
44354f531f6SZhou Wang 		hisi_nfc_send_cmd_readstart(host);
44454f531f6SZhou Wang 		break;
44554f531f6SZhou Wang 
44654f531f6SZhou Wang 	case NAND_CMD_SEQIN:
44754f531f6SZhou Wang 		host->offset = column;
44854f531f6SZhou Wang 		set_addr(mtd, column, page_addr);
44954f531f6SZhou Wang 		break;
45054f531f6SZhou Wang 
45154f531f6SZhou Wang 	case NAND_CMD_ERASE1:
45254f531f6SZhou Wang 		set_addr(mtd, column, page_addr);
45354f531f6SZhou Wang 		break;
45454f531f6SZhou Wang 
45554f531f6SZhou Wang 	case NAND_CMD_PAGEPROG:
45654f531f6SZhou Wang 		hisi_nfc_send_cmd_pageprog(host);
45754f531f6SZhou Wang 		break;
45854f531f6SZhou Wang 
45954f531f6SZhou Wang 	case NAND_CMD_ERASE2:
46054f531f6SZhou Wang 		hisi_nfc_send_cmd_erase(host);
46154f531f6SZhou Wang 		break;
46254f531f6SZhou Wang 
46354f531f6SZhou Wang 	case NAND_CMD_READID:
46454f531f6SZhou Wang 		host->offset = column;
46554f531f6SZhou Wang 		memset(host->mmio, 0, 0x10);
46654f531f6SZhou Wang 		hisi_nfc_send_cmd_readid(host);
46754f531f6SZhou Wang 		break;
46854f531f6SZhou Wang 
46954f531f6SZhou Wang 	case NAND_CMD_STATUS:
47054f531f6SZhou Wang 		flag = hinfc_read(host, HINFC504_CON);
471bace41f8SMiquel Raynal 		if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
47254f531f6SZhou Wang 			hinfc_write(host,
473dd58d38fSDan Carpenter 				    flag & ~(HINFC504_CON_ECCTYPE_MASK <<
47454f531f6SZhou Wang 				    HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON);
47554f531f6SZhou Wang 
47654f531f6SZhou Wang 		host->offset = 0;
47754f531f6SZhou Wang 		memset(host->mmio, 0, 0x10);
47854f531f6SZhou Wang 		hisi_nfc_send_cmd_status(host);
47954f531f6SZhou Wang 		hinfc_write(host, flag, HINFC504_CON);
48054f531f6SZhou Wang 		break;
48154f531f6SZhou Wang 
48254f531f6SZhou Wang 	case NAND_CMD_RESET:
48354f531f6SZhou Wang 		hisi_nfc_send_cmd_reset(host, host->chipselect);
48454f531f6SZhou Wang 		break;
48554f531f6SZhou Wang 
48654f531f6SZhou Wang 	default:
48754f531f6SZhou Wang 		dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
48854f531f6SZhou Wang 			command, column, page_addr);
48954f531f6SZhou Wang 	}
49054f531f6SZhou Wang 
49154f531f6SZhou Wang 	if (is_cache_invalid) {
49254f531f6SZhou Wang 		host->cache_addr_value[0] = ~0;
49354f531f6SZhou Wang 		host->cache_addr_value[1] = ~0;
49454f531f6SZhou Wang 	}
49554f531f6SZhou Wang }
49654f531f6SZhou Wang 
49754f531f6SZhou Wang static irqreturn_t hinfc_irq_handle(int irq, void *devid)
49854f531f6SZhou Wang {
49954f531f6SZhou Wang 	struct hinfc_host *host = devid;
50054f531f6SZhou Wang 	unsigned int flag;
50154f531f6SZhou Wang 
50254f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_INTS);
50354f531f6SZhou Wang 	/* store interrupts state */
50454f531f6SZhou Wang 	host->irq_status |= flag;
50554f531f6SZhou Wang 
50654f531f6SZhou Wang 	if (flag & HINFC504_INTS_DMA) {
50754f531f6SZhou Wang 		hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
50854f531f6SZhou Wang 		complete(&host->cmd_complete);
50954f531f6SZhou Wang 	} else if (flag & HINFC504_INTS_CE) {
51054f531f6SZhou Wang 		hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR);
51154f531f6SZhou Wang 	} else if (flag & HINFC504_INTS_UE) {
51254f531f6SZhou Wang 		hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR);
51354f531f6SZhou Wang 	}
51454f531f6SZhou Wang 
51554f531f6SZhou Wang 	return IRQ_HANDLED;
51654f531f6SZhou Wang }
51754f531f6SZhou Wang 
518b9761687SBoris Brezillon static int hisi_nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
519b9761687SBoris Brezillon 				     int oob_required, int page)
52054f531f6SZhou Wang {
521b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
522d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
52354f531f6SZhou Wang 	int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
52454f531f6SZhou Wang 	int stat_1, stat_2;
52554f531f6SZhou Wang 
52625f815f6SBoris Brezillon 	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
527716bbbabSBoris Brezillon 	chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
52854f531f6SZhou Wang 
52954f531f6SZhou Wang 	/* errors which can not be corrected by ECC */
53054f531f6SZhou Wang 	if (host->irq_status & HINFC504_INTS_UE) {
53154f531f6SZhou Wang 		mtd->ecc_stats.failed++;
53254f531f6SZhou Wang 	} else if (host->irq_status & HINFC504_INTS_CE) {
53354f531f6SZhou Wang 		/* TODO: need add other ECC modes! */
53454f531f6SZhou Wang 		switch (chip->ecc.strength) {
53554f531f6SZhou Wang 		case 16:
53654f531f6SZhou Wang 			status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >>
53754f531f6SZhou Wang 					HINFC504_ECC_16_BIT_SHIFT & 0x0fff;
53854f531f6SZhou Wang 			stat_2 = status_ecc & 0x3f;
53954f531f6SZhou Wang 			stat_1 = status_ecc >> 6 & 0x3f;
54054f531f6SZhou Wang 			stat = stat_1 + stat_2;
54154f531f6SZhou Wang 			stat_max = max_t(int, stat_1, stat_2);
54254f531f6SZhou Wang 		}
54354f531f6SZhou Wang 		mtd->ecc_stats.corrected += stat;
54454f531f6SZhou Wang 		max_bitflips = max_t(int, max_bitflips, stat_max);
54554f531f6SZhou Wang 	}
54654f531f6SZhou Wang 	host->irq_status = 0;
54754f531f6SZhou Wang 
54854f531f6SZhou Wang 	return max_bitflips;
54954f531f6SZhou Wang }
55054f531f6SZhou Wang 
551b9761687SBoris Brezillon static int hisi_nand_read_oob(struct nand_chip *chip, int page)
55254f531f6SZhou Wang {
553b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
554d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
55554f531f6SZhou Wang 
55697d90da8SBoris Brezillon 	nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
55754f531f6SZhou Wang 
55854f531f6SZhou Wang 	if (host->irq_status & HINFC504_INTS_UE) {
55954f531f6SZhou Wang 		host->irq_status = 0;
56054f531f6SZhou Wang 		return -EBADMSG;
56154f531f6SZhou Wang 	}
56254f531f6SZhou Wang 
56354f531f6SZhou Wang 	host->irq_status = 0;
56454f531f6SZhou Wang 	return 0;
56554f531f6SZhou Wang }
56654f531f6SZhou Wang 
567767eb6fbSBoris Brezillon static int hisi_nand_write_page_hwecc(struct nand_chip *chip,
568767eb6fbSBoris Brezillon 				      const uint8_t *buf, int oob_required,
56945aaeff9SBoris BREZILLON 				      int page)
57054f531f6SZhou Wang {
571767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
572767eb6fbSBoris Brezillon 
57325f815f6SBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
57454f531f6SZhou Wang 	if (oob_required)
575716bbbabSBoris Brezillon 		chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
57654f531f6SZhou Wang 
57725f815f6SBoris Brezillon 	return nand_prog_page_end_op(chip);
57854f531f6SZhou Wang }
57954f531f6SZhou Wang 
58054f531f6SZhou Wang static void hisi_nfc_host_init(struct hinfc_host *host)
58154f531f6SZhou Wang {
58254f531f6SZhou Wang 	struct nand_chip *chip = &host->chip;
58354f531f6SZhou Wang 	unsigned int flag = 0;
58454f531f6SZhou Wang 
58554f531f6SZhou Wang 	host->version = hinfc_read(host, HINFC_VERSION);
58654f531f6SZhou Wang 	host->addr_cycle		= 0;
58754f531f6SZhou Wang 	host->addr_value[0]		= 0;
58854f531f6SZhou Wang 	host->addr_value[1]		= 0;
58954f531f6SZhou Wang 	host->cache_addr_value[0]	= ~0;
59054f531f6SZhou Wang 	host->cache_addr_value[1]	= ~0;
59154f531f6SZhou Wang 	host->chipselect		= 0;
59254f531f6SZhou Wang 
59354f531f6SZhou Wang 	/* default page size: 2K, ecc_none. need modify */
59454f531f6SZhou Wang 	flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL
59554f531f6SZhou Wang 		| ((0x001 & HINFC504_CON_PAGESIZE_MASK)
59654f531f6SZhou Wang 			<< HINFC504_CON_PAGEISZE_SHIFT)
59754f531f6SZhou Wang 		| ((0x0 & HINFC504_CON_ECCTYPE_MASK)
59854f531f6SZhou Wang 			<< HINFC504_CON_ECCTYPE_SHIFT)
59954f531f6SZhou Wang 		| ((chip->options & NAND_BUSWIDTH_16) ?
60054f531f6SZhou Wang 			HINFC504_CON_BUS_WIDTH : 0);
60154f531f6SZhou Wang 	hinfc_write(host, flag, HINFC504_CON);
60254f531f6SZhou Wang 
60354f531f6SZhou Wang 	memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
60454f531f6SZhou Wang 
60554f531f6SZhou Wang 	hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
60654f531f6SZhou Wang 		    HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
60754f531f6SZhou Wang 
60854f531f6SZhou Wang 	/* enable DMA irq */
60954f531f6SZhou Wang 	hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
61054f531f6SZhou Wang }
61154f531f6SZhou Wang 
6122ca9ec9aSBoris Brezillon static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
6132ca9ec9aSBoris Brezillon 			      struct mtd_oob_region *oobregion)
6142ca9ec9aSBoris Brezillon {
6152ca9ec9aSBoris Brezillon 	/* FIXME: add ECC bytes position */
6162ca9ec9aSBoris Brezillon 	return -ENOTSUPP;
6172ca9ec9aSBoris Brezillon }
6182ca9ec9aSBoris Brezillon 
6192ca9ec9aSBoris Brezillon static int hisi_ooblayout_free(struct mtd_info *mtd, int section,
6202ca9ec9aSBoris Brezillon 			       struct mtd_oob_region *oobregion)
6212ca9ec9aSBoris Brezillon {
6222ca9ec9aSBoris Brezillon 	if (section)
6232ca9ec9aSBoris Brezillon 		return -ERANGE;
6242ca9ec9aSBoris Brezillon 
6252ca9ec9aSBoris Brezillon 	oobregion->offset = 2;
6262ca9ec9aSBoris Brezillon 	oobregion->length = 6;
6272ca9ec9aSBoris Brezillon 
6282ca9ec9aSBoris Brezillon 	return 0;
6292ca9ec9aSBoris Brezillon }
6302ca9ec9aSBoris Brezillon 
6312ca9ec9aSBoris Brezillon static const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
6322ca9ec9aSBoris Brezillon 	.ecc = hisi_ooblayout_ecc,
6332ca9ec9aSBoris Brezillon 	.free = hisi_ooblayout_free,
63454f531f6SZhou Wang };
63554f531f6SZhou Wang 
63654f531f6SZhou Wang static int hisi_nfc_ecc_probe(struct hinfc_host *host)
63754f531f6SZhou Wang {
63854f531f6SZhou Wang 	unsigned int flag;
63954f531f6SZhou Wang 	int size, strength, ecc_bits;
64054f531f6SZhou Wang 	struct device *dev = host->dev;
64154f531f6SZhou Wang 	struct nand_chip *chip = &host->chip;
642fa100163SBoris BREZILLON 	struct mtd_info *mtd = nand_to_mtd(chip);
64354f531f6SZhou Wang 
6442d01922cSBoris Brezillon 	size = chip->ecc.size;
6452d01922cSBoris Brezillon 	strength = chip->ecc.strength;
64654f531f6SZhou Wang 	if (size != 1024) {
64754f531f6SZhou Wang 		dev_err(dev, "error ecc size: %d\n", size);
64854f531f6SZhou Wang 		return -EINVAL;
64954f531f6SZhou Wang 	}
65054f531f6SZhou Wang 
65154f531f6SZhou Wang 	if ((size == 1024) && ((strength != 8) && (strength != 16) &&
65254f531f6SZhou Wang 				(strength != 24) && (strength != 40))) {
65354f531f6SZhou Wang 		dev_err(dev, "ecc size and strength do not match\n");
65454f531f6SZhou Wang 		return -EINVAL;
65554f531f6SZhou Wang 	}
65654f531f6SZhou Wang 
65754f531f6SZhou Wang 	chip->ecc.size = size;
65854f531f6SZhou Wang 	chip->ecc.strength = strength;
65954f531f6SZhou Wang 
66054f531f6SZhou Wang 	chip->ecc.read_page = hisi_nand_read_page_hwecc;
66154f531f6SZhou Wang 	chip->ecc.read_oob = hisi_nand_read_oob;
66254f531f6SZhou Wang 	chip->ecc.write_page = hisi_nand_write_page_hwecc;
66354f531f6SZhou Wang 
66454f531f6SZhou Wang 	switch (chip->ecc.strength) {
66554f531f6SZhou Wang 	case 16:
66654f531f6SZhou Wang 		ecc_bits = 6;
66754f531f6SZhou Wang 		if (mtd->writesize == 2048)
6682ca9ec9aSBoris Brezillon 			mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
66954f531f6SZhou Wang 
67054f531f6SZhou Wang 		/* TODO: add more page size support */
67154f531f6SZhou Wang 		break;
67254f531f6SZhou Wang 
67354f531f6SZhou Wang 	/* TODO: add more ecc strength support */
67454f531f6SZhou Wang 	default:
67554f531f6SZhou Wang 		dev_err(dev, "not support strength: %d\n", chip->ecc.strength);
67654f531f6SZhou Wang 		return -EINVAL;
67754f531f6SZhou Wang 	}
67854f531f6SZhou Wang 
67954f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_CON);
68054f531f6SZhou Wang 	/* add ecc type configure */
68154f531f6SZhou Wang 	flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK)
68254f531f6SZhou Wang 						<< HINFC504_CON_ECCTYPE_SHIFT);
68354f531f6SZhou Wang 	hinfc_write(host, flag, HINFC504_CON);
68454f531f6SZhou Wang 
68554f531f6SZhou Wang 	/* enable ecc irq */
68654f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_INTEN) & 0xfff;
68754f531f6SZhou Wang 	hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE,
68854f531f6SZhou Wang 		    HINFC504_INTEN);
68954f531f6SZhou Wang 
69054f531f6SZhou Wang 	return 0;
69154f531f6SZhou Wang }
69254f531f6SZhou Wang 
69307c1a4b3SMiquel Raynal static int hisi_nfc_attach_chip(struct nand_chip *chip)
69407c1a4b3SMiquel Raynal {
69507c1a4b3SMiquel Raynal 	struct mtd_info *mtd = nand_to_mtd(chip);
69607c1a4b3SMiquel Raynal 	struct hinfc_host *host = nand_get_controller_data(chip);
69707c1a4b3SMiquel Raynal 	int flag;
69807c1a4b3SMiquel Raynal 
69907c1a4b3SMiquel Raynal 	host->buffer = dmam_alloc_coherent(host->dev,
70007c1a4b3SMiquel Raynal 					   mtd->writesize + mtd->oobsize,
70107c1a4b3SMiquel Raynal 					   &host->dma_buffer, GFP_KERNEL);
70207c1a4b3SMiquel Raynal 	if (!host->buffer)
70307c1a4b3SMiquel Raynal 		return -ENOMEM;
70407c1a4b3SMiquel Raynal 
70507c1a4b3SMiquel Raynal 	host->dma_oob = host->dma_buffer + mtd->writesize;
70607c1a4b3SMiquel Raynal 	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
70707c1a4b3SMiquel Raynal 
70807c1a4b3SMiquel Raynal 	flag = hinfc_read(host, HINFC504_CON);
70907c1a4b3SMiquel Raynal 	flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
71007c1a4b3SMiquel Raynal 	switch (mtd->writesize) {
71107c1a4b3SMiquel Raynal 	case 2048:
71207c1a4b3SMiquel Raynal 		flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT);
71307c1a4b3SMiquel Raynal 		break;
71407c1a4b3SMiquel Raynal 	/*
71507c1a4b3SMiquel Raynal 	 * TODO: add more pagesize support,
71607c1a4b3SMiquel Raynal 	 * default pagesize has been set in hisi_nfc_host_init
71707c1a4b3SMiquel Raynal 	 */
71807c1a4b3SMiquel Raynal 	default:
71907c1a4b3SMiquel Raynal 		dev_err(host->dev, "NON-2KB page size nand flash\n");
72007c1a4b3SMiquel Raynal 		return -EINVAL;
72107c1a4b3SMiquel Raynal 	}
72207c1a4b3SMiquel Raynal 	hinfc_write(host, flag, HINFC504_CON);
72307c1a4b3SMiquel Raynal 
724bace41f8SMiquel Raynal 	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
72507c1a4b3SMiquel Raynal 		hisi_nfc_ecc_probe(host);
72607c1a4b3SMiquel Raynal 
72707c1a4b3SMiquel Raynal 	return 0;
72807c1a4b3SMiquel Raynal }
72907c1a4b3SMiquel Raynal 
73007c1a4b3SMiquel Raynal static const struct nand_controller_ops hisi_nfc_controller_ops = {
73107c1a4b3SMiquel Raynal 	.attach_chip = hisi_nfc_attach_chip,
73207c1a4b3SMiquel Raynal };
73307c1a4b3SMiquel Raynal 
73454f531f6SZhou Wang static int hisi_nfc_probe(struct platform_device *pdev)
73554f531f6SZhou Wang {
73607c1a4b3SMiquel Raynal 	int ret = 0, irq, max_chips = HINFC504_MAX_CHIP;
73754f531f6SZhou Wang 	struct device *dev = &pdev->dev;
73854f531f6SZhou Wang 	struct hinfc_host *host;
73954f531f6SZhou Wang 	struct nand_chip  *chip;
74054f531f6SZhou Wang 	struct mtd_info   *mtd;
74154f531f6SZhou Wang 	struct resource	  *res;
74254f531f6SZhou Wang 	struct device_node *np = dev->of_node;
74354f531f6SZhou Wang 
74454f531f6SZhou Wang 	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
74554f531f6SZhou Wang 	if (!host)
74654f531f6SZhou Wang 		return -ENOMEM;
74754f531f6SZhou Wang 	host->dev = dev;
74854f531f6SZhou Wang 
74954f531f6SZhou Wang 	platform_set_drvdata(pdev, host);
75054f531f6SZhou Wang 	chip = &host->chip;
751fa100163SBoris BREZILLON 	mtd  = nand_to_mtd(chip);
75254f531f6SZhou Wang 
75354f531f6SZhou Wang 	irq = platform_get_irq(pdev, 0);
754aab478caSStephen Boyd 	if (irq < 0)
7559326dc75SMiquel Raynal 		return -ENXIO;
75654f531f6SZhou Wang 
75754f531f6SZhou Wang 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
75854f531f6SZhou Wang 	host->iobase = devm_ioremap_resource(dev, res);
7599326dc75SMiquel Raynal 	if (IS_ERR(host->iobase))
7609326dc75SMiquel Raynal 		return PTR_ERR(host->iobase);
76154f531f6SZhou Wang 
76254f531f6SZhou Wang 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
76354f531f6SZhou Wang 	host->mmio = devm_ioremap_resource(dev, res);
764*03299d05SWei Li 	if (IS_ERR(host->mmio))
7659326dc75SMiquel Raynal 		return PTR_ERR(host->mmio);
76654f531f6SZhou Wang 
76754f531f6SZhou Wang 	mtd->name		= "hisi_nand";
76854f531f6SZhou Wang 	mtd->dev.parent         = &pdev->dev;
76954f531f6SZhou Wang 
770d699ed25SBoris BREZILLON 	nand_set_controller_data(chip, host);
771a61ae81aSBrian Norris 	nand_set_flash_node(chip, np);
772bf6065c6SBoris Brezillon 	chip->legacy.cmdfunc	= hisi_nfc_cmdfunc;
7737d6c37e9SBoris Brezillon 	chip->legacy.select_chip	= hisi_nfc_select_chip;
774716bbbabSBoris Brezillon 	chip->legacy.read_byte	= hisi_nfc_read_byte;
775716bbbabSBoris Brezillon 	chip->legacy.write_buf	= hisi_nfc_write_buf;
776716bbbabSBoris Brezillon 	chip->legacy.read_buf	= hisi_nfc_read_buf;
7773cece3abSBoris Brezillon 	chip->legacy.chip_delay	= HINFC504_CHIP_DELAY;
77845240367SBoris Brezillon 	chip->legacy.set_features	= nand_get_set_features_notsupp;
77945240367SBoris Brezillon 	chip->legacy.get_features	= nand_get_set_features_notsupp;
78054f531f6SZhou Wang 
78154f531f6SZhou Wang 	hisi_nfc_host_init(host);
78254f531f6SZhou Wang 
783d8bf368dSValentin Rothberg 	ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);
78454f531f6SZhou Wang 	if (ret) {
78554f531f6SZhou Wang 		dev_err(dev, "failed to request IRQ\n");
7869326dc75SMiquel Raynal 		return ret;
78754f531f6SZhou Wang 	}
78854f531f6SZhou Wang 
7897b6a9b28SBoris Brezillon 	chip->legacy.dummy_controller.ops = &hisi_nfc_controller_ops;
79000ad378fSBoris Brezillon 	ret = nand_scan(chip, max_chips);
791c8cae355SMasahiro Yamada 	if (ret)
7929326dc75SMiquel Raynal 		return ret;
79354f531f6SZhou Wang 
794a61ae81aSBrian Norris 	ret = mtd_device_register(mtd, NULL, 0);
79554f531f6SZhou Wang 	if (ret) {
79654f531f6SZhou Wang 		dev_err(dev, "Err MTD partition=%d\n", ret);
7976f533c46SMiquel Raynal 		nand_cleanup(chip);
7989326dc75SMiquel Raynal 		return ret;
79954f531f6SZhou Wang 	}
80054f531f6SZhou Wang 
80154f531f6SZhou Wang 	return 0;
80254f531f6SZhou Wang }
80354f531f6SZhou Wang 
80454f531f6SZhou Wang static int hisi_nfc_remove(struct platform_device *pdev)
80554f531f6SZhou Wang {
80654f531f6SZhou Wang 	struct hinfc_host *host = platform_get_drvdata(pdev);
80771a4917bSMiquel Raynal 	struct nand_chip *chip = &host->chip;
80871a4917bSMiquel Raynal 	int ret;
80954f531f6SZhou Wang 
81071a4917bSMiquel Raynal 	ret = mtd_device_unregister(nand_to_mtd(chip));
81171a4917bSMiquel Raynal 	WARN_ON(ret);
81271a4917bSMiquel Raynal 	nand_cleanup(chip);
81354f531f6SZhou Wang 
81454f531f6SZhou Wang 	return 0;
81554f531f6SZhou Wang }
81654f531f6SZhou Wang 
81754f531f6SZhou Wang #ifdef CONFIG_PM_SLEEP
81854f531f6SZhou Wang static int hisi_nfc_suspend(struct device *dev)
81954f531f6SZhou Wang {
82054f531f6SZhou Wang 	struct hinfc_host *host = dev_get_drvdata(dev);
82154f531f6SZhou Wang 	unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT;
82254f531f6SZhou Wang 
82354f531f6SZhou Wang 	while (time_before(jiffies, timeout)) {
82454f531f6SZhou Wang 		if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) &&
82554f531f6SZhou Wang 		    (hinfc_read(host, HINFC504_DMA_CTRL) &
82654f531f6SZhou Wang 		     HINFC504_DMA_CTRL_DMA_START)) {
82754f531f6SZhou Wang 			cond_resched();
82854f531f6SZhou Wang 			return 0;
82954f531f6SZhou Wang 		}
83054f531f6SZhou Wang 	}
83154f531f6SZhou Wang 
83254f531f6SZhou Wang 	dev_err(host->dev, "nand controller suspend timeout.\n");
83354f531f6SZhou Wang 
83454f531f6SZhou Wang 	return -EAGAIN;
83554f531f6SZhou Wang }
83654f531f6SZhou Wang 
83754f531f6SZhou Wang static int hisi_nfc_resume(struct device *dev)
83854f531f6SZhou Wang {
83954f531f6SZhou Wang 	int cs;
84054f531f6SZhou Wang 	struct hinfc_host *host = dev_get_drvdata(dev);
84154f531f6SZhou Wang 	struct nand_chip *chip = &host->chip;
84254f531f6SZhou Wang 
84332813e28SBoris Brezillon 	for (cs = 0; cs < nanddev_ntargets(&chip->base); cs++)
84454f531f6SZhou Wang 		hisi_nfc_send_cmd_reset(host, cs);
84554f531f6SZhou Wang 	hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
84654f531f6SZhou Wang 		    HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
84754f531f6SZhou Wang 
84854f531f6SZhou Wang 	return 0;
84954f531f6SZhou Wang }
85054f531f6SZhou Wang #endif
85154f531f6SZhou Wang static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume);
85254f531f6SZhou Wang 
85354f531f6SZhou Wang static const struct of_device_id nfc_id_table[] = {
85454f531f6SZhou Wang 	{ .compatible = "hisilicon,504-nfc" },
85554f531f6SZhou Wang 	{}
85654f531f6SZhou Wang };
85754f531f6SZhou Wang MODULE_DEVICE_TABLE(of, nfc_id_table);
85854f531f6SZhou Wang 
85954f531f6SZhou Wang static struct platform_driver hisi_nfc_driver = {
86054f531f6SZhou Wang 	.driver = {
86154f531f6SZhou Wang 		.name  = "hisi_nand",
86254f531f6SZhou Wang 		.of_match_table = nfc_id_table,
86354f531f6SZhou Wang 		.pm = &hisi_nfc_pm_ops,
86454f531f6SZhou Wang 	},
86554f531f6SZhou Wang 	.probe		= hisi_nfc_probe,
86654f531f6SZhou Wang 	.remove		= hisi_nfc_remove,
86754f531f6SZhou Wang };
86854f531f6SZhou Wang 
86954f531f6SZhou Wang module_platform_driver(hisi_nfc_driver);
87054f531f6SZhou Wang 
87154f531f6SZhou Wang MODULE_LICENSE("GPL");
87254f531f6SZhou Wang MODULE_AUTHOR("Zhou Wang");
87354f531f6SZhou Wang MODULE_AUTHOR("Zhiyong Cai");
87454f531f6SZhou Wang MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver");
875