xref: /linux/drivers/mtd/nand/raw/hisi504_nand.c (revision 9326dc754c64996ccdc354413674f42486590574)
154f531f6SZhou Wang /*
254f531f6SZhou Wang  * Hisilicon NAND Flash controller driver
354f531f6SZhou Wang  *
454f531f6SZhou Wang  * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd.
554f531f6SZhou Wang  *              http://www.hisilicon.com
654f531f6SZhou Wang  *
754f531f6SZhou Wang  * Author: Zhou Wang <wangzhou.bry@gmail.com>
854f531f6SZhou Wang  * The initial developer of the original code is Zhiyong Cai
954f531f6SZhou Wang  * <caizhiyong@huawei.com>
1054f531f6SZhou Wang  *
1154f531f6SZhou Wang  * This program is free software; you can redistribute it and/or modify
1254f531f6SZhou Wang  * it under the terms of the GNU General Public License as published by
1354f531f6SZhou Wang  * the Free Software Foundation; either version 2 of the License, or
1454f531f6SZhou Wang  * (at your option) any later version.
1554f531f6SZhou Wang  *
1654f531f6SZhou Wang  * This program is distributed in the hope that it will be useful,
1754f531f6SZhou Wang  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1854f531f6SZhou Wang  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1954f531f6SZhou Wang  * GNU General Public License for more details.
2054f531f6SZhou Wang  */
2154f531f6SZhou Wang #include <linux/of.h>
2254f531f6SZhou Wang #include <linux/mtd/mtd.h>
2354f531f6SZhou Wang #include <linux/sizes.h>
2454f531f6SZhou Wang #include <linux/clk.h>
2554f531f6SZhou Wang #include <linux/slab.h>
2654f531f6SZhou Wang #include <linux/module.h>
2754f531f6SZhou Wang #include <linux/delay.h>
2854f531f6SZhou Wang #include <linux/interrupt.h>
29d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
3054f531f6SZhou Wang #include <linux/dma-mapping.h>
3154f531f6SZhou Wang #include <linux/platform_device.h>
3254f531f6SZhou Wang #include <linux/mtd/partitions.h>
3354f531f6SZhou Wang 
3454f531f6SZhou Wang #define HINFC504_MAX_CHIP                               (4)
3554f531f6SZhou Wang #define HINFC504_W_LATCH                                (5)
3654f531f6SZhou Wang #define HINFC504_R_LATCH                                (7)
3754f531f6SZhou Wang #define HINFC504_RW_LATCH                               (3)
3854f531f6SZhou Wang 
3954f531f6SZhou Wang #define HINFC504_NFC_TIMEOUT				(2 * HZ)
4054f531f6SZhou Wang #define HINFC504_NFC_PM_TIMEOUT				(1 * HZ)
4154f531f6SZhou Wang #define HINFC504_NFC_DMA_TIMEOUT			(5 * HZ)
4254f531f6SZhou Wang #define HINFC504_CHIP_DELAY				(25)
4354f531f6SZhou Wang 
4454f531f6SZhou Wang #define HINFC504_REG_BASE_ADDRESS_LEN			(0x100)
4554f531f6SZhou Wang #define HINFC504_BUFFER_BASE_ADDRESS_LEN		(2048 + 128)
4654f531f6SZhou Wang 
4754f531f6SZhou Wang #define HINFC504_ADDR_CYCLE_MASK			0x4
4854f531f6SZhou Wang 
4954f531f6SZhou Wang #define HINFC504_CON					0x00
5054f531f6SZhou Wang #define HINFC504_CON_OP_MODE_NORMAL			BIT(0)
5154f531f6SZhou Wang #define HINFC504_CON_PAGEISZE_SHIFT			(1)
5254f531f6SZhou Wang #define HINFC504_CON_PAGESIZE_MASK			(0x07)
5354f531f6SZhou Wang #define HINFC504_CON_BUS_WIDTH				BIT(4)
5454f531f6SZhou Wang #define HINFC504_CON_READY_BUSY_SEL			BIT(8)
5554f531f6SZhou Wang #define HINFC504_CON_ECCTYPE_SHIFT			(9)
5654f531f6SZhou Wang #define HINFC504_CON_ECCTYPE_MASK			(0x07)
5754f531f6SZhou Wang 
5854f531f6SZhou Wang #define HINFC504_PWIDTH					0x04
5954f531f6SZhou Wang #define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
6054f531f6SZhou Wang 	((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
6154f531f6SZhou Wang 
6254f531f6SZhou Wang #define HINFC504_CMD					0x0C
6354f531f6SZhou Wang #define HINFC504_ADDRL					0x10
6454f531f6SZhou Wang #define HINFC504_ADDRH					0x14
6554f531f6SZhou Wang #define HINFC504_DATA_NUM				0x18
6654f531f6SZhou Wang 
6754f531f6SZhou Wang #define HINFC504_OP					0x1C
6854f531f6SZhou Wang #define HINFC504_OP_READ_DATA_EN			BIT(1)
6954f531f6SZhou Wang #define HINFC504_OP_WAIT_READY_EN			BIT(2)
7054f531f6SZhou Wang #define HINFC504_OP_CMD2_EN				BIT(3)
7154f531f6SZhou Wang #define HINFC504_OP_WRITE_DATA_EN			BIT(4)
7254f531f6SZhou Wang #define HINFC504_OP_ADDR_EN				BIT(5)
7354f531f6SZhou Wang #define HINFC504_OP_CMD1_EN				BIT(6)
7454f531f6SZhou Wang #define HINFC504_OP_NF_CS_SHIFT                         (7)
7554f531f6SZhou Wang #define HINFC504_OP_NF_CS_MASK				(3)
7654f531f6SZhou Wang #define HINFC504_OP_ADDR_CYCLE_SHIFT			(9)
7754f531f6SZhou Wang #define HINFC504_OP_ADDR_CYCLE_MASK			(7)
7854f531f6SZhou Wang 
7954f531f6SZhou Wang #define HINFC504_STATUS                                 0x20
8054f531f6SZhou Wang #define HINFC504_READY					BIT(0)
8154f531f6SZhou Wang 
8254f531f6SZhou Wang #define HINFC504_INTEN					0x24
8354f531f6SZhou Wang #define HINFC504_INTEN_DMA				BIT(9)
8454f531f6SZhou Wang #define HINFC504_INTEN_UE				BIT(6)
8554f531f6SZhou Wang #define HINFC504_INTEN_CE				BIT(5)
8654f531f6SZhou Wang 
8754f531f6SZhou Wang #define HINFC504_INTS					0x28
8854f531f6SZhou Wang #define HINFC504_INTS_DMA				BIT(9)
8954f531f6SZhou Wang #define HINFC504_INTS_UE				BIT(6)
9054f531f6SZhou Wang #define HINFC504_INTS_CE				BIT(5)
9154f531f6SZhou Wang 
9254f531f6SZhou Wang #define HINFC504_INTCLR                                 0x2C
9354f531f6SZhou Wang #define HINFC504_INTCLR_DMA				BIT(9)
9454f531f6SZhou Wang #define HINFC504_INTCLR_UE				BIT(6)
9554f531f6SZhou Wang #define HINFC504_INTCLR_CE				BIT(5)
9654f531f6SZhou Wang 
9754f531f6SZhou Wang #define HINFC504_ECC_STATUS                             0x5C
9854f531f6SZhou Wang #define HINFC504_ECC_16_BIT_SHIFT                       12
9954f531f6SZhou Wang 
10054f531f6SZhou Wang #define HINFC504_DMA_CTRL				0x60
10154f531f6SZhou Wang #define HINFC504_DMA_CTRL_DMA_START			BIT(0)
10254f531f6SZhou Wang #define HINFC504_DMA_CTRL_WE				BIT(1)
10354f531f6SZhou Wang #define HINFC504_DMA_CTRL_DATA_AREA_EN			BIT(2)
10454f531f6SZhou Wang #define HINFC504_DMA_CTRL_OOB_AREA_EN			BIT(3)
10554f531f6SZhou Wang #define HINFC504_DMA_CTRL_BURST4_EN			BIT(4)
10654f531f6SZhou Wang #define HINFC504_DMA_CTRL_BURST8_EN			BIT(5)
10754f531f6SZhou Wang #define HINFC504_DMA_CTRL_BURST16_EN			BIT(6)
10854f531f6SZhou Wang #define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT		(7)
10954f531f6SZhou Wang #define HINFC504_DMA_CTRL_ADDR_NUM_MASK                 (1)
11054f531f6SZhou Wang #define HINFC504_DMA_CTRL_CS_SHIFT			(8)
11154f531f6SZhou Wang #define HINFC504_DMA_CTRL_CS_MASK			(0x03)
11254f531f6SZhou Wang 
11354f531f6SZhou Wang #define HINFC504_DMA_ADDR_DATA				0x64
11454f531f6SZhou Wang #define HINFC504_DMA_ADDR_OOB				0x68
11554f531f6SZhou Wang 
11654f531f6SZhou Wang #define HINFC504_DMA_LEN				0x6C
11754f531f6SZhou Wang #define HINFC504_DMA_LEN_OOB_SHIFT			(16)
11854f531f6SZhou Wang #define HINFC504_DMA_LEN_OOB_MASK			(0xFFF)
11954f531f6SZhou Wang 
12054f531f6SZhou Wang #define HINFC504_DMA_PARA				0x70
12154f531f6SZhou Wang #define HINFC504_DMA_PARA_DATA_RW_EN			BIT(0)
12254f531f6SZhou Wang #define HINFC504_DMA_PARA_OOB_RW_EN			BIT(1)
12354f531f6SZhou Wang #define HINFC504_DMA_PARA_DATA_EDC_EN			BIT(2)
12454f531f6SZhou Wang #define HINFC504_DMA_PARA_OOB_EDC_EN			BIT(3)
12554f531f6SZhou Wang #define HINFC504_DMA_PARA_DATA_ECC_EN			BIT(4)
12654f531f6SZhou Wang #define HINFC504_DMA_PARA_OOB_ECC_EN			BIT(5)
12754f531f6SZhou Wang 
12854f531f6SZhou Wang #define HINFC_VERSION                                   0x74
12954f531f6SZhou Wang #define HINFC504_LOG_READ_ADDR				0x7C
13054f531f6SZhou Wang #define HINFC504_LOG_READ_LEN				0x80
13154f531f6SZhou Wang 
13254f531f6SZhou Wang #define HINFC504_NANDINFO_LEN				0x10
13354f531f6SZhou Wang 
13454f531f6SZhou Wang struct hinfc_host {
13554f531f6SZhou Wang 	struct nand_chip	chip;
13654f531f6SZhou Wang 	struct device		*dev;
13754f531f6SZhou Wang 	void __iomem		*iobase;
13854f531f6SZhou Wang 	void __iomem		*mmio;
13954f531f6SZhou Wang 	struct completion       cmd_complete;
14054f531f6SZhou Wang 	unsigned int		offset;
14154f531f6SZhou Wang 	unsigned int		command;
14254f531f6SZhou Wang 	int			chipselect;
14354f531f6SZhou Wang 	unsigned int		addr_cycle;
14454f531f6SZhou Wang 	u32                     addr_value[2];
14554f531f6SZhou Wang 	u32                     cache_addr_value[2];
14654f531f6SZhou Wang 	char			*buffer;
14754f531f6SZhou Wang 	dma_addr_t		dma_buffer;
14854f531f6SZhou Wang 	dma_addr_t		dma_oob;
14954f531f6SZhou Wang 	int			version;
15054f531f6SZhou Wang 	unsigned int            irq_status; /* interrupt status */
15154f531f6SZhou Wang };
15254f531f6SZhou Wang 
15354f531f6SZhou Wang static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg)
15454f531f6SZhou Wang {
15554f531f6SZhou Wang 	return readl(host->iobase + reg);
15654f531f6SZhou Wang }
15754f531f6SZhou Wang 
15854f531f6SZhou Wang static inline void hinfc_write(struct hinfc_host *host, unsigned int value,
15954f531f6SZhou Wang 			       unsigned int reg)
16054f531f6SZhou Wang {
16154f531f6SZhou Wang 	writel(value, host->iobase + reg);
16254f531f6SZhou Wang }
16354f531f6SZhou Wang 
16454f531f6SZhou Wang static void wait_controller_finished(struct hinfc_host *host)
16554f531f6SZhou Wang {
16654f531f6SZhou Wang 	unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
16754f531f6SZhou Wang 	int val;
16854f531f6SZhou Wang 
16954f531f6SZhou Wang 	while (time_before(jiffies, timeout)) {
17054f531f6SZhou Wang 		val = hinfc_read(host, HINFC504_STATUS);
17154f531f6SZhou Wang 		if (host->command == NAND_CMD_ERASE2) {
17254f531f6SZhou Wang 			/* nfc is ready */
17354f531f6SZhou Wang 			while (!(val & HINFC504_READY))	{
17454f531f6SZhou Wang 				usleep_range(500, 1000);
17554f531f6SZhou Wang 				val = hinfc_read(host, HINFC504_STATUS);
17654f531f6SZhou Wang 			}
17754f531f6SZhou Wang 			return;
17854f531f6SZhou Wang 		}
17954f531f6SZhou Wang 
18054f531f6SZhou Wang 		if (val & HINFC504_READY)
18154f531f6SZhou Wang 			return;
18254f531f6SZhou Wang 	}
18354f531f6SZhou Wang 
18454f531f6SZhou Wang 	/* wait cmd timeout */
18554f531f6SZhou Wang 	dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n");
18654f531f6SZhou Wang }
18754f531f6SZhou Wang 
18854f531f6SZhou Wang static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
18954f531f6SZhou Wang {
190fa100163SBoris BREZILLON 	struct nand_chip *chip = &host->chip;
191fa100163SBoris BREZILLON 	struct mtd_info	*mtd = nand_to_mtd(chip);
19254f531f6SZhou Wang 	unsigned long val;
19354f531f6SZhou Wang 	int ret;
19454f531f6SZhou Wang 
19554f531f6SZhou Wang 	hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA);
19654f531f6SZhou Wang 	hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
19754f531f6SZhou Wang 
19854f531f6SZhou Wang 	if (chip->ecc.mode == NAND_ECC_NONE) {
19954f531f6SZhou Wang 		hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK)
20054f531f6SZhou Wang 			<< HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN);
20154f531f6SZhou Wang 
20254f531f6SZhou Wang 		hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
20354f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA);
20454f531f6SZhou Wang 	} else {
20554f531f6SZhou Wang 		if (host->command == NAND_CMD_READOOB)
20654f531f6SZhou Wang 			hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN
20754f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_EDC_EN
20854f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
20954f531f6SZhou Wang 		else
21054f531f6SZhou Wang 			hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
21154f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_RW_EN
21254f531f6SZhou Wang 			| HINFC504_DMA_PARA_DATA_EDC_EN
21354f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_EDC_EN
21454f531f6SZhou Wang 			| HINFC504_DMA_PARA_DATA_ECC_EN
21554f531f6SZhou Wang 			| HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
21654f531f6SZhou Wang 
21754f531f6SZhou Wang 	}
21854f531f6SZhou Wang 
21954f531f6SZhou Wang 	val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN
22054f531f6SZhou Wang 		| HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN
22154f531f6SZhou Wang 		| HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN
22254f531f6SZhou Wang 		| ((host->addr_cycle == 4 ? 1 : 0)
22354f531f6SZhou Wang 			<< HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
22454f531f6SZhou Wang 		| ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
22554f531f6SZhou Wang 			<< HINFC504_DMA_CTRL_CS_SHIFT));
22654f531f6SZhou Wang 
22754f531f6SZhou Wang 	if (todev)
22854f531f6SZhou Wang 		val |= HINFC504_DMA_CTRL_WE;
22954f531f6SZhou Wang 
23054f531f6SZhou Wang 	init_completion(&host->cmd_complete);
23154f531f6SZhou Wang 
23254f531f6SZhou Wang 	hinfc_write(host, val, HINFC504_DMA_CTRL);
23354f531f6SZhou Wang 	ret = wait_for_completion_timeout(&host->cmd_complete,
23454f531f6SZhou Wang 			HINFC504_NFC_DMA_TIMEOUT);
23554f531f6SZhou Wang 
23654f531f6SZhou Wang 	if (!ret) {
23754f531f6SZhou Wang 		dev_err(host->dev, "DMA operation(irq) timeout!\n");
23854f531f6SZhou Wang 		/* sanity check */
23954f531f6SZhou Wang 		val = hinfc_read(host, HINFC504_DMA_CTRL);
24054f531f6SZhou Wang 		if (!(val & HINFC504_DMA_CTRL_DMA_START))
24154f531f6SZhou Wang 			dev_err(host->dev, "DMA is already done but without irq ACK!\n");
24254f531f6SZhou Wang 		else
24354f531f6SZhou Wang 			dev_err(host->dev, "DMA is really timeout!\n");
24454f531f6SZhou Wang 	}
24554f531f6SZhou Wang }
24654f531f6SZhou Wang 
24754f531f6SZhou Wang static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
24854f531f6SZhou Wang {
24954f531f6SZhou Wang 	host->addr_value[0] &= 0xffff0000;
25054f531f6SZhou Wang 
25154f531f6SZhou Wang 	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
25254f531f6SZhou Wang 	hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
25354f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
25454f531f6SZhou Wang 		    HINFC504_CMD);
25554f531f6SZhou Wang 
25654f531f6SZhou Wang 	hisi_nfc_dma_transfer(host, 1);
25754f531f6SZhou Wang 
25854f531f6SZhou Wang 	return 0;
25954f531f6SZhou Wang }
26054f531f6SZhou Wang 
26154f531f6SZhou Wang static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
26254f531f6SZhou Wang {
263fa100163SBoris BREZILLON 	struct mtd_info	*mtd = nand_to_mtd(&host->chip);
26454f531f6SZhou Wang 
26554f531f6SZhou Wang 	if ((host->addr_value[0] == host->cache_addr_value[0]) &&
26654f531f6SZhou Wang 	    (host->addr_value[1] == host->cache_addr_value[1]))
26754f531f6SZhou Wang 		return 0;
26854f531f6SZhou Wang 
26954f531f6SZhou Wang 	host->addr_value[0] &= 0xffff0000;
27054f531f6SZhou Wang 
27154f531f6SZhou Wang 	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
27254f531f6SZhou Wang 	hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
27354f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
27454f531f6SZhou Wang 		    HINFC504_CMD);
27554f531f6SZhou Wang 
27654f531f6SZhou Wang 	hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
27754f531f6SZhou Wang 	hinfc_write(host, mtd->writesize + mtd->oobsize,
27854f531f6SZhou Wang 		    HINFC504_LOG_READ_LEN);
27954f531f6SZhou Wang 
28054f531f6SZhou Wang 	hisi_nfc_dma_transfer(host, 0);
28154f531f6SZhou Wang 
28254f531f6SZhou Wang 	host->cache_addr_value[0] = host->addr_value[0];
28354f531f6SZhou Wang 	host->cache_addr_value[1] = host->addr_value[1];
28454f531f6SZhou Wang 
28554f531f6SZhou Wang 	return 0;
28654f531f6SZhou Wang }
28754f531f6SZhou Wang 
28854f531f6SZhou Wang static int hisi_nfc_send_cmd_erase(struct hinfc_host *host)
28954f531f6SZhou Wang {
29054f531f6SZhou Wang 	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
29154f531f6SZhou Wang 	hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
29254f531f6SZhou Wang 		    HINFC504_CMD);
29354f531f6SZhou Wang 
29454f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_WAIT_READY_EN
29554f531f6SZhou Wang 		| HINFC504_OP_CMD2_EN
29654f531f6SZhou Wang 		| HINFC504_OP_CMD1_EN
29754f531f6SZhou Wang 		| HINFC504_OP_ADDR_EN
29854f531f6SZhou Wang 		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
29954f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT)
30054f531f6SZhou Wang 		| ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
30154f531f6SZhou Wang 			<< HINFC504_OP_ADDR_CYCLE_SHIFT),
30254f531f6SZhou Wang 		HINFC504_OP);
30354f531f6SZhou Wang 
30454f531f6SZhou Wang 	wait_controller_finished(host);
30554f531f6SZhou Wang 
30654f531f6SZhou Wang 	return 0;
30754f531f6SZhou Wang }
30854f531f6SZhou Wang 
30954f531f6SZhou Wang static int hisi_nfc_send_cmd_readid(struct hinfc_host *host)
31054f531f6SZhou Wang {
31154f531f6SZhou Wang 	hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
31254f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
31354f531f6SZhou Wang 	hinfc_write(host, 0, HINFC504_ADDRL);
31454f531f6SZhou Wang 
31554f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN
31654f531f6SZhou Wang 		| HINFC504_OP_READ_DATA_EN
31754f531f6SZhou Wang 		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
31854f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT)
31954f531f6SZhou Wang 		| 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP);
32054f531f6SZhou Wang 
32154f531f6SZhou Wang 	wait_controller_finished(host);
32254f531f6SZhou Wang 
32354f531f6SZhou Wang 	return 0;
32454f531f6SZhou Wang }
32554f531f6SZhou Wang 
32654f531f6SZhou Wang static int hisi_nfc_send_cmd_status(struct hinfc_host *host)
32754f531f6SZhou Wang {
32854f531f6SZhou Wang 	hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
32954f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
33054f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_CMD1_EN
33154f531f6SZhou Wang 		| HINFC504_OP_READ_DATA_EN
33254f531f6SZhou Wang 		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
33354f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT),
33454f531f6SZhou Wang 		HINFC504_OP);
33554f531f6SZhou Wang 
33654f531f6SZhou Wang 	wait_controller_finished(host);
33754f531f6SZhou Wang 
33854f531f6SZhou Wang 	return 0;
33954f531f6SZhou Wang }
34054f531f6SZhou Wang 
34154f531f6SZhou Wang static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
34254f531f6SZhou Wang {
34354f531f6SZhou Wang 	hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
34454f531f6SZhou Wang 
34554f531f6SZhou Wang 	hinfc_write(host, HINFC504_OP_CMD1_EN
34654f531f6SZhou Wang 		| ((chipselect & HINFC504_OP_NF_CS_MASK)
34754f531f6SZhou Wang 			<< HINFC504_OP_NF_CS_SHIFT)
34854f531f6SZhou Wang 		| HINFC504_OP_WAIT_READY_EN,
34954f531f6SZhou Wang 		HINFC504_OP);
35054f531f6SZhou Wang 
35154f531f6SZhou Wang 	wait_controller_finished(host);
35254f531f6SZhou Wang 
35354f531f6SZhou Wang 	return 0;
35454f531f6SZhou Wang }
35554f531f6SZhou Wang 
35654f531f6SZhou Wang static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect)
35754f531f6SZhou Wang {
3584bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
359d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
36054f531f6SZhou Wang 
36154f531f6SZhou Wang 	if (chipselect < 0)
36254f531f6SZhou Wang 		return;
36354f531f6SZhou Wang 
36454f531f6SZhou Wang 	host->chipselect = chipselect;
36554f531f6SZhou Wang }
36654f531f6SZhou Wang 
36754f531f6SZhou Wang static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd)
36854f531f6SZhou Wang {
3694bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
370d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
37154f531f6SZhou Wang 
37254f531f6SZhou Wang 	if (host->command == NAND_CMD_STATUS)
37354f531f6SZhou Wang 		return *(uint8_t *)(host->mmio);
37454f531f6SZhou Wang 
37554f531f6SZhou Wang 	host->offset++;
37654f531f6SZhou Wang 
37754f531f6SZhou Wang 	if (host->command == NAND_CMD_READID)
37854f531f6SZhou Wang 		return *(uint8_t *)(host->mmio + host->offset - 1);
37954f531f6SZhou Wang 
38054f531f6SZhou Wang 	return *(uint8_t *)(host->buffer + host->offset - 1);
38154f531f6SZhou Wang }
38254f531f6SZhou Wang 
38354f531f6SZhou Wang static u16 hisi_nfc_read_word(struct mtd_info *mtd)
38454f531f6SZhou Wang {
3854bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
386d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
38754f531f6SZhou Wang 
38854f531f6SZhou Wang 	host->offset += 2;
38954f531f6SZhou Wang 	return *(u16 *)(host->buffer + host->offset - 2);
39054f531f6SZhou Wang }
39154f531f6SZhou Wang 
39254f531f6SZhou Wang static void
39354f531f6SZhou Wang hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
39454f531f6SZhou Wang {
3954bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
396d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
39754f531f6SZhou Wang 
39854f531f6SZhou Wang 	memcpy(host->buffer + host->offset, buf, len);
39954f531f6SZhou Wang 	host->offset += len;
40054f531f6SZhou Wang }
40154f531f6SZhou Wang 
40254f531f6SZhou Wang static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
40354f531f6SZhou Wang {
4044bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
405d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
40654f531f6SZhou Wang 
40754f531f6SZhou Wang 	memcpy(buf, host->buffer + host->offset, len);
40854f531f6SZhou Wang 	host->offset += len;
40954f531f6SZhou Wang }
41054f531f6SZhou Wang 
41154f531f6SZhou Wang static void set_addr(struct mtd_info *mtd, int column, int page_addr)
41254f531f6SZhou Wang {
4134bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
414d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
41554f531f6SZhou Wang 	unsigned int command = host->command;
41654f531f6SZhou Wang 
41754f531f6SZhou Wang 	host->addr_cycle    = 0;
41854f531f6SZhou Wang 	host->addr_value[0] = 0;
41954f531f6SZhou Wang 	host->addr_value[1] = 0;
42054f531f6SZhou Wang 
42154f531f6SZhou Wang 	/* Serially input address */
42254f531f6SZhou Wang 	if (column != -1) {
42354f531f6SZhou Wang 		/* Adjust columns for 16 bit buswidth */
42454f531f6SZhou Wang 		if (chip->options & NAND_BUSWIDTH_16 &&
42554f531f6SZhou Wang 				!nand_opcode_8bits(command))
42654f531f6SZhou Wang 			column >>= 1;
42754f531f6SZhou Wang 
42854f531f6SZhou Wang 		host->addr_value[0] = column & 0xffff;
42954f531f6SZhou Wang 		host->addr_cycle    = 2;
43054f531f6SZhou Wang 	}
43154f531f6SZhou Wang 	if (page_addr != -1) {
43254f531f6SZhou Wang 		host->addr_value[0] |= (page_addr & 0xffff)
43354f531f6SZhou Wang 			<< (host->addr_cycle * 8);
43454f531f6SZhou Wang 		host->addr_cycle    += 2;
43514157f86SMasahiro Yamada 		if (chip->options & NAND_ROW_ADDR_3) {
43654f531f6SZhou Wang 			host->addr_cycle += 1;
43754f531f6SZhou Wang 			if (host->command == NAND_CMD_ERASE1)
43854f531f6SZhou Wang 				host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16;
43954f531f6SZhou Wang 			else
44054f531f6SZhou Wang 				host->addr_value[1] |= ((page_addr >> 16) & 0xff);
44154f531f6SZhou Wang 		}
44254f531f6SZhou Wang 	}
44354f531f6SZhou Wang }
44454f531f6SZhou Wang 
44554f531f6SZhou Wang static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
44654f531f6SZhou Wang 		int page_addr)
44754f531f6SZhou Wang {
4484bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
449d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
45054f531f6SZhou Wang 	int is_cache_invalid = 1;
45154f531f6SZhou Wang 	unsigned int flag = 0;
45254f531f6SZhou Wang 
45354f531f6SZhou Wang 	host->command =  command;
45454f531f6SZhou Wang 
45554f531f6SZhou Wang 	switch (command) {
45654f531f6SZhou Wang 	case NAND_CMD_READ0:
45754f531f6SZhou Wang 	case NAND_CMD_READOOB:
45854f531f6SZhou Wang 		if (command == NAND_CMD_READ0)
45954f531f6SZhou Wang 			host->offset = column;
46054f531f6SZhou Wang 		else
46154f531f6SZhou Wang 			host->offset = column + mtd->writesize;
46254f531f6SZhou Wang 
46354f531f6SZhou Wang 		is_cache_invalid = 0;
46454f531f6SZhou Wang 		set_addr(mtd, column, page_addr);
46554f531f6SZhou Wang 		hisi_nfc_send_cmd_readstart(host);
46654f531f6SZhou Wang 		break;
46754f531f6SZhou Wang 
46854f531f6SZhou Wang 	case NAND_CMD_SEQIN:
46954f531f6SZhou Wang 		host->offset = column;
47054f531f6SZhou Wang 		set_addr(mtd, column, page_addr);
47154f531f6SZhou Wang 		break;
47254f531f6SZhou Wang 
47354f531f6SZhou Wang 	case NAND_CMD_ERASE1:
47454f531f6SZhou Wang 		set_addr(mtd, column, page_addr);
47554f531f6SZhou Wang 		break;
47654f531f6SZhou Wang 
47754f531f6SZhou Wang 	case NAND_CMD_PAGEPROG:
47854f531f6SZhou Wang 		hisi_nfc_send_cmd_pageprog(host);
47954f531f6SZhou Wang 		break;
48054f531f6SZhou Wang 
48154f531f6SZhou Wang 	case NAND_CMD_ERASE2:
48254f531f6SZhou Wang 		hisi_nfc_send_cmd_erase(host);
48354f531f6SZhou Wang 		break;
48454f531f6SZhou Wang 
48554f531f6SZhou Wang 	case NAND_CMD_READID:
48654f531f6SZhou Wang 		host->offset = column;
48754f531f6SZhou Wang 		memset(host->mmio, 0, 0x10);
48854f531f6SZhou Wang 		hisi_nfc_send_cmd_readid(host);
48954f531f6SZhou Wang 		break;
49054f531f6SZhou Wang 
49154f531f6SZhou Wang 	case NAND_CMD_STATUS:
49254f531f6SZhou Wang 		flag = hinfc_read(host, HINFC504_CON);
49354f531f6SZhou Wang 		if (chip->ecc.mode == NAND_ECC_HW)
49454f531f6SZhou Wang 			hinfc_write(host,
495dd58d38fSDan Carpenter 				    flag & ~(HINFC504_CON_ECCTYPE_MASK <<
49654f531f6SZhou Wang 				    HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON);
49754f531f6SZhou Wang 
49854f531f6SZhou Wang 		host->offset = 0;
49954f531f6SZhou Wang 		memset(host->mmio, 0, 0x10);
50054f531f6SZhou Wang 		hisi_nfc_send_cmd_status(host);
50154f531f6SZhou Wang 		hinfc_write(host, flag, HINFC504_CON);
50254f531f6SZhou Wang 		break;
50354f531f6SZhou Wang 
50454f531f6SZhou Wang 	case NAND_CMD_RESET:
50554f531f6SZhou Wang 		hisi_nfc_send_cmd_reset(host, host->chipselect);
50654f531f6SZhou Wang 		break;
50754f531f6SZhou Wang 
50854f531f6SZhou Wang 	default:
50954f531f6SZhou Wang 		dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
51054f531f6SZhou Wang 			command, column, page_addr);
51154f531f6SZhou Wang 	}
51254f531f6SZhou Wang 
51354f531f6SZhou Wang 	if (is_cache_invalid) {
51454f531f6SZhou Wang 		host->cache_addr_value[0] = ~0;
51554f531f6SZhou Wang 		host->cache_addr_value[1] = ~0;
51654f531f6SZhou Wang 	}
51754f531f6SZhou Wang }
51854f531f6SZhou Wang 
51954f531f6SZhou Wang static irqreturn_t hinfc_irq_handle(int irq, void *devid)
52054f531f6SZhou Wang {
52154f531f6SZhou Wang 	struct hinfc_host *host = devid;
52254f531f6SZhou Wang 	unsigned int flag;
52354f531f6SZhou Wang 
52454f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_INTS);
52554f531f6SZhou Wang 	/* store interrupts state */
52654f531f6SZhou Wang 	host->irq_status |= flag;
52754f531f6SZhou Wang 
52854f531f6SZhou Wang 	if (flag & HINFC504_INTS_DMA) {
52954f531f6SZhou Wang 		hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
53054f531f6SZhou Wang 		complete(&host->cmd_complete);
53154f531f6SZhou Wang 	} else if (flag & HINFC504_INTS_CE) {
53254f531f6SZhou Wang 		hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR);
53354f531f6SZhou Wang 	} else if (flag & HINFC504_INTS_UE) {
53454f531f6SZhou Wang 		hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR);
53554f531f6SZhou Wang 	}
53654f531f6SZhou Wang 
53754f531f6SZhou Wang 	return IRQ_HANDLED;
53854f531f6SZhou Wang }
53954f531f6SZhou Wang 
54054f531f6SZhou Wang static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,
54154f531f6SZhou Wang 	struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
54254f531f6SZhou Wang {
543d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
54454f531f6SZhou Wang 	int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
54554f531f6SZhou Wang 	int stat_1, stat_2;
54654f531f6SZhou Wang 
54725f815f6SBoris Brezillon 	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
54854f531f6SZhou Wang 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
54954f531f6SZhou Wang 
55054f531f6SZhou Wang 	/* errors which can not be corrected by ECC */
55154f531f6SZhou Wang 	if (host->irq_status & HINFC504_INTS_UE) {
55254f531f6SZhou Wang 		mtd->ecc_stats.failed++;
55354f531f6SZhou Wang 	} else if (host->irq_status & HINFC504_INTS_CE) {
55454f531f6SZhou Wang 		/* TODO: need add other ECC modes! */
55554f531f6SZhou Wang 		switch (chip->ecc.strength) {
55654f531f6SZhou Wang 		case 16:
55754f531f6SZhou Wang 			status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >>
55854f531f6SZhou Wang 					HINFC504_ECC_16_BIT_SHIFT & 0x0fff;
55954f531f6SZhou Wang 			stat_2 = status_ecc & 0x3f;
56054f531f6SZhou Wang 			stat_1 = status_ecc >> 6 & 0x3f;
56154f531f6SZhou Wang 			stat = stat_1 + stat_2;
56254f531f6SZhou Wang 			stat_max = max_t(int, stat_1, stat_2);
56354f531f6SZhou Wang 		}
56454f531f6SZhou Wang 		mtd->ecc_stats.corrected += stat;
56554f531f6SZhou Wang 		max_bitflips = max_t(int, max_bitflips, stat_max);
56654f531f6SZhou Wang 	}
56754f531f6SZhou Wang 	host->irq_status = 0;
56854f531f6SZhou Wang 
56954f531f6SZhou Wang 	return max_bitflips;
57054f531f6SZhou Wang }
57154f531f6SZhou Wang 
57254f531f6SZhou Wang static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
57354f531f6SZhou Wang 				int page)
57454f531f6SZhou Wang {
575d699ed25SBoris BREZILLON 	struct hinfc_host *host = nand_get_controller_data(chip);
57654f531f6SZhou Wang 
57797d90da8SBoris Brezillon 	nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
57854f531f6SZhou Wang 
57954f531f6SZhou Wang 	if (host->irq_status & HINFC504_INTS_UE) {
58054f531f6SZhou Wang 		host->irq_status = 0;
58154f531f6SZhou Wang 		return -EBADMSG;
58254f531f6SZhou Wang 	}
58354f531f6SZhou Wang 
58454f531f6SZhou Wang 	host->irq_status = 0;
58554f531f6SZhou Wang 	return 0;
58654f531f6SZhou Wang }
58754f531f6SZhou Wang 
58854f531f6SZhou Wang static int hisi_nand_write_page_hwecc(struct mtd_info *mtd,
58945aaeff9SBoris BREZILLON 		struct nand_chip *chip, const uint8_t *buf, int oob_required,
59045aaeff9SBoris BREZILLON 		int page)
59154f531f6SZhou Wang {
59225f815f6SBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
59354f531f6SZhou Wang 	if (oob_required)
59454f531f6SZhou Wang 		chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
59554f531f6SZhou Wang 
59625f815f6SBoris Brezillon 	return nand_prog_page_end_op(chip);
59754f531f6SZhou Wang }
59854f531f6SZhou Wang 
59954f531f6SZhou Wang static void hisi_nfc_host_init(struct hinfc_host *host)
60054f531f6SZhou Wang {
60154f531f6SZhou Wang 	struct nand_chip *chip = &host->chip;
60254f531f6SZhou Wang 	unsigned int flag = 0;
60354f531f6SZhou Wang 
60454f531f6SZhou Wang 	host->version = hinfc_read(host, HINFC_VERSION);
60554f531f6SZhou Wang 	host->addr_cycle		= 0;
60654f531f6SZhou Wang 	host->addr_value[0]		= 0;
60754f531f6SZhou Wang 	host->addr_value[1]		= 0;
60854f531f6SZhou Wang 	host->cache_addr_value[0]	= ~0;
60954f531f6SZhou Wang 	host->cache_addr_value[1]	= ~0;
61054f531f6SZhou Wang 	host->chipselect		= 0;
61154f531f6SZhou Wang 
61254f531f6SZhou Wang 	/* default page size: 2K, ecc_none. need modify */
61354f531f6SZhou Wang 	flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL
61454f531f6SZhou Wang 		| ((0x001 & HINFC504_CON_PAGESIZE_MASK)
61554f531f6SZhou Wang 			<< HINFC504_CON_PAGEISZE_SHIFT)
61654f531f6SZhou Wang 		| ((0x0 & HINFC504_CON_ECCTYPE_MASK)
61754f531f6SZhou Wang 			<< HINFC504_CON_ECCTYPE_SHIFT)
61854f531f6SZhou Wang 		| ((chip->options & NAND_BUSWIDTH_16) ?
61954f531f6SZhou Wang 			HINFC504_CON_BUS_WIDTH : 0);
62054f531f6SZhou Wang 	hinfc_write(host, flag, HINFC504_CON);
62154f531f6SZhou Wang 
62254f531f6SZhou Wang 	memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
62354f531f6SZhou Wang 
62454f531f6SZhou Wang 	hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
62554f531f6SZhou Wang 		    HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
62654f531f6SZhou Wang 
62754f531f6SZhou Wang 	/* enable DMA irq */
62854f531f6SZhou Wang 	hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
62954f531f6SZhou Wang }
63054f531f6SZhou Wang 
6312ca9ec9aSBoris Brezillon static int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
6322ca9ec9aSBoris Brezillon 			      struct mtd_oob_region *oobregion)
6332ca9ec9aSBoris Brezillon {
6342ca9ec9aSBoris Brezillon 	/* FIXME: add ECC bytes position */
6352ca9ec9aSBoris Brezillon 	return -ENOTSUPP;
6362ca9ec9aSBoris Brezillon }
6372ca9ec9aSBoris Brezillon 
6382ca9ec9aSBoris Brezillon static int hisi_ooblayout_free(struct mtd_info *mtd, int section,
6392ca9ec9aSBoris Brezillon 			       struct mtd_oob_region *oobregion)
6402ca9ec9aSBoris Brezillon {
6412ca9ec9aSBoris Brezillon 	if (section)
6422ca9ec9aSBoris Brezillon 		return -ERANGE;
6432ca9ec9aSBoris Brezillon 
6442ca9ec9aSBoris Brezillon 	oobregion->offset = 2;
6452ca9ec9aSBoris Brezillon 	oobregion->length = 6;
6462ca9ec9aSBoris Brezillon 
6472ca9ec9aSBoris Brezillon 	return 0;
6482ca9ec9aSBoris Brezillon }
6492ca9ec9aSBoris Brezillon 
6502ca9ec9aSBoris Brezillon static const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
6512ca9ec9aSBoris Brezillon 	.ecc = hisi_ooblayout_ecc,
6522ca9ec9aSBoris Brezillon 	.free = hisi_ooblayout_free,
65354f531f6SZhou Wang };
65454f531f6SZhou Wang 
65554f531f6SZhou Wang static int hisi_nfc_ecc_probe(struct hinfc_host *host)
65654f531f6SZhou Wang {
65754f531f6SZhou Wang 	unsigned int flag;
65854f531f6SZhou Wang 	int size, strength, ecc_bits;
65954f531f6SZhou Wang 	struct device *dev = host->dev;
66054f531f6SZhou Wang 	struct nand_chip *chip = &host->chip;
661fa100163SBoris BREZILLON 	struct mtd_info *mtd = nand_to_mtd(chip);
66254f531f6SZhou Wang 
6632d01922cSBoris Brezillon 	size = chip->ecc.size;
6642d01922cSBoris Brezillon 	strength = chip->ecc.strength;
66554f531f6SZhou Wang 	if (size != 1024) {
66654f531f6SZhou Wang 		dev_err(dev, "error ecc size: %d\n", size);
66754f531f6SZhou Wang 		return -EINVAL;
66854f531f6SZhou Wang 	}
66954f531f6SZhou Wang 
67054f531f6SZhou Wang 	if ((size == 1024) && ((strength != 8) && (strength != 16) &&
67154f531f6SZhou Wang 				(strength != 24) && (strength != 40))) {
67254f531f6SZhou Wang 		dev_err(dev, "ecc size and strength do not match\n");
67354f531f6SZhou Wang 		return -EINVAL;
67454f531f6SZhou Wang 	}
67554f531f6SZhou Wang 
67654f531f6SZhou Wang 	chip->ecc.size = size;
67754f531f6SZhou Wang 	chip->ecc.strength = strength;
67854f531f6SZhou Wang 
67954f531f6SZhou Wang 	chip->ecc.read_page = hisi_nand_read_page_hwecc;
68054f531f6SZhou Wang 	chip->ecc.read_oob = hisi_nand_read_oob;
68154f531f6SZhou Wang 	chip->ecc.write_page = hisi_nand_write_page_hwecc;
68254f531f6SZhou Wang 
68354f531f6SZhou Wang 	switch (chip->ecc.strength) {
68454f531f6SZhou Wang 	case 16:
68554f531f6SZhou Wang 		ecc_bits = 6;
68654f531f6SZhou Wang 		if (mtd->writesize == 2048)
6872ca9ec9aSBoris Brezillon 			mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
68854f531f6SZhou Wang 
68954f531f6SZhou Wang 		/* TODO: add more page size support */
69054f531f6SZhou Wang 		break;
69154f531f6SZhou Wang 
69254f531f6SZhou Wang 	/* TODO: add more ecc strength support */
69354f531f6SZhou Wang 	default:
69454f531f6SZhou Wang 		dev_err(dev, "not support strength: %d\n", chip->ecc.strength);
69554f531f6SZhou Wang 		return -EINVAL;
69654f531f6SZhou Wang 	}
69754f531f6SZhou Wang 
69854f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_CON);
69954f531f6SZhou Wang 	/* add ecc type configure */
70054f531f6SZhou Wang 	flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK)
70154f531f6SZhou Wang 						<< HINFC504_CON_ECCTYPE_SHIFT);
70254f531f6SZhou Wang 	hinfc_write(host, flag, HINFC504_CON);
70354f531f6SZhou Wang 
70454f531f6SZhou Wang 	/* enable ecc irq */
70554f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_INTEN) & 0xfff;
70654f531f6SZhou Wang 	hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE,
70754f531f6SZhou Wang 		    HINFC504_INTEN);
70854f531f6SZhou Wang 
70954f531f6SZhou Wang 	return 0;
71054f531f6SZhou Wang }
71154f531f6SZhou Wang 
71254f531f6SZhou Wang static int hisi_nfc_probe(struct platform_device *pdev)
71354f531f6SZhou Wang {
7142d01922cSBoris Brezillon 	int ret = 0, irq, flag, max_chips = HINFC504_MAX_CHIP;
71554f531f6SZhou Wang 	struct device *dev = &pdev->dev;
71654f531f6SZhou Wang 	struct hinfc_host *host;
71754f531f6SZhou Wang 	struct nand_chip  *chip;
71854f531f6SZhou Wang 	struct mtd_info   *mtd;
71954f531f6SZhou Wang 	struct resource	  *res;
72054f531f6SZhou Wang 	struct device_node *np = dev->of_node;
72154f531f6SZhou Wang 
72254f531f6SZhou Wang 	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
72354f531f6SZhou Wang 	if (!host)
72454f531f6SZhou Wang 		return -ENOMEM;
72554f531f6SZhou Wang 	host->dev = dev;
72654f531f6SZhou Wang 
72754f531f6SZhou Wang 	platform_set_drvdata(pdev, host);
72854f531f6SZhou Wang 	chip = &host->chip;
729fa100163SBoris BREZILLON 	mtd  = nand_to_mtd(chip);
73054f531f6SZhou Wang 
73154f531f6SZhou Wang 	irq = platform_get_irq(pdev, 0);
73254f531f6SZhou Wang 	if (irq < 0) {
73354f531f6SZhou Wang 		dev_err(dev, "no IRQ resource defined\n");
734*9326dc75SMiquel Raynal 		return -ENXIO;
73554f531f6SZhou Wang 	}
73654f531f6SZhou Wang 
73754f531f6SZhou Wang 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
73854f531f6SZhou Wang 	host->iobase = devm_ioremap_resource(dev, res);
739*9326dc75SMiquel Raynal 	if (IS_ERR(host->iobase))
740*9326dc75SMiquel Raynal 		return PTR_ERR(host->iobase);
74154f531f6SZhou Wang 
74254f531f6SZhou Wang 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
74354f531f6SZhou Wang 	host->mmio = devm_ioremap_resource(dev, res);
74454f531f6SZhou Wang 	if (IS_ERR(host->mmio)) {
74554f531f6SZhou Wang 		dev_err(dev, "devm_ioremap_resource[1] fail\n");
746*9326dc75SMiquel Raynal 		return PTR_ERR(host->mmio);
74754f531f6SZhou Wang 	}
74854f531f6SZhou Wang 
74954f531f6SZhou Wang 	mtd->name		= "hisi_nand";
75054f531f6SZhou Wang 	mtd->dev.parent         = &pdev->dev;
75154f531f6SZhou Wang 
752d699ed25SBoris BREZILLON 	nand_set_controller_data(chip, host);
753a61ae81aSBrian Norris 	nand_set_flash_node(chip, np);
75454f531f6SZhou Wang 	chip->cmdfunc		= hisi_nfc_cmdfunc;
75554f531f6SZhou Wang 	chip->select_chip	= hisi_nfc_select_chip;
75654f531f6SZhou Wang 	chip->read_byte		= hisi_nfc_read_byte;
75754f531f6SZhou Wang 	chip->read_word		= hisi_nfc_read_word;
75854f531f6SZhou Wang 	chip->write_buf		= hisi_nfc_write_buf;
75954f531f6SZhou Wang 	chip->read_buf		= hisi_nfc_read_buf;
76054f531f6SZhou Wang 	chip->chip_delay	= HINFC504_CHIP_DELAY;
761b958758eSMiquel Raynal 	chip->set_features	= nand_get_set_features_notsupp;
762b958758eSMiquel Raynal 	chip->get_features	= nand_get_set_features_notsupp;
76354f531f6SZhou Wang 
76454f531f6SZhou Wang 	hisi_nfc_host_init(host);
76554f531f6SZhou Wang 
766d8bf368dSValentin Rothberg 	ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);
76754f531f6SZhou Wang 	if (ret) {
76854f531f6SZhou Wang 		dev_err(dev, "failed to request IRQ\n");
769*9326dc75SMiquel Raynal 		return ret;
77054f531f6SZhou Wang 	}
77154f531f6SZhou Wang 
77254f531f6SZhou Wang 	ret = nand_scan_ident(mtd, max_chips, NULL);
773c8cae355SMasahiro Yamada 	if (ret)
774*9326dc75SMiquel Raynal 		return ret;
77554f531f6SZhou Wang 
77654f531f6SZhou Wang 	host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
77754f531f6SZhou Wang 		&host->dma_buffer, GFP_KERNEL);
778*9326dc75SMiquel Raynal 	if (!host->buffer)
779*9326dc75SMiquel Raynal 		return -ENOMEM;
78054f531f6SZhou Wang 
78154f531f6SZhou Wang 	host->dma_oob = host->dma_buffer + mtd->writesize;
78254f531f6SZhou Wang 	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
78354f531f6SZhou Wang 
78454f531f6SZhou Wang 	flag = hinfc_read(host, HINFC504_CON);
78554f531f6SZhou Wang 	flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
78654f531f6SZhou Wang 	switch (mtd->writesize) {
78754f531f6SZhou Wang 	case 2048:
78854f531f6SZhou Wang 		flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break;
78954f531f6SZhou Wang 	/*
79054f531f6SZhou Wang 	 * TODO: add more pagesize support,
79154f531f6SZhou Wang 	 * default pagesize has been set in hisi_nfc_host_init
79254f531f6SZhou Wang 	 */
79354f531f6SZhou Wang 	default:
79454f531f6SZhou Wang 		dev_err(dev, "NON-2KB page size nand flash\n");
795*9326dc75SMiquel Raynal 		return -EINVAL;
79654f531f6SZhou Wang 	}
79754f531f6SZhou Wang 	hinfc_write(host, flag, HINFC504_CON);
79854f531f6SZhou Wang 
79954f531f6SZhou Wang 	if (chip->ecc.mode == NAND_ECC_HW)
80054f531f6SZhou Wang 		hisi_nfc_ecc_probe(host);
80154f531f6SZhou Wang 
80254f531f6SZhou Wang 	ret = nand_scan_tail(mtd);
80354f531f6SZhou Wang 	if (ret) {
80454f531f6SZhou Wang 		dev_err(dev, "nand_scan_tail failed: %d\n", ret);
805*9326dc75SMiquel Raynal 		return ret;
80654f531f6SZhou Wang 	}
80754f531f6SZhou Wang 
808a61ae81aSBrian Norris 	ret = mtd_device_register(mtd, NULL, 0);
80954f531f6SZhou Wang 	if (ret) {
81054f531f6SZhou Wang 		dev_err(dev, "Err MTD partition=%d\n", ret);
811*9326dc75SMiquel Raynal 		nand_release(mtd);
812*9326dc75SMiquel Raynal 		return ret;
81354f531f6SZhou Wang 	}
81454f531f6SZhou Wang 
81554f531f6SZhou Wang 	return 0;
81654f531f6SZhou Wang }
81754f531f6SZhou Wang 
81854f531f6SZhou Wang static int hisi_nfc_remove(struct platform_device *pdev)
81954f531f6SZhou Wang {
82054f531f6SZhou Wang 	struct hinfc_host *host = platform_get_drvdata(pdev);
821fa100163SBoris BREZILLON 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
82254f531f6SZhou Wang 
82354f531f6SZhou Wang 	nand_release(mtd);
82454f531f6SZhou Wang 
82554f531f6SZhou Wang 	return 0;
82654f531f6SZhou Wang }
82754f531f6SZhou Wang 
82854f531f6SZhou Wang #ifdef CONFIG_PM_SLEEP
82954f531f6SZhou Wang static int hisi_nfc_suspend(struct device *dev)
83054f531f6SZhou Wang {
83154f531f6SZhou Wang 	struct hinfc_host *host = dev_get_drvdata(dev);
83254f531f6SZhou Wang 	unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT;
83354f531f6SZhou Wang 
83454f531f6SZhou Wang 	while (time_before(jiffies, timeout)) {
83554f531f6SZhou Wang 		if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) &&
83654f531f6SZhou Wang 		    (hinfc_read(host, HINFC504_DMA_CTRL) &
83754f531f6SZhou Wang 		     HINFC504_DMA_CTRL_DMA_START)) {
83854f531f6SZhou Wang 			cond_resched();
83954f531f6SZhou Wang 			return 0;
84054f531f6SZhou Wang 		}
84154f531f6SZhou Wang 	}
84254f531f6SZhou Wang 
84354f531f6SZhou Wang 	dev_err(host->dev, "nand controller suspend timeout.\n");
84454f531f6SZhou Wang 
84554f531f6SZhou Wang 	return -EAGAIN;
84654f531f6SZhou Wang }
84754f531f6SZhou Wang 
84854f531f6SZhou Wang static int hisi_nfc_resume(struct device *dev)
84954f531f6SZhou Wang {
85054f531f6SZhou Wang 	int cs;
85154f531f6SZhou Wang 	struct hinfc_host *host = dev_get_drvdata(dev);
85254f531f6SZhou Wang 	struct nand_chip *chip = &host->chip;
85354f531f6SZhou Wang 
85454f531f6SZhou Wang 	for (cs = 0; cs < chip->numchips; cs++)
85554f531f6SZhou Wang 		hisi_nfc_send_cmd_reset(host, cs);
85654f531f6SZhou Wang 	hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
85754f531f6SZhou Wang 		    HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
85854f531f6SZhou Wang 
85954f531f6SZhou Wang 	return 0;
86054f531f6SZhou Wang }
86154f531f6SZhou Wang #endif
86254f531f6SZhou Wang static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume);
86354f531f6SZhou Wang 
86454f531f6SZhou Wang static const struct of_device_id nfc_id_table[] = {
86554f531f6SZhou Wang 	{ .compatible = "hisilicon,504-nfc" },
86654f531f6SZhou Wang 	{}
86754f531f6SZhou Wang };
86854f531f6SZhou Wang MODULE_DEVICE_TABLE(of, nfc_id_table);
86954f531f6SZhou Wang 
87054f531f6SZhou Wang static struct platform_driver hisi_nfc_driver = {
87154f531f6SZhou Wang 	.driver = {
87254f531f6SZhou Wang 		.name  = "hisi_nand",
87354f531f6SZhou Wang 		.of_match_table = nfc_id_table,
87454f531f6SZhou Wang 		.pm = &hisi_nfc_pm_ops,
87554f531f6SZhou Wang 	},
87654f531f6SZhou Wang 	.probe		= hisi_nfc_probe,
87754f531f6SZhou Wang 	.remove		= hisi_nfc_remove,
87854f531f6SZhou Wang };
87954f531f6SZhou Wang 
88054f531f6SZhou Wang module_platform_driver(hisi_nfc_driver);
88154f531f6SZhou Wang 
88254f531f6SZhou Wang MODULE_LICENSE("GPL");
88354f531f6SZhou Wang MODULE_AUTHOR("Zhou Wang");
88454f531f6SZhou Wang MODULE_AUTHOR("Zhiyong Cai");
88554f531f6SZhou Wang MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver");
886