18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Hisilicon NAND Flash controller driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright © 2012-2014 HiSilicon Technologies Co., Ltd.
68c2ecf20Sopenharmony_ci *              http://www.hisilicon.com
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Zhou Wang <wangzhou.bry@gmail.com>
98c2ecf20Sopenharmony_ci * The initial developer of the original code is Zhiyong Cai
108c2ecf20Sopenharmony_ci * <caizhiyong@huawei.com>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
148c2ecf20Sopenharmony_ci#include <linux/sizes.h>
158c2ecf20Sopenharmony_ci#include <linux/clk.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
228c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
238c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define HINFC504_MAX_CHIP                               (4)
268c2ecf20Sopenharmony_ci#define HINFC504_W_LATCH                                (5)
278c2ecf20Sopenharmony_ci#define HINFC504_R_LATCH                                (7)
288c2ecf20Sopenharmony_ci#define HINFC504_RW_LATCH                               (3)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define HINFC504_NFC_TIMEOUT				(2 * HZ)
318c2ecf20Sopenharmony_ci#define HINFC504_NFC_PM_TIMEOUT				(1 * HZ)
328c2ecf20Sopenharmony_ci#define HINFC504_NFC_DMA_TIMEOUT			(5 * HZ)
338c2ecf20Sopenharmony_ci#define HINFC504_CHIP_DELAY				(25)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define HINFC504_REG_BASE_ADDRESS_LEN			(0x100)
368c2ecf20Sopenharmony_ci#define HINFC504_BUFFER_BASE_ADDRESS_LEN		(2048 + 128)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define HINFC504_ADDR_CYCLE_MASK			0x4
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define HINFC504_CON					0x00
418c2ecf20Sopenharmony_ci#define HINFC504_CON_OP_MODE_NORMAL			BIT(0)
428c2ecf20Sopenharmony_ci#define HINFC504_CON_PAGEISZE_SHIFT			(1)
438c2ecf20Sopenharmony_ci#define HINFC504_CON_PAGESIZE_MASK			(0x07)
448c2ecf20Sopenharmony_ci#define HINFC504_CON_BUS_WIDTH				BIT(4)
458c2ecf20Sopenharmony_ci#define HINFC504_CON_READY_BUSY_SEL			BIT(8)
468c2ecf20Sopenharmony_ci#define HINFC504_CON_ECCTYPE_SHIFT			(9)
478c2ecf20Sopenharmony_ci#define HINFC504_CON_ECCTYPE_MASK			(0x07)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define HINFC504_PWIDTH					0x04
508c2ecf20Sopenharmony_ci#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
518c2ecf20Sopenharmony_ci	((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define HINFC504_CMD					0x0C
548c2ecf20Sopenharmony_ci#define HINFC504_ADDRL					0x10
558c2ecf20Sopenharmony_ci#define HINFC504_ADDRH					0x14
568c2ecf20Sopenharmony_ci#define HINFC504_DATA_NUM				0x18
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define HINFC504_OP					0x1C
598c2ecf20Sopenharmony_ci#define HINFC504_OP_READ_DATA_EN			BIT(1)
608c2ecf20Sopenharmony_ci#define HINFC504_OP_WAIT_READY_EN			BIT(2)
618c2ecf20Sopenharmony_ci#define HINFC504_OP_CMD2_EN				BIT(3)
628c2ecf20Sopenharmony_ci#define HINFC504_OP_WRITE_DATA_EN			BIT(4)
638c2ecf20Sopenharmony_ci#define HINFC504_OP_ADDR_EN				BIT(5)
648c2ecf20Sopenharmony_ci#define HINFC504_OP_CMD1_EN				BIT(6)
658c2ecf20Sopenharmony_ci#define HINFC504_OP_NF_CS_SHIFT                         (7)
668c2ecf20Sopenharmony_ci#define HINFC504_OP_NF_CS_MASK				(3)
678c2ecf20Sopenharmony_ci#define HINFC504_OP_ADDR_CYCLE_SHIFT			(9)
688c2ecf20Sopenharmony_ci#define HINFC504_OP_ADDR_CYCLE_MASK			(7)
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#define HINFC504_STATUS                                 0x20
718c2ecf20Sopenharmony_ci#define HINFC504_READY					BIT(0)
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define HINFC504_INTEN					0x24
748c2ecf20Sopenharmony_ci#define HINFC504_INTEN_DMA				BIT(9)
758c2ecf20Sopenharmony_ci#define HINFC504_INTEN_UE				BIT(6)
768c2ecf20Sopenharmony_ci#define HINFC504_INTEN_CE				BIT(5)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define HINFC504_INTS					0x28
798c2ecf20Sopenharmony_ci#define HINFC504_INTS_DMA				BIT(9)
808c2ecf20Sopenharmony_ci#define HINFC504_INTS_UE				BIT(6)
818c2ecf20Sopenharmony_ci#define HINFC504_INTS_CE				BIT(5)
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#define HINFC504_INTCLR                                 0x2C
848c2ecf20Sopenharmony_ci#define HINFC504_INTCLR_DMA				BIT(9)
858c2ecf20Sopenharmony_ci#define HINFC504_INTCLR_UE				BIT(6)
868c2ecf20Sopenharmony_ci#define HINFC504_INTCLR_CE				BIT(5)
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci#define HINFC504_ECC_STATUS                             0x5C
898c2ecf20Sopenharmony_ci#define HINFC504_ECC_16_BIT_SHIFT                       12
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL				0x60
928c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_DMA_START			BIT(0)
938c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_WE				BIT(1)
948c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_DATA_AREA_EN			BIT(2)
958c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_OOB_AREA_EN			BIT(3)
968c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_BURST4_EN			BIT(4)
978c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_BURST8_EN			BIT(5)
988c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_BURST16_EN			BIT(6)
998c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT		(7)
1008c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_ADDR_NUM_MASK                 (1)
1018c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_CS_SHIFT			(8)
1028c2ecf20Sopenharmony_ci#define HINFC504_DMA_CTRL_CS_MASK			(0x03)
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci#define HINFC504_DMA_ADDR_DATA				0x64
1058c2ecf20Sopenharmony_ci#define HINFC504_DMA_ADDR_OOB				0x68
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define HINFC504_DMA_LEN				0x6C
1088c2ecf20Sopenharmony_ci#define HINFC504_DMA_LEN_OOB_SHIFT			(16)
1098c2ecf20Sopenharmony_ci#define HINFC504_DMA_LEN_OOB_MASK			(0xFFF)
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA				0x70
1128c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA_DATA_RW_EN			BIT(0)
1138c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA_OOB_RW_EN			BIT(1)
1148c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA_DATA_EDC_EN			BIT(2)
1158c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA_OOB_EDC_EN			BIT(3)
1168c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA_DATA_ECC_EN			BIT(4)
1178c2ecf20Sopenharmony_ci#define HINFC504_DMA_PARA_OOB_ECC_EN			BIT(5)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#define HINFC_VERSION                                   0x74
1208c2ecf20Sopenharmony_ci#define HINFC504_LOG_READ_ADDR				0x7C
1218c2ecf20Sopenharmony_ci#define HINFC504_LOG_READ_LEN				0x80
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define HINFC504_NANDINFO_LEN				0x10
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct hinfc_host {
1268c2ecf20Sopenharmony_ci	struct nand_chip	chip;
1278c2ecf20Sopenharmony_ci	struct device		*dev;
1288c2ecf20Sopenharmony_ci	void __iomem		*iobase;
1298c2ecf20Sopenharmony_ci	void __iomem		*mmio;
1308c2ecf20Sopenharmony_ci	struct completion       cmd_complete;
1318c2ecf20Sopenharmony_ci	unsigned int		offset;
1328c2ecf20Sopenharmony_ci	unsigned int		command;
1338c2ecf20Sopenharmony_ci	int			chipselect;
1348c2ecf20Sopenharmony_ci	unsigned int		addr_cycle;
1358c2ecf20Sopenharmony_ci	u32                     addr_value[2];
1368c2ecf20Sopenharmony_ci	u32                     cache_addr_value[2];
1378c2ecf20Sopenharmony_ci	char			*buffer;
1388c2ecf20Sopenharmony_ci	dma_addr_t		dma_buffer;
1398c2ecf20Sopenharmony_ci	dma_addr_t		dma_oob;
1408c2ecf20Sopenharmony_ci	int			version;
1418c2ecf20Sopenharmony_ci	unsigned int            irq_status; /* interrupt status */
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	return readl(host->iobase + reg);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic inline void hinfc_write(struct hinfc_host *host, unsigned int value,
1508c2ecf20Sopenharmony_ci			       unsigned int reg)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	writel(value, host->iobase + reg);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void wait_controller_finished(struct hinfc_host *host)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
1588c2ecf20Sopenharmony_ci	int val;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
1618c2ecf20Sopenharmony_ci		val = hinfc_read(host, HINFC504_STATUS);
1628c2ecf20Sopenharmony_ci		if (host->command == NAND_CMD_ERASE2) {
1638c2ecf20Sopenharmony_ci			/* nfc is ready */
1648c2ecf20Sopenharmony_ci			while (!(val & HINFC504_READY))	{
1658c2ecf20Sopenharmony_ci				usleep_range(500, 1000);
1668c2ecf20Sopenharmony_ci				val = hinfc_read(host, HINFC504_STATUS);
1678c2ecf20Sopenharmony_ci			}
1688c2ecf20Sopenharmony_ci			return;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		if (val & HINFC504_READY)
1728c2ecf20Sopenharmony_ci			return;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* wait cmd timeout */
1768c2ecf20Sopenharmony_ci	dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n");
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
1828c2ecf20Sopenharmony_ci	struct mtd_info	*mtd = nand_to_mtd(chip);
1838c2ecf20Sopenharmony_ci	unsigned long val;
1848c2ecf20Sopenharmony_ci	int ret;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA);
1878c2ecf20Sopenharmony_ci	hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_NONE) {
1908c2ecf20Sopenharmony_ci		hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK)
1918c2ecf20Sopenharmony_ci			<< HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
1948c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA);
1958c2ecf20Sopenharmony_ci	} else {
1968c2ecf20Sopenharmony_ci		if (host->command == NAND_CMD_READOOB)
1978c2ecf20Sopenharmony_ci			hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN
1988c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_OOB_EDC_EN
1998c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
2008c2ecf20Sopenharmony_ci		else
2018c2ecf20Sopenharmony_ci			hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
2028c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_OOB_RW_EN
2038c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_DATA_EDC_EN
2048c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_OOB_EDC_EN
2058c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_DATA_ECC_EN
2068c2ecf20Sopenharmony_ci			| HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN
2118c2ecf20Sopenharmony_ci		| HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN
2128c2ecf20Sopenharmony_ci		| HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN
2138c2ecf20Sopenharmony_ci		| ((host->addr_cycle == 4 ? 1 : 0)
2148c2ecf20Sopenharmony_ci			<< HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
2158c2ecf20Sopenharmony_ci		| ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
2168c2ecf20Sopenharmony_ci			<< HINFC504_DMA_CTRL_CS_SHIFT));
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (todev)
2198c2ecf20Sopenharmony_ci		val |= HINFC504_DMA_CTRL_WE;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	init_completion(&host->cmd_complete);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	hinfc_write(host, val, HINFC504_DMA_CTRL);
2248c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&host->cmd_complete,
2258c2ecf20Sopenharmony_ci			HINFC504_NFC_DMA_TIMEOUT);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!ret) {
2288c2ecf20Sopenharmony_ci		dev_err(host->dev, "DMA operation(irq) timeout!\n");
2298c2ecf20Sopenharmony_ci		/* sanity check */
2308c2ecf20Sopenharmony_ci		val = hinfc_read(host, HINFC504_DMA_CTRL);
2318c2ecf20Sopenharmony_ci		if (!(val & HINFC504_DMA_CTRL_DMA_START))
2328c2ecf20Sopenharmony_ci			dev_err(host->dev, "DMA is already done but without irq ACK!\n");
2338c2ecf20Sopenharmony_ci		else
2348c2ecf20Sopenharmony_ci			dev_err(host->dev, "DMA is really timeout!\n");
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	host->addr_value[0] &= 0xffff0000;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
2438c2ecf20Sopenharmony_ci	hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
2448c2ecf20Sopenharmony_ci	hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
2458c2ecf20Sopenharmony_ci		    HINFC504_CMD);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	hisi_nfc_dma_transfer(host, 1);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	struct mtd_info	*mtd = nand_to_mtd(&host->chip);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if ((host->addr_value[0] == host->cache_addr_value[0]) &&
2578c2ecf20Sopenharmony_ci	    (host->addr_value[1] == host->cache_addr_value[1]))
2588c2ecf20Sopenharmony_ci		return 0;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	host->addr_value[0] &= 0xffff0000;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
2638c2ecf20Sopenharmony_ci	hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
2648c2ecf20Sopenharmony_ci	hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
2658c2ecf20Sopenharmony_ci		    HINFC504_CMD);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
2688c2ecf20Sopenharmony_ci	hinfc_write(host, mtd->writesize + mtd->oobsize,
2698c2ecf20Sopenharmony_ci		    HINFC504_LOG_READ_LEN);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	hisi_nfc_dma_transfer(host, 0);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	host->cache_addr_value[0] = host->addr_value[0];
2748c2ecf20Sopenharmony_ci	host->cache_addr_value[1] = host->addr_value[1];
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int hisi_nfc_send_cmd_erase(struct hinfc_host *host)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
2828c2ecf20Sopenharmony_ci	hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
2838c2ecf20Sopenharmony_ci		    HINFC504_CMD);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_OP_WAIT_READY_EN
2868c2ecf20Sopenharmony_ci		| HINFC504_OP_CMD2_EN
2878c2ecf20Sopenharmony_ci		| HINFC504_OP_CMD1_EN
2888c2ecf20Sopenharmony_ci		| HINFC504_OP_ADDR_EN
2898c2ecf20Sopenharmony_ci		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
2908c2ecf20Sopenharmony_ci			<< HINFC504_OP_NF_CS_SHIFT)
2918c2ecf20Sopenharmony_ci		| ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
2928c2ecf20Sopenharmony_ci			<< HINFC504_OP_ADDR_CYCLE_SHIFT),
2938c2ecf20Sopenharmony_ci		HINFC504_OP);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	wait_controller_finished(host);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int hisi_nfc_send_cmd_readid(struct hinfc_host *host)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
3038c2ecf20Sopenharmony_ci	hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
3048c2ecf20Sopenharmony_ci	hinfc_write(host, 0, HINFC504_ADDRL);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN
3078c2ecf20Sopenharmony_ci		| HINFC504_OP_READ_DATA_EN
3088c2ecf20Sopenharmony_ci		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
3098c2ecf20Sopenharmony_ci			<< HINFC504_OP_NF_CS_SHIFT)
3108c2ecf20Sopenharmony_ci		| 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	wait_controller_finished(host);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int hisi_nfc_send_cmd_status(struct hinfc_host *host)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
3208c2ecf20Sopenharmony_ci	hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
3218c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_OP_CMD1_EN
3228c2ecf20Sopenharmony_ci		| HINFC504_OP_READ_DATA_EN
3238c2ecf20Sopenharmony_ci		| ((host->chipselect & HINFC504_OP_NF_CS_MASK)
3248c2ecf20Sopenharmony_ci			<< HINFC504_OP_NF_CS_SHIFT),
3258c2ecf20Sopenharmony_ci		HINFC504_OP);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	wait_controller_finished(host);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_OP_CMD1_EN
3378c2ecf20Sopenharmony_ci		| ((chipselect & HINFC504_OP_NF_CS_MASK)
3388c2ecf20Sopenharmony_ci			<< HINFC504_OP_NF_CS_SHIFT)
3398c2ecf20Sopenharmony_ci		| HINFC504_OP_WAIT_READY_EN,
3408c2ecf20Sopenharmony_ci		HINFC504_OP);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	wait_controller_finished(host);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	return 0;
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void hisi_nfc_select_chip(struct nand_chip *chip, int chipselect)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (chipselect < 0)
3528c2ecf20Sopenharmony_ci		return;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	host->chipselect = chipselect;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic uint8_t hisi_nfc_read_byte(struct nand_chip *chip)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (host->command == NAND_CMD_STATUS)
3628c2ecf20Sopenharmony_ci		return *(uint8_t *)(host->mmio);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	host->offset++;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (host->command == NAND_CMD_READID)
3678c2ecf20Sopenharmony_ci		return *(uint8_t *)(host->mmio + host->offset - 1);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return *(uint8_t *)(host->buffer + host->offset - 1);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic void
3738c2ecf20Sopenharmony_cihisi_nfc_write_buf(struct nand_chip *chip, const uint8_t *buf, int len)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	memcpy(host->buffer + host->offset, buf, len);
3788c2ecf20Sopenharmony_ci	host->offset += len;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void hisi_nfc_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	memcpy(buf, host->buffer + host->offset, len);
3868c2ecf20Sopenharmony_ci	host->offset += len;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
3928c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
3938c2ecf20Sopenharmony_ci	unsigned int command = host->command;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	host->addr_cycle    = 0;
3968c2ecf20Sopenharmony_ci	host->addr_value[0] = 0;
3978c2ecf20Sopenharmony_ci	host->addr_value[1] = 0;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/* Serially input address */
4008c2ecf20Sopenharmony_ci	if (column != -1) {
4018c2ecf20Sopenharmony_ci		/* Adjust columns for 16 bit buswidth */
4028c2ecf20Sopenharmony_ci		if (chip->options & NAND_BUSWIDTH_16 &&
4038c2ecf20Sopenharmony_ci				!nand_opcode_8bits(command))
4048c2ecf20Sopenharmony_ci			column >>= 1;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		host->addr_value[0] = column & 0xffff;
4078c2ecf20Sopenharmony_ci		host->addr_cycle    = 2;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	if (page_addr != -1) {
4108c2ecf20Sopenharmony_ci		host->addr_value[0] |= (page_addr & 0xffff)
4118c2ecf20Sopenharmony_ci			<< (host->addr_cycle * 8);
4128c2ecf20Sopenharmony_ci		host->addr_cycle    += 2;
4138c2ecf20Sopenharmony_ci		if (chip->options & NAND_ROW_ADDR_3) {
4148c2ecf20Sopenharmony_ci			host->addr_cycle += 1;
4158c2ecf20Sopenharmony_ci			if (host->command == NAND_CMD_ERASE1)
4168c2ecf20Sopenharmony_ci				host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16;
4178c2ecf20Sopenharmony_ci			else
4188c2ecf20Sopenharmony_ci				host->addr_value[1] |= ((page_addr >> 16) & 0xff);
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic void hisi_nfc_cmdfunc(struct nand_chip *chip, unsigned command,
4248c2ecf20Sopenharmony_ci			     int column, int page_addr)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
4278c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
4288c2ecf20Sopenharmony_ci	int is_cache_invalid = 1;
4298c2ecf20Sopenharmony_ci	unsigned int flag = 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	host->command =  command;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	switch (command) {
4348c2ecf20Sopenharmony_ci	case NAND_CMD_READ0:
4358c2ecf20Sopenharmony_ci	case NAND_CMD_READOOB:
4368c2ecf20Sopenharmony_ci		if (command == NAND_CMD_READ0)
4378c2ecf20Sopenharmony_ci			host->offset = column;
4388c2ecf20Sopenharmony_ci		else
4398c2ecf20Sopenharmony_ci			host->offset = column + mtd->writesize;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		is_cache_invalid = 0;
4428c2ecf20Sopenharmony_ci		set_addr(mtd, column, page_addr);
4438c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_readstart(host);
4448c2ecf20Sopenharmony_ci		break;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	case NAND_CMD_SEQIN:
4478c2ecf20Sopenharmony_ci		host->offset = column;
4488c2ecf20Sopenharmony_ci		set_addr(mtd, column, page_addr);
4498c2ecf20Sopenharmony_ci		break;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE1:
4528c2ecf20Sopenharmony_ci		set_addr(mtd, column, page_addr);
4538c2ecf20Sopenharmony_ci		break;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	case NAND_CMD_PAGEPROG:
4568c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_pageprog(host);
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	case NAND_CMD_ERASE2:
4608c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_erase(host);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	case NAND_CMD_READID:
4648c2ecf20Sopenharmony_ci		host->offset = column;
4658c2ecf20Sopenharmony_ci		memset(host->mmio, 0, 0x10);
4668c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_readid(host);
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	case NAND_CMD_STATUS:
4708c2ecf20Sopenharmony_ci		flag = hinfc_read(host, HINFC504_CON);
4718c2ecf20Sopenharmony_ci		if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
4728c2ecf20Sopenharmony_ci			hinfc_write(host,
4738c2ecf20Sopenharmony_ci				    flag & ~(HINFC504_CON_ECCTYPE_MASK <<
4748c2ecf20Sopenharmony_ci				    HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		host->offset = 0;
4778c2ecf20Sopenharmony_ci		memset(host->mmio, 0, 0x10);
4788c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_status(host);
4798c2ecf20Sopenharmony_ci		hinfc_write(host, flag, HINFC504_CON);
4808c2ecf20Sopenharmony_ci		break;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	case NAND_CMD_RESET:
4838c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_reset(host, host->chipselect);
4848c2ecf20Sopenharmony_ci		break;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	default:
4878c2ecf20Sopenharmony_ci		dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
4888c2ecf20Sopenharmony_ci			command, column, page_addr);
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (is_cache_invalid) {
4928c2ecf20Sopenharmony_ci		host->cache_addr_value[0] = ~0;
4938c2ecf20Sopenharmony_ci		host->cache_addr_value[1] = ~0;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic irqreturn_t hinfc_irq_handle(int irq, void *devid)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct hinfc_host *host = devid;
5008c2ecf20Sopenharmony_ci	unsigned int flag;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	flag = hinfc_read(host, HINFC504_INTS);
5038c2ecf20Sopenharmony_ci	/* store interrupts state */
5048c2ecf20Sopenharmony_ci	host->irq_status |= flag;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (flag & HINFC504_INTS_DMA) {
5078c2ecf20Sopenharmony_ci		hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
5088c2ecf20Sopenharmony_ci		complete(&host->cmd_complete);
5098c2ecf20Sopenharmony_ci	} else if (flag & HINFC504_INTS_CE) {
5108c2ecf20Sopenharmony_ci		hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR);
5118c2ecf20Sopenharmony_ci	} else if (flag & HINFC504_INTS_UE) {
5128c2ecf20Sopenharmony_ci		hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR);
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic int hisi_nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
5198c2ecf20Sopenharmony_ci				     int oob_required, int page)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
5228c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
5238c2ecf20Sopenharmony_ci	int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
5248c2ecf20Sopenharmony_ci	int stat_1, stat_2;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	nand_read_page_op(chip, page, 0, buf, mtd->writesize);
5278c2ecf20Sopenharmony_ci	chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* errors which can not be corrected by ECC */
5308c2ecf20Sopenharmony_ci	if (host->irq_status & HINFC504_INTS_UE) {
5318c2ecf20Sopenharmony_ci		mtd->ecc_stats.failed++;
5328c2ecf20Sopenharmony_ci	} else if (host->irq_status & HINFC504_INTS_CE) {
5338c2ecf20Sopenharmony_ci		/* TODO: need add other ECC modes! */
5348c2ecf20Sopenharmony_ci		switch (chip->ecc.strength) {
5358c2ecf20Sopenharmony_ci		case 16:
5368c2ecf20Sopenharmony_ci			status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >>
5378c2ecf20Sopenharmony_ci					HINFC504_ECC_16_BIT_SHIFT & 0x0fff;
5388c2ecf20Sopenharmony_ci			stat_2 = status_ecc & 0x3f;
5398c2ecf20Sopenharmony_ci			stat_1 = status_ecc >> 6 & 0x3f;
5408c2ecf20Sopenharmony_ci			stat = stat_1 + stat_2;
5418c2ecf20Sopenharmony_ci			stat_max = max_t(int, stat_1, stat_2);
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci		mtd->ecc_stats.corrected += stat;
5448c2ecf20Sopenharmony_ci		max_bitflips = max_t(int, max_bitflips, stat_max);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci	host->irq_status = 0;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	return max_bitflips;
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic int hisi_nand_read_oob(struct nand_chip *chip, int page)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
5548c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (host->irq_status & HINFC504_INTS_UE) {
5598c2ecf20Sopenharmony_ci		host->irq_status = 0;
5608c2ecf20Sopenharmony_ci		return -EBADMSG;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	host->irq_status = 0;
5648c2ecf20Sopenharmony_ci	return 0;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic int hisi_nand_write_page_hwecc(struct nand_chip *chip,
5688c2ecf20Sopenharmony_ci				      const uint8_t *buf, int oob_required,
5698c2ecf20Sopenharmony_ci				      int page)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);
5748c2ecf20Sopenharmony_ci	if (oob_required)
5758c2ecf20Sopenharmony_ci		chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return nand_prog_page_end_op(chip);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic void hisi_nfc_host_init(struct hinfc_host *host)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
5838c2ecf20Sopenharmony_ci	unsigned int flag = 0;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	host->version = hinfc_read(host, HINFC_VERSION);
5868c2ecf20Sopenharmony_ci	host->addr_cycle		= 0;
5878c2ecf20Sopenharmony_ci	host->addr_value[0]		= 0;
5888c2ecf20Sopenharmony_ci	host->addr_value[1]		= 0;
5898c2ecf20Sopenharmony_ci	host->cache_addr_value[0]	= ~0;
5908c2ecf20Sopenharmony_ci	host->cache_addr_value[1]	= ~0;
5918c2ecf20Sopenharmony_ci	host->chipselect		= 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	/* default page size: 2K, ecc_none. need modify */
5948c2ecf20Sopenharmony_ci	flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL
5958c2ecf20Sopenharmony_ci		| ((0x001 & HINFC504_CON_PAGESIZE_MASK)
5968c2ecf20Sopenharmony_ci			<< HINFC504_CON_PAGEISZE_SHIFT)
5978c2ecf20Sopenharmony_ci		| ((0x0 & HINFC504_CON_ECCTYPE_MASK)
5988c2ecf20Sopenharmony_ci			<< HINFC504_CON_ECCTYPE_SHIFT)
5998c2ecf20Sopenharmony_ci		| ((chip->options & NAND_BUSWIDTH_16) ?
6008c2ecf20Sopenharmony_ci			HINFC504_CON_BUS_WIDTH : 0);
6018c2ecf20Sopenharmony_ci	hinfc_write(host, flag, HINFC504_CON);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
6068c2ecf20Sopenharmony_ci		    HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* enable DMA irq */
6098c2ecf20Sopenharmony_ci	hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int hisi_ooblayout_ecc(struct mtd_info *mtd, int section,
6138c2ecf20Sopenharmony_ci			      struct mtd_oob_region *oobregion)
6148c2ecf20Sopenharmony_ci{
6158c2ecf20Sopenharmony_ci	/* FIXME: add ECC bytes position */
6168c2ecf20Sopenharmony_ci	return -ENOTSUPP;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int hisi_ooblayout_free(struct mtd_info *mtd, int section,
6208c2ecf20Sopenharmony_ci			       struct mtd_oob_region *oobregion)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	if (section)
6238c2ecf20Sopenharmony_ci		return -ERANGE;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	oobregion->offset = 2;
6268c2ecf20Sopenharmony_ci	oobregion->length = 6;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops hisi_ooblayout_ops = {
6328c2ecf20Sopenharmony_ci	.ecc = hisi_ooblayout_ecc,
6338c2ecf20Sopenharmony_ci	.free = hisi_ooblayout_free,
6348c2ecf20Sopenharmony_ci};
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int hisi_nfc_ecc_probe(struct hinfc_host *host)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	unsigned int flag;
6398c2ecf20Sopenharmony_ci	int size, strength, ecc_bits;
6408c2ecf20Sopenharmony_ci	struct device *dev = host->dev;
6418c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
6428c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	size = chip->ecc.size;
6458c2ecf20Sopenharmony_ci	strength = chip->ecc.strength;
6468c2ecf20Sopenharmony_ci	if (size != 1024) {
6478c2ecf20Sopenharmony_ci		dev_err(dev, "error ecc size: %d\n", size);
6488c2ecf20Sopenharmony_ci		return -EINVAL;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if ((size == 1024) && ((strength != 8) && (strength != 16) &&
6528c2ecf20Sopenharmony_ci				(strength != 24) && (strength != 40))) {
6538c2ecf20Sopenharmony_ci		dev_err(dev, "ecc size and strength do not match\n");
6548c2ecf20Sopenharmony_ci		return -EINVAL;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	chip->ecc.size = size;
6588c2ecf20Sopenharmony_ci	chip->ecc.strength = strength;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	chip->ecc.read_page = hisi_nand_read_page_hwecc;
6618c2ecf20Sopenharmony_ci	chip->ecc.read_oob = hisi_nand_read_oob;
6628c2ecf20Sopenharmony_ci	chip->ecc.write_page = hisi_nand_write_page_hwecc;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	switch (chip->ecc.strength) {
6658c2ecf20Sopenharmony_ci	case 16:
6668c2ecf20Sopenharmony_ci		ecc_bits = 6;
6678c2ecf20Sopenharmony_ci		if (mtd->writesize == 2048)
6688c2ecf20Sopenharmony_ci			mtd_set_ooblayout(mtd, &hisi_ooblayout_ops);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci		/* TODO: add more page size support */
6718c2ecf20Sopenharmony_ci		break;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/* TODO: add more ecc strength support */
6748c2ecf20Sopenharmony_ci	default:
6758c2ecf20Sopenharmony_ci		dev_err(dev, "not support strength: %d\n", chip->ecc.strength);
6768c2ecf20Sopenharmony_ci		return -EINVAL;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	flag = hinfc_read(host, HINFC504_CON);
6808c2ecf20Sopenharmony_ci	/* add ecc type configure */
6818c2ecf20Sopenharmony_ci	flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK)
6828c2ecf20Sopenharmony_ci						<< HINFC504_CON_ECCTYPE_SHIFT);
6838c2ecf20Sopenharmony_ci	hinfc_write(host, flag, HINFC504_CON);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* enable ecc irq */
6868c2ecf20Sopenharmony_ci	flag = hinfc_read(host, HINFC504_INTEN) & 0xfff;
6878c2ecf20Sopenharmony_ci	hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE,
6888c2ecf20Sopenharmony_ci		    HINFC504_INTEN);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return 0;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic int hisi_nfc_attach_chip(struct nand_chip *chip)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6968c2ecf20Sopenharmony_ci	struct hinfc_host *host = nand_get_controller_data(chip);
6978c2ecf20Sopenharmony_ci	int flag;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	host->buffer = dmam_alloc_coherent(host->dev,
7008c2ecf20Sopenharmony_ci					   mtd->writesize + mtd->oobsize,
7018c2ecf20Sopenharmony_ci					   &host->dma_buffer, GFP_KERNEL);
7028c2ecf20Sopenharmony_ci	if (!host->buffer)
7038c2ecf20Sopenharmony_ci		return -ENOMEM;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	host->dma_oob = host->dma_buffer + mtd->writesize;
7068c2ecf20Sopenharmony_ci	memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	flag = hinfc_read(host, HINFC504_CON);
7098c2ecf20Sopenharmony_ci	flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
7108c2ecf20Sopenharmony_ci	switch (mtd->writesize) {
7118c2ecf20Sopenharmony_ci	case 2048:
7128c2ecf20Sopenharmony_ci		flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT);
7138c2ecf20Sopenharmony_ci		break;
7148c2ecf20Sopenharmony_ci	/*
7158c2ecf20Sopenharmony_ci	 * TODO: add more pagesize support,
7168c2ecf20Sopenharmony_ci	 * default pagesize has been set in hisi_nfc_host_init
7178c2ecf20Sopenharmony_ci	 */
7188c2ecf20Sopenharmony_ci	default:
7198c2ecf20Sopenharmony_ci		dev_err(host->dev, "NON-2KB page size nand flash\n");
7208c2ecf20Sopenharmony_ci		return -EINVAL;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci	hinfc_write(host, flag, HINFC504_CON);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
7258c2ecf20Sopenharmony_ci		hisi_nfc_ecc_probe(host);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	return 0;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic const struct nand_controller_ops hisi_nfc_controller_ops = {
7318c2ecf20Sopenharmony_ci	.attach_chip = hisi_nfc_attach_chip,
7328c2ecf20Sopenharmony_ci};
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic int hisi_nfc_probe(struct platform_device *pdev)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	int ret = 0, irq, max_chips = HINFC504_MAX_CHIP;
7378c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
7388c2ecf20Sopenharmony_ci	struct hinfc_host *host;
7398c2ecf20Sopenharmony_ci	struct nand_chip  *chip;
7408c2ecf20Sopenharmony_ci	struct mtd_info   *mtd;
7418c2ecf20Sopenharmony_ci	struct resource	  *res;
7428c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
7458c2ecf20Sopenharmony_ci	if (!host)
7468c2ecf20Sopenharmony_ci		return -ENOMEM;
7478c2ecf20Sopenharmony_ci	host->dev = dev;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, host);
7508c2ecf20Sopenharmony_ci	chip = &host->chip;
7518c2ecf20Sopenharmony_ci	mtd  = nand_to_mtd(chip);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
7548c2ecf20Sopenharmony_ci	if (irq < 0)
7558c2ecf20Sopenharmony_ci		return -ENXIO;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7588c2ecf20Sopenharmony_ci	host->iobase = devm_ioremap_resource(dev, res);
7598c2ecf20Sopenharmony_ci	if (IS_ERR(host->iobase))
7608c2ecf20Sopenharmony_ci		return PTR_ERR(host->iobase);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
7638c2ecf20Sopenharmony_ci	host->mmio = devm_ioremap_resource(dev, res);
7648c2ecf20Sopenharmony_ci	if (IS_ERR(host->mmio)) {
7658c2ecf20Sopenharmony_ci		dev_err(dev, "devm_ioremap_resource[1] fail\n");
7668c2ecf20Sopenharmony_ci		return PTR_ERR(host->mmio);
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	mtd->name		= "hisi_nand";
7708c2ecf20Sopenharmony_ci	mtd->dev.parent         = &pdev->dev;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	nand_set_controller_data(chip, host);
7738c2ecf20Sopenharmony_ci	nand_set_flash_node(chip, np);
7748c2ecf20Sopenharmony_ci	chip->legacy.cmdfunc	= hisi_nfc_cmdfunc;
7758c2ecf20Sopenharmony_ci	chip->legacy.select_chip	= hisi_nfc_select_chip;
7768c2ecf20Sopenharmony_ci	chip->legacy.read_byte	= hisi_nfc_read_byte;
7778c2ecf20Sopenharmony_ci	chip->legacy.write_buf	= hisi_nfc_write_buf;
7788c2ecf20Sopenharmony_ci	chip->legacy.read_buf	= hisi_nfc_read_buf;
7798c2ecf20Sopenharmony_ci	chip->legacy.chip_delay	= HINFC504_CHIP_DELAY;
7808c2ecf20Sopenharmony_ci	chip->legacy.set_features	= nand_get_set_features_notsupp;
7818c2ecf20Sopenharmony_ci	chip->legacy.get_features	= nand_get_set_features_notsupp;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	hisi_nfc_host_init(host);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	ret = devm_request_irq(dev, irq, hinfc_irq_handle, 0x0, "nandc", host);
7868c2ecf20Sopenharmony_ci	if (ret) {
7878c2ecf20Sopenharmony_ci		dev_err(dev, "failed to request IRQ\n");
7888c2ecf20Sopenharmony_ci		return ret;
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	chip->legacy.dummy_controller.ops = &hisi_nfc_controller_ops;
7928c2ecf20Sopenharmony_ci	ret = nand_scan(chip, max_chips);
7938c2ecf20Sopenharmony_ci	if (ret)
7948c2ecf20Sopenharmony_ci		return ret;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
7978c2ecf20Sopenharmony_ci	if (ret) {
7988c2ecf20Sopenharmony_ci		dev_err(dev, "Err MTD partition=%d\n", ret);
7998c2ecf20Sopenharmony_ci		nand_cleanup(chip);
8008c2ecf20Sopenharmony_ci		return ret;
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	return 0;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic int hisi_nfc_remove(struct platform_device *pdev)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct hinfc_host *host = platform_get_drvdata(pdev);
8098c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
8108c2ecf20Sopenharmony_ci	int ret;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
8138c2ecf20Sopenharmony_ci	WARN_ON(ret);
8148c2ecf20Sopenharmony_ci	nand_cleanup(chip);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return 0;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
8208c2ecf20Sopenharmony_cistatic int hisi_nfc_suspend(struct device *dev)
8218c2ecf20Sopenharmony_ci{
8228c2ecf20Sopenharmony_ci	struct hinfc_host *host = dev_get_drvdata(dev);
8238c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout)) {
8268c2ecf20Sopenharmony_ci		if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) &&
8278c2ecf20Sopenharmony_ci		    (hinfc_read(host, HINFC504_DMA_CTRL) &
8288c2ecf20Sopenharmony_ci		     HINFC504_DMA_CTRL_DMA_START)) {
8298c2ecf20Sopenharmony_ci			cond_resched();
8308c2ecf20Sopenharmony_ci			return 0;
8318c2ecf20Sopenharmony_ci		}
8328c2ecf20Sopenharmony_ci	}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	dev_err(host->dev, "nand controller suspend timeout.\n");
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	return -EAGAIN;
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cistatic int hisi_nfc_resume(struct device *dev)
8408c2ecf20Sopenharmony_ci{
8418c2ecf20Sopenharmony_ci	int cs;
8428c2ecf20Sopenharmony_ci	struct hinfc_host *host = dev_get_drvdata(dev);
8438c2ecf20Sopenharmony_ci	struct nand_chip *chip = &host->chip;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	for (cs = 0; cs < nanddev_ntargets(&chip->base); cs++)
8468c2ecf20Sopenharmony_ci		hisi_nfc_send_cmd_reset(host, cs);
8478c2ecf20Sopenharmony_ci	hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
8488c2ecf20Sopenharmony_ci		    HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	return 0;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci#endif
8538c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume);
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic const struct of_device_id nfc_id_table[] = {
8568c2ecf20Sopenharmony_ci	{ .compatible = "hisilicon,504-nfc" },
8578c2ecf20Sopenharmony_ci	{}
8588c2ecf20Sopenharmony_ci};
8598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, nfc_id_table);
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic struct platform_driver hisi_nfc_driver = {
8628c2ecf20Sopenharmony_ci	.driver = {
8638c2ecf20Sopenharmony_ci		.name  = "hisi_nand",
8648c2ecf20Sopenharmony_ci		.of_match_table = nfc_id_table,
8658c2ecf20Sopenharmony_ci		.pm = &hisi_nfc_pm_ops,
8668c2ecf20Sopenharmony_ci	},
8678c2ecf20Sopenharmony_ci	.probe		= hisi_nfc_probe,
8688c2ecf20Sopenharmony_ci	.remove		= hisi_nfc_remove,
8698c2ecf20Sopenharmony_ci};
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cimodule_platform_driver(hisi_nfc_driver);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhou Wang");
8758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhiyong Cai");
8768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver");
877