18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Stefan Agner <stefan@agner.ch>
48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Lucas Stach <dev@lynxeye.de>
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/completion.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h>
178c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
208c2ecf20Sopenharmony_ci#include <linux/reset.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define COMMAND					0x00
238c2ecf20Sopenharmony_ci#define   COMMAND_GO				BIT(31)
248c2ecf20Sopenharmony_ci#define   COMMAND_CLE				BIT(30)
258c2ecf20Sopenharmony_ci#define   COMMAND_ALE				BIT(29)
268c2ecf20Sopenharmony_ci#define   COMMAND_PIO				BIT(28)
278c2ecf20Sopenharmony_ci#define   COMMAND_TX				BIT(27)
288c2ecf20Sopenharmony_ci#define   COMMAND_RX				BIT(26)
298c2ecf20Sopenharmony_ci#define   COMMAND_SEC_CMD			BIT(25)
308c2ecf20Sopenharmony_ci#define   COMMAND_AFT_DAT			BIT(24)
318c2ecf20Sopenharmony_ci#define   COMMAND_TRANS_SIZE(size)		((((size) - 1) & 0xf) << 20)
328c2ecf20Sopenharmony_ci#define   COMMAND_A_VALID			BIT(19)
338c2ecf20Sopenharmony_ci#define   COMMAND_B_VALID			BIT(18)
348c2ecf20Sopenharmony_ci#define   COMMAND_RD_STATUS_CHK			BIT(17)
358c2ecf20Sopenharmony_ci#define   COMMAND_RBSY_CHK			BIT(16)
368c2ecf20Sopenharmony_ci#define   COMMAND_CE(x)				BIT(8 + ((x) & 0x7))
378c2ecf20Sopenharmony_ci#define   COMMAND_CLE_SIZE(size)		((((size) - 1) & 0x3) << 4)
388c2ecf20Sopenharmony_ci#define   COMMAND_ALE_SIZE(size)		((((size) - 1) & 0xf) << 0)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define STATUS					0x04
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define ISR					0x08
438c2ecf20Sopenharmony_ci#define   ISR_CORRFAIL_ERR			BIT(24)
448c2ecf20Sopenharmony_ci#define   ISR_UND				BIT(7)
458c2ecf20Sopenharmony_ci#define   ISR_OVR				BIT(6)
468c2ecf20Sopenharmony_ci#define   ISR_CMD_DONE				BIT(5)
478c2ecf20Sopenharmony_ci#define   ISR_ECC_ERR				BIT(4)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define IER					0x0c
508c2ecf20Sopenharmony_ci#define   IER_ERR_TRIG_VAL(x)			(((x) & 0xf) << 16)
518c2ecf20Sopenharmony_ci#define   IER_UND				BIT(7)
528c2ecf20Sopenharmony_ci#define   IER_OVR				BIT(6)
538c2ecf20Sopenharmony_ci#define   IER_CMD_DONE				BIT(5)
548c2ecf20Sopenharmony_ci#define   IER_ECC_ERR				BIT(4)
558c2ecf20Sopenharmony_ci#define   IER_GIE				BIT(0)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define CONFIG					0x10
588c2ecf20Sopenharmony_ci#define   CONFIG_HW_ECC				BIT(31)
598c2ecf20Sopenharmony_ci#define   CONFIG_ECC_SEL			BIT(30)
608c2ecf20Sopenharmony_ci#define   CONFIG_ERR_COR			BIT(29)
618c2ecf20Sopenharmony_ci#define   CONFIG_PIPE_EN			BIT(28)
628c2ecf20Sopenharmony_ci#define   CONFIG_TVAL_4				(0 << 24)
638c2ecf20Sopenharmony_ci#define   CONFIG_TVAL_6				(1 << 24)
648c2ecf20Sopenharmony_ci#define   CONFIG_TVAL_8				(2 << 24)
658c2ecf20Sopenharmony_ci#define   CONFIG_SKIP_SPARE			BIT(23)
668c2ecf20Sopenharmony_ci#define   CONFIG_BUS_WIDTH_16			BIT(21)
678c2ecf20Sopenharmony_ci#define   CONFIG_COM_BSY			BIT(20)
688c2ecf20Sopenharmony_ci#define   CONFIG_PS_256				(0 << 16)
698c2ecf20Sopenharmony_ci#define   CONFIG_PS_512				(1 << 16)
708c2ecf20Sopenharmony_ci#define   CONFIG_PS_1024			(2 << 16)
718c2ecf20Sopenharmony_ci#define   CONFIG_PS_2048			(3 << 16)
728c2ecf20Sopenharmony_ci#define   CONFIG_PS_4096			(4 << 16)
738c2ecf20Sopenharmony_ci#define   CONFIG_SKIP_SPARE_SIZE_4		(0 << 14)
748c2ecf20Sopenharmony_ci#define   CONFIG_SKIP_SPARE_SIZE_8		(1 << 14)
758c2ecf20Sopenharmony_ci#define   CONFIG_SKIP_SPARE_SIZE_12		(2 << 14)
768c2ecf20Sopenharmony_ci#define   CONFIG_SKIP_SPARE_SIZE_16		(3 << 14)
778c2ecf20Sopenharmony_ci#define   CONFIG_TAG_BYTE_SIZE(x)			((x) & 0xff)
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define TIMING_1				0x14
808c2ecf20Sopenharmony_ci#define   TIMING_TRP_RESP(x)			(((x) & 0xf) << 28)
818c2ecf20Sopenharmony_ci#define   TIMING_TWB(x)				(((x) & 0xf) << 24)
828c2ecf20Sopenharmony_ci#define   TIMING_TCR_TAR_TRR(x)			(((x) & 0xf) << 20)
838c2ecf20Sopenharmony_ci#define   TIMING_TWHR(x)			(((x) & 0xf) << 16)
848c2ecf20Sopenharmony_ci#define   TIMING_TCS(x)				(((x) & 0x3) << 14)
858c2ecf20Sopenharmony_ci#define   TIMING_TWH(x)				(((x) & 0x3) << 12)
868c2ecf20Sopenharmony_ci#define   TIMING_TWP(x)				(((x) & 0xf) <<  8)
878c2ecf20Sopenharmony_ci#define   TIMING_TRH(x)				(((x) & 0x3) <<  4)
888c2ecf20Sopenharmony_ci#define   TIMING_TRP(x)				(((x) & 0xf) <<  0)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define RESP					0x18
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define TIMING_2				0x1c
938c2ecf20Sopenharmony_ci#define   TIMING_TADL(x)			((x) & 0xf)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define CMD_REG1				0x20
968c2ecf20Sopenharmony_ci#define CMD_REG2				0x24
978c2ecf20Sopenharmony_ci#define ADDR_REG1				0x28
988c2ecf20Sopenharmony_ci#define ADDR_REG2				0x2c
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci#define DMA_MST_CTRL				0x30
1018c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_GO			BIT(31)
1028c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_IN			(0 << 30)
1038c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_OUT			BIT(30)
1048c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_PERF_EN			BIT(29)
1058c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_IE_DONE			BIT(28)
1068c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_REUSE			BIT(27)
1078c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_BURST_1			(2 << 24)
1088c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_BURST_4			(3 << 24)
1098c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_BURST_8			(4 << 24)
1108c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_BURST_16			(5 << 24)
1118c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_IS_DONE			BIT(20)
1128c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_EN_A			BIT(2)
1138c2ecf20Sopenharmony_ci#define   DMA_MST_CTRL_EN_B			BIT(1)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define DMA_CFG_A				0x34
1168c2ecf20Sopenharmony_ci#define DMA_CFG_B				0x38
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define FIFO_CTRL				0x3c
1198c2ecf20Sopenharmony_ci#define   FIFO_CTRL_CLR_ALL			BIT(3)
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define DATA_PTR				0x40
1228c2ecf20Sopenharmony_ci#define TAG_PTR					0x44
1238c2ecf20Sopenharmony_ci#define ECC_PTR					0x48
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#define DEC_STATUS				0x4c
1268c2ecf20Sopenharmony_ci#define   DEC_STATUS_A_ECC_FAIL			BIT(1)
1278c2ecf20Sopenharmony_ci#define   DEC_STATUS_ERR_COUNT_MASK		0x00ff0000
1288c2ecf20Sopenharmony_ci#define   DEC_STATUS_ERR_COUNT_SHIFT		16
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define HWSTATUS_CMD				0x50
1318c2ecf20Sopenharmony_ci#define HWSTATUS_MASK				0x54
1328c2ecf20Sopenharmony_ci#define   HWSTATUS_RDSTATUS_MASK(x)		(((x) & 0xff) << 24)
1338c2ecf20Sopenharmony_ci#define   HWSTATUS_RDSTATUS_VALUE(x)		(((x) & 0xff) << 16)
1348c2ecf20Sopenharmony_ci#define   HWSTATUS_RBSY_MASK(x)			(((x) & 0xff) << 8)
1358c2ecf20Sopenharmony_ci#define   HWSTATUS_RBSY_VALUE(x)		(((x) & 0xff) << 0)
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#define BCH_CONFIG				0xcc
1388c2ecf20Sopenharmony_ci#define   BCH_ENABLE				BIT(0)
1398c2ecf20Sopenharmony_ci#define   BCH_TVAL_4				(0 << 4)
1408c2ecf20Sopenharmony_ci#define   BCH_TVAL_8				(1 << 4)
1418c2ecf20Sopenharmony_ci#define   BCH_TVAL_14				(2 << 4)
1428c2ecf20Sopenharmony_ci#define   BCH_TVAL_16				(3 << 4)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#define DEC_STAT_RESULT				0xd0
1458c2ecf20Sopenharmony_ci#define DEC_STAT_BUF				0xd4
1468c2ecf20Sopenharmony_ci#define   DEC_STAT_BUF_FAIL_SEC_FLAG_MASK	0xff000000
1478c2ecf20Sopenharmony_ci#define   DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT	24
1488c2ecf20Sopenharmony_ci#define   DEC_STAT_BUF_CORR_SEC_FLAG_MASK	0x00ff0000
1498c2ecf20Sopenharmony_ci#define   DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT	16
1508c2ecf20Sopenharmony_ci#define   DEC_STAT_BUF_MAX_CORR_CNT_MASK	0x00001f00
1518c2ecf20Sopenharmony_ci#define   DEC_STAT_BUF_MAX_CORR_CNT_SHIFT	8
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci#define OFFSET(val, off)	((val) < (off) ? 0 : (val) - (off))
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci#define SKIP_SPARE_BYTES	4
1568c2ecf20Sopenharmony_ci#define BITS_PER_STEP_RS	18
1578c2ecf20Sopenharmony_ci#define BITS_PER_STEP_BCH	13
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci#define INT_MASK		(IER_UND | IER_OVR | IER_CMD_DONE | IER_GIE)
1608c2ecf20Sopenharmony_ci#define HWSTATUS_CMD_DEFAULT	NAND_STATUS_READY
1618c2ecf20Sopenharmony_ci#define HWSTATUS_MASK_DEFAULT	(HWSTATUS_RDSTATUS_MASK(1) | \
1628c2ecf20Sopenharmony_ci				HWSTATUS_RDSTATUS_VALUE(0) | \
1638c2ecf20Sopenharmony_ci				HWSTATUS_RBSY_MASK(NAND_STATUS_READY) | \
1648c2ecf20Sopenharmony_ci				HWSTATUS_RBSY_VALUE(NAND_STATUS_READY))
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistruct tegra_nand_controller {
1678c2ecf20Sopenharmony_ci	struct nand_controller controller;
1688c2ecf20Sopenharmony_ci	struct device *dev;
1698c2ecf20Sopenharmony_ci	void __iomem *regs;
1708c2ecf20Sopenharmony_ci	int irq;
1718c2ecf20Sopenharmony_ci	struct clk *clk;
1728c2ecf20Sopenharmony_ci	struct completion command_complete;
1738c2ecf20Sopenharmony_ci	struct completion dma_complete;
1748c2ecf20Sopenharmony_ci	bool last_read_error;
1758c2ecf20Sopenharmony_ci	int cur_cs;
1768c2ecf20Sopenharmony_ci	struct nand_chip *chip;
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistruct tegra_nand_chip {
1808c2ecf20Sopenharmony_ci	struct nand_chip chip;
1818c2ecf20Sopenharmony_ci	struct gpio_desc *wp_gpio;
1828c2ecf20Sopenharmony_ci	struct mtd_oob_region ecc;
1838c2ecf20Sopenharmony_ci	u32 config;
1848c2ecf20Sopenharmony_ci	u32 config_ecc;
1858c2ecf20Sopenharmony_ci	u32 bch_config;
1868c2ecf20Sopenharmony_ci	int cs[1];
1878c2ecf20Sopenharmony_ci};
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic inline struct tegra_nand_controller *
1908c2ecf20Sopenharmony_ci			to_tegra_ctrl(struct nand_controller *hw_ctrl)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	return container_of(hw_ctrl, struct tegra_nand_controller, controller);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic inline struct tegra_nand_chip *to_tegra_chip(struct nand_chip *chip)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	return container_of(chip, struct tegra_nand_chip, chip);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int tegra_nand_ooblayout_rs_ecc(struct mtd_info *mtd, int section,
2018c2ecf20Sopenharmony_ci				       struct mtd_oob_region *oobregion)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
2048c2ecf20Sopenharmony_ci	int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_RS * chip->ecc.strength,
2058c2ecf20Sopenharmony_ci					  BITS_PER_BYTE);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (section > 0)
2088c2ecf20Sopenharmony_ci		return -ERANGE;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	oobregion->offset = SKIP_SPARE_BYTES;
2118c2ecf20Sopenharmony_ci	oobregion->length = round_up(bytes_per_step * chip->ecc.steps, 4);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int tegra_nand_ooblayout_no_free(struct mtd_info *mtd, int section,
2178c2ecf20Sopenharmony_ci					struct mtd_oob_region *oobregion)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	return -ERANGE;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops tegra_nand_oob_rs_ops = {
2238c2ecf20Sopenharmony_ci	.ecc = tegra_nand_ooblayout_rs_ecc,
2248c2ecf20Sopenharmony_ci	.free = tegra_nand_ooblayout_no_free,
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int tegra_nand_ooblayout_bch_ecc(struct mtd_info *mtd, int section,
2288c2ecf20Sopenharmony_ci					struct mtd_oob_region *oobregion)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
2318c2ecf20Sopenharmony_ci	int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_BCH * chip->ecc.strength,
2328c2ecf20Sopenharmony_ci					  BITS_PER_BYTE);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (section > 0)
2358c2ecf20Sopenharmony_ci		return -ERANGE;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	oobregion->offset = SKIP_SPARE_BYTES;
2388c2ecf20Sopenharmony_ci	oobregion->length = round_up(bytes_per_step * chip->ecc.steps, 4);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return 0;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops tegra_nand_oob_bch_ops = {
2448c2ecf20Sopenharmony_ci	.ecc = tegra_nand_ooblayout_bch_ecc,
2458c2ecf20Sopenharmony_ci	.free = tegra_nand_ooblayout_no_free,
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic irqreturn_t tegra_nand_irq(int irq, void *data)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = data;
2518c2ecf20Sopenharmony_ci	u32 isr, dma;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	isr = readl_relaxed(ctrl->regs + ISR);
2548c2ecf20Sopenharmony_ci	dma = readl_relaxed(ctrl->regs + DMA_MST_CTRL);
2558c2ecf20Sopenharmony_ci	dev_dbg(ctrl->dev, "isr %08x\n", isr);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (!isr && !(dma & DMA_MST_CTRL_IS_DONE))
2588c2ecf20Sopenharmony_ci		return IRQ_NONE;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/*
2618c2ecf20Sopenharmony_ci	 * The bit name is somewhat missleading: This is also set when
2628c2ecf20Sopenharmony_ci	 * HW ECC was successful. The data sheet states:
2638c2ecf20Sopenharmony_ci	 * Correctable OR Un-correctable errors occurred in the DMA transfer...
2648c2ecf20Sopenharmony_ci	 */
2658c2ecf20Sopenharmony_ci	if (isr & ISR_CORRFAIL_ERR)
2668c2ecf20Sopenharmony_ci		ctrl->last_read_error = true;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (isr & ISR_CMD_DONE)
2698c2ecf20Sopenharmony_ci		complete(&ctrl->command_complete);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (isr & ISR_UND)
2728c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "FIFO underrun\n");
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (isr & ISR_OVR)
2758c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "FIFO overrun\n");
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* handle DMA interrupts */
2788c2ecf20Sopenharmony_ci	if (dma & DMA_MST_CTRL_IS_DONE) {
2798c2ecf20Sopenharmony_ci		writel_relaxed(dma, ctrl->regs + DMA_MST_CTRL);
2808c2ecf20Sopenharmony_ci		complete(&ctrl->dma_complete);
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* clear interrupts */
2848c2ecf20Sopenharmony_ci	writel_relaxed(isr, ctrl->regs + ISR);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic const char * const tegra_nand_reg_names[] = {
2908c2ecf20Sopenharmony_ci	"COMMAND",
2918c2ecf20Sopenharmony_ci	"STATUS",
2928c2ecf20Sopenharmony_ci	"ISR",
2938c2ecf20Sopenharmony_ci	"IER",
2948c2ecf20Sopenharmony_ci	"CONFIG",
2958c2ecf20Sopenharmony_ci	"TIMING",
2968c2ecf20Sopenharmony_ci	NULL,
2978c2ecf20Sopenharmony_ci	"TIMING2",
2988c2ecf20Sopenharmony_ci	"CMD_REG1",
2998c2ecf20Sopenharmony_ci	"CMD_REG2",
3008c2ecf20Sopenharmony_ci	"ADDR_REG1",
3018c2ecf20Sopenharmony_ci	"ADDR_REG2",
3028c2ecf20Sopenharmony_ci	"DMA_MST_CTRL",
3038c2ecf20Sopenharmony_ci	"DMA_CFG_A",
3048c2ecf20Sopenharmony_ci	"DMA_CFG_B",
3058c2ecf20Sopenharmony_ci	"FIFO_CTRL",
3068c2ecf20Sopenharmony_ci};
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void tegra_nand_dump_reg(struct tegra_nand_controller *ctrl)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	u32 reg;
3118c2ecf20Sopenharmony_ci	int i;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	dev_err(ctrl->dev, "Tegra NAND controller register dump\n");
3148c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tegra_nand_reg_names); i++) {
3158c2ecf20Sopenharmony_ci		const char *reg_name = tegra_nand_reg_names[i];
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		if (!reg_name)
3188c2ecf20Sopenharmony_ci			continue;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		reg = readl_relaxed(ctrl->regs + (i * 4));
3218c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "%s: 0x%08x\n", reg_name, reg);
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void tegra_nand_controller_abort(struct tegra_nand_controller *ctrl)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	u32 isr, dma;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	disable_irq(ctrl->irq);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Abort current command/DMA operation */
3328c2ecf20Sopenharmony_ci	writel_relaxed(0, ctrl->regs + DMA_MST_CTRL);
3338c2ecf20Sopenharmony_ci	writel_relaxed(0, ctrl->regs + COMMAND);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* clear interrupts */
3368c2ecf20Sopenharmony_ci	isr = readl_relaxed(ctrl->regs + ISR);
3378c2ecf20Sopenharmony_ci	writel_relaxed(isr, ctrl->regs + ISR);
3388c2ecf20Sopenharmony_ci	dma = readl_relaxed(ctrl->regs + DMA_MST_CTRL);
3398c2ecf20Sopenharmony_ci	writel_relaxed(dma, ctrl->regs + DMA_MST_CTRL);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	reinit_completion(&ctrl->command_complete);
3428c2ecf20Sopenharmony_ci	reinit_completion(&ctrl->dma_complete);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	enable_irq(ctrl->irq);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int tegra_nand_cmd(struct nand_chip *chip,
3488c2ecf20Sopenharmony_ci			  const struct nand_subop *subop)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	const struct nand_op_instr *instr;
3518c2ecf20Sopenharmony_ci	const struct nand_op_instr *instr_data_in = NULL;
3528c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
3538c2ecf20Sopenharmony_ci	unsigned int op_id, size = 0, offset = 0;
3548c2ecf20Sopenharmony_ci	bool first_cmd = true;
3558c2ecf20Sopenharmony_ci	u32 reg, cmd = 0;
3568c2ecf20Sopenharmony_ci	int ret;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	for (op_id = 0; op_id < subop->ninstrs; op_id++) {
3598c2ecf20Sopenharmony_ci		unsigned int naddrs, i;
3608c2ecf20Sopenharmony_ci		const u8 *addrs;
3618c2ecf20Sopenharmony_ci		u32 addr1 = 0, addr2 = 0;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci		instr = &subop->instrs[op_id];
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		switch (instr->type) {
3668c2ecf20Sopenharmony_ci		case NAND_OP_CMD_INSTR:
3678c2ecf20Sopenharmony_ci			if (first_cmd) {
3688c2ecf20Sopenharmony_ci				cmd |= COMMAND_CLE;
3698c2ecf20Sopenharmony_ci				writel_relaxed(instr->ctx.cmd.opcode,
3708c2ecf20Sopenharmony_ci					       ctrl->regs + CMD_REG1);
3718c2ecf20Sopenharmony_ci			} else {
3728c2ecf20Sopenharmony_ci				cmd |= COMMAND_SEC_CMD;
3738c2ecf20Sopenharmony_ci				writel_relaxed(instr->ctx.cmd.opcode,
3748c2ecf20Sopenharmony_ci					       ctrl->regs + CMD_REG2);
3758c2ecf20Sopenharmony_ci			}
3768c2ecf20Sopenharmony_ci			first_cmd = false;
3778c2ecf20Sopenharmony_ci			break;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		case NAND_OP_ADDR_INSTR:
3808c2ecf20Sopenharmony_ci			offset = nand_subop_get_addr_start_off(subop, op_id);
3818c2ecf20Sopenharmony_ci			naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
3828c2ecf20Sopenharmony_ci			addrs = &instr->ctx.addr.addrs[offset];
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci			cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(naddrs);
3858c2ecf20Sopenharmony_ci			for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
3868c2ecf20Sopenharmony_ci				addr1 |= *addrs++ << (BITS_PER_BYTE * i);
3878c2ecf20Sopenharmony_ci			naddrs -= i;
3888c2ecf20Sopenharmony_ci			for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
3898c2ecf20Sopenharmony_ci				addr2 |= *addrs++ << (BITS_PER_BYTE * i);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci			writel_relaxed(addr1, ctrl->regs + ADDR_REG1);
3928c2ecf20Sopenharmony_ci			writel_relaxed(addr2, ctrl->regs + ADDR_REG2);
3938c2ecf20Sopenharmony_ci			break;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		case NAND_OP_DATA_IN_INSTR:
3968c2ecf20Sopenharmony_ci			size = nand_subop_get_data_len(subop, op_id);
3978c2ecf20Sopenharmony_ci			offset = nand_subop_get_data_start_off(subop, op_id);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci			cmd |= COMMAND_TRANS_SIZE(size) | COMMAND_PIO |
4008c2ecf20Sopenharmony_ci				COMMAND_RX | COMMAND_A_VALID;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci			instr_data_in = instr;
4038c2ecf20Sopenharmony_ci			break;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		case NAND_OP_DATA_OUT_INSTR:
4068c2ecf20Sopenharmony_ci			size = nand_subop_get_data_len(subop, op_id);
4078c2ecf20Sopenharmony_ci			offset = nand_subop_get_data_start_off(subop, op_id);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			cmd |= COMMAND_TRANS_SIZE(size) | COMMAND_PIO |
4108c2ecf20Sopenharmony_ci				COMMAND_TX | COMMAND_A_VALID;
4118c2ecf20Sopenharmony_ci			memcpy(&reg, instr->ctx.data.buf.out + offset, size);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci			writel_relaxed(reg, ctrl->regs + RESP);
4148c2ecf20Sopenharmony_ci			break;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		case NAND_OP_WAITRDY_INSTR:
4178c2ecf20Sopenharmony_ci			cmd |= COMMAND_RBSY_CHK;
4188c2ecf20Sopenharmony_ci			break;
4198c2ecf20Sopenharmony_ci		}
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	cmd |= COMMAND_GO | COMMAND_CE(ctrl->cur_cs);
4238c2ecf20Sopenharmony_ci	writel_relaxed(cmd, ctrl->regs + COMMAND);
4248c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&ctrl->command_complete,
4258c2ecf20Sopenharmony_ci					  msecs_to_jiffies(500));
4268c2ecf20Sopenharmony_ci	if (!ret) {
4278c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "COMMAND timeout\n");
4288c2ecf20Sopenharmony_ci		tegra_nand_dump_reg(ctrl);
4298c2ecf20Sopenharmony_ci		tegra_nand_controller_abort(ctrl);
4308c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (instr_data_in) {
4348c2ecf20Sopenharmony_ci		reg = readl_relaxed(ctrl->regs + RESP);
4358c2ecf20Sopenharmony_ci		memcpy(instr_data_in->ctx.data.buf.in + offset, &reg, size);
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	return 0;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cistatic const struct nand_op_parser tegra_nand_op_parser = NAND_OP_PARSER(
4428c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
4438c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_CMD_ELEM(true),
4448c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
4458c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_CMD_ELEM(true),
4468c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
4478c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
4488c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 4)),
4498c2ecf20Sopenharmony_ci	NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
4508c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_CMD_ELEM(true),
4518c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
4528c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_CMD_ELEM(true),
4538c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
4548c2ecf20Sopenharmony_ci		NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)),
4558c2ecf20Sopenharmony_ci	);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void tegra_nand_select_target(struct nand_chip *chip,
4588c2ecf20Sopenharmony_ci				     unsigned int die_nr)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct tegra_nand_chip *nand = to_tegra_chip(chip);
4618c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	ctrl->cur_cs = nand->cs[die_nr];
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int tegra_nand_exec_op(struct nand_chip *chip,
4678c2ecf20Sopenharmony_ci			      const struct nand_operation *op,
4688c2ecf20Sopenharmony_ci			      bool check_only)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	if (!check_only)
4718c2ecf20Sopenharmony_ci		tegra_nand_select_target(chip, op->cs);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	return nand_op_parser_exec_op(chip, &tegra_nand_op_parser, op,
4748c2ecf20Sopenharmony_ci				      check_only);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl,
4788c2ecf20Sopenharmony_ci			      struct nand_chip *chip, bool enable)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct tegra_nand_chip *nand = to_tegra_chip(chip);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	if (chip->ecc.algo == NAND_ECC_ALGO_BCH && enable)
4838c2ecf20Sopenharmony_ci		writel_relaxed(nand->bch_config, ctrl->regs + BCH_CONFIG);
4848c2ecf20Sopenharmony_ci	else
4858c2ecf20Sopenharmony_ci		writel_relaxed(0, ctrl->regs + BCH_CONFIG);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	if (enable)
4888c2ecf20Sopenharmony_ci		writel_relaxed(nand->config_ecc, ctrl->regs + CONFIG);
4898c2ecf20Sopenharmony_ci	else
4908c2ecf20Sopenharmony_ci		writel_relaxed(nand->config, ctrl->regs + CONFIG);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int tegra_nand_page_xfer(struct mtd_info *mtd, struct nand_chip *chip,
4948c2ecf20Sopenharmony_ci				void *buf, void *oob_buf, int oob_len, int page,
4958c2ecf20Sopenharmony_ci				bool read)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
4988c2ecf20Sopenharmony_ci	enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
4998c2ecf20Sopenharmony_ci	dma_addr_t dma_addr = 0, dma_addr_oob = 0;
5008c2ecf20Sopenharmony_ci	u32 addr1, cmd, dma_ctrl;
5018c2ecf20Sopenharmony_ci	int ret;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	tegra_nand_select_target(chip, chip->cur_cs);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (read) {
5068c2ecf20Sopenharmony_ci		writel_relaxed(NAND_CMD_READ0, ctrl->regs + CMD_REG1);
5078c2ecf20Sopenharmony_ci		writel_relaxed(NAND_CMD_READSTART, ctrl->regs + CMD_REG2);
5088c2ecf20Sopenharmony_ci	} else {
5098c2ecf20Sopenharmony_ci		writel_relaxed(NAND_CMD_SEQIN, ctrl->regs + CMD_REG1);
5108c2ecf20Sopenharmony_ci		writel_relaxed(NAND_CMD_PAGEPROG, ctrl->regs + CMD_REG2);
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci	cmd = COMMAND_CLE | COMMAND_SEC_CMD;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* Lower 16-bits are column, by default 0 */
5158c2ecf20Sopenharmony_ci	addr1 = page << 16;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (!buf)
5188c2ecf20Sopenharmony_ci		addr1 |= mtd->writesize;
5198c2ecf20Sopenharmony_ci	writel_relaxed(addr1, ctrl->regs + ADDR_REG1);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (chip->options & NAND_ROW_ADDR_3) {
5228c2ecf20Sopenharmony_ci		writel_relaxed(page >> 16, ctrl->regs + ADDR_REG2);
5238c2ecf20Sopenharmony_ci		cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(5);
5248c2ecf20Sopenharmony_ci	} else {
5258c2ecf20Sopenharmony_ci		cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(4);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	if (buf) {
5298c2ecf20Sopenharmony_ci		dma_addr = dma_map_single(ctrl->dev, buf, mtd->writesize, dir);
5308c2ecf20Sopenharmony_ci		ret = dma_mapping_error(ctrl->dev, dma_addr);
5318c2ecf20Sopenharmony_ci		if (ret) {
5328c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "dma mapping error\n");
5338c2ecf20Sopenharmony_ci			return -EINVAL;
5348c2ecf20Sopenharmony_ci		}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		writel_relaxed(mtd->writesize - 1, ctrl->regs + DMA_CFG_A);
5378c2ecf20Sopenharmony_ci		writel_relaxed(dma_addr, ctrl->regs + DATA_PTR);
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (oob_buf) {
5418c2ecf20Sopenharmony_ci		dma_addr_oob = dma_map_single(ctrl->dev, oob_buf, mtd->oobsize,
5428c2ecf20Sopenharmony_ci					      dir);
5438c2ecf20Sopenharmony_ci		ret = dma_mapping_error(ctrl->dev, dma_addr_oob);
5448c2ecf20Sopenharmony_ci		if (ret) {
5458c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "dma mapping error\n");
5468c2ecf20Sopenharmony_ci			ret = -EINVAL;
5478c2ecf20Sopenharmony_ci			goto err_unmap_dma_page;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		writel_relaxed(oob_len - 1, ctrl->regs + DMA_CFG_B);
5518c2ecf20Sopenharmony_ci		writel_relaxed(dma_addr_oob, ctrl->regs + TAG_PTR);
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	dma_ctrl = DMA_MST_CTRL_GO | DMA_MST_CTRL_PERF_EN |
5558c2ecf20Sopenharmony_ci		   DMA_MST_CTRL_IE_DONE | DMA_MST_CTRL_IS_DONE |
5568c2ecf20Sopenharmony_ci		   DMA_MST_CTRL_BURST_16;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	if (buf)
5598c2ecf20Sopenharmony_ci		dma_ctrl |= DMA_MST_CTRL_EN_A;
5608c2ecf20Sopenharmony_ci	if (oob_buf)
5618c2ecf20Sopenharmony_ci		dma_ctrl |= DMA_MST_CTRL_EN_B;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (read)
5648c2ecf20Sopenharmony_ci		dma_ctrl |= DMA_MST_CTRL_IN | DMA_MST_CTRL_REUSE;
5658c2ecf20Sopenharmony_ci	else
5668c2ecf20Sopenharmony_ci		dma_ctrl |= DMA_MST_CTRL_OUT;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	writel_relaxed(dma_ctrl, ctrl->regs + DMA_MST_CTRL);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	cmd |= COMMAND_GO | COMMAND_RBSY_CHK | COMMAND_TRANS_SIZE(9) |
5718c2ecf20Sopenharmony_ci	       COMMAND_CE(ctrl->cur_cs);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if (buf)
5748c2ecf20Sopenharmony_ci		cmd |= COMMAND_A_VALID;
5758c2ecf20Sopenharmony_ci	if (oob_buf)
5768c2ecf20Sopenharmony_ci		cmd |= COMMAND_B_VALID;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (read)
5798c2ecf20Sopenharmony_ci		cmd |= COMMAND_RX;
5808c2ecf20Sopenharmony_ci	else
5818c2ecf20Sopenharmony_ci		cmd |= COMMAND_TX | COMMAND_AFT_DAT;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	writel_relaxed(cmd, ctrl->regs + COMMAND);
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&ctrl->command_complete,
5868c2ecf20Sopenharmony_ci					  msecs_to_jiffies(500));
5878c2ecf20Sopenharmony_ci	if (!ret) {
5888c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "COMMAND timeout\n");
5898c2ecf20Sopenharmony_ci		tegra_nand_dump_reg(ctrl);
5908c2ecf20Sopenharmony_ci		tegra_nand_controller_abort(ctrl);
5918c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
5928c2ecf20Sopenharmony_ci		goto err_unmap_dma;
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&ctrl->dma_complete,
5968c2ecf20Sopenharmony_ci					  msecs_to_jiffies(500));
5978c2ecf20Sopenharmony_ci	if (!ret) {
5988c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "DMA timeout\n");
5998c2ecf20Sopenharmony_ci		tegra_nand_dump_reg(ctrl);
6008c2ecf20Sopenharmony_ci		tegra_nand_controller_abort(ctrl);
6018c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
6028c2ecf20Sopenharmony_ci		goto err_unmap_dma;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci	ret = 0;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cierr_unmap_dma:
6078c2ecf20Sopenharmony_ci	if (oob_buf)
6088c2ecf20Sopenharmony_ci		dma_unmap_single(ctrl->dev, dma_addr_oob, mtd->oobsize, dir);
6098c2ecf20Sopenharmony_cierr_unmap_dma_page:
6108c2ecf20Sopenharmony_ci	if (buf)
6118c2ecf20Sopenharmony_ci		dma_unmap_single(ctrl->dev, dma_addr, mtd->writesize, dir);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return ret;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int tegra_nand_read_page_raw(struct nand_chip *chip, u8 *buf,
6178c2ecf20Sopenharmony_ci				    int oob_required, int page)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6208c2ecf20Sopenharmony_ci	void *oob_buf = oob_required ? chip->oob_poi : NULL;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	return tegra_nand_page_xfer(mtd, chip, buf, oob_buf,
6238c2ecf20Sopenharmony_ci				    mtd->oobsize, page, true);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic int tegra_nand_write_page_raw(struct nand_chip *chip, const u8 *buf,
6278c2ecf20Sopenharmony_ci				     int oob_required, int page)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6308c2ecf20Sopenharmony_ci	void *oob_buf = oob_required ? chip->oob_poi : NULL;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	return tegra_nand_page_xfer(mtd, chip, (void *)buf, oob_buf,
6338c2ecf20Sopenharmony_ci				     mtd->oobsize, page, false);
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic int tegra_nand_read_oob(struct nand_chip *chip, int page)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	return tegra_nand_page_xfer(mtd, chip, NULL, chip->oob_poi,
6418c2ecf20Sopenharmony_ci				    mtd->oobsize, page, true);
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic int tegra_nand_write_oob(struct nand_chip *chip, int page)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return tegra_nand_page_xfer(mtd, chip, NULL, chip->oob_poi,
6498c2ecf20Sopenharmony_ci				    mtd->oobsize, page, false);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_cistatic int tegra_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
6538c2ecf20Sopenharmony_ci				      int oob_required, int page)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
6568c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
6578c2ecf20Sopenharmony_ci	struct tegra_nand_chip *nand = to_tegra_chip(chip);
6588c2ecf20Sopenharmony_ci	void *oob_buf = oob_required ? chip->oob_poi : NULL;
6598c2ecf20Sopenharmony_ci	u32 dec_stat, max_corr_cnt;
6608c2ecf20Sopenharmony_ci	unsigned long fail_sec_flag;
6618c2ecf20Sopenharmony_ci	int ret;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	tegra_nand_hw_ecc(ctrl, chip, true);
6648c2ecf20Sopenharmony_ci	ret = tegra_nand_page_xfer(mtd, chip, buf, oob_buf, 0, page, true);
6658c2ecf20Sopenharmony_ci	tegra_nand_hw_ecc(ctrl, chip, false);
6668c2ecf20Sopenharmony_ci	if (ret)
6678c2ecf20Sopenharmony_ci		return ret;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/* No correctable or un-correctable errors, page must have 0 bitflips */
6708c2ecf20Sopenharmony_ci	if (!ctrl->last_read_error)
6718c2ecf20Sopenharmony_ci		return 0;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	/*
6748c2ecf20Sopenharmony_ci	 * Correctable or un-correctable errors occurred. Use DEC_STAT_BUF
6758c2ecf20Sopenharmony_ci	 * which contains information for all ECC selections.
6768c2ecf20Sopenharmony_ci	 *
6778c2ecf20Sopenharmony_ci	 * Note that since we do not use Command Queues DEC_RESULT does not
6788c2ecf20Sopenharmony_ci	 * state the number of pages we can read from the DEC_STAT_BUF. But
6798c2ecf20Sopenharmony_ci	 * since CORRFAIL_ERR did occur during page read we do have a valid
6808c2ecf20Sopenharmony_ci	 * result in DEC_STAT_BUF.
6818c2ecf20Sopenharmony_ci	 */
6828c2ecf20Sopenharmony_ci	ctrl->last_read_error = false;
6838c2ecf20Sopenharmony_ci	dec_stat = readl_relaxed(ctrl->regs + DEC_STAT_BUF);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	fail_sec_flag = (dec_stat & DEC_STAT_BUF_FAIL_SEC_FLAG_MASK) >>
6868c2ecf20Sopenharmony_ci			DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	max_corr_cnt = (dec_stat & DEC_STAT_BUF_MAX_CORR_CNT_MASK) >>
6898c2ecf20Sopenharmony_ci		       DEC_STAT_BUF_MAX_CORR_CNT_SHIFT;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (fail_sec_flag) {
6928c2ecf20Sopenharmony_ci		int bit, max_bitflips = 0;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci		/*
6958c2ecf20Sopenharmony_ci		 * Since we do not support subpage writes, a complete page
6968c2ecf20Sopenharmony_ci		 * is either written or not. We can take a shortcut here by
6978c2ecf20Sopenharmony_ci		 * checking wheather any of the sector has been successful
6988c2ecf20Sopenharmony_ci		 * read. If at least one sectors has been read successfully,
6998c2ecf20Sopenharmony_ci		 * the page must have been a written previously. It cannot
7008c2ecf20Sopenharmony_ci		 * be an erased page.
7018c2ecf20Sopenharmony_ci		 *
7028c2ecf20Sopenharmony_ci		 * E.g. controller might return fail_sec_flag with 0x4, which
7038c2ecf20Sopenharmony_ci		 * would mean only the third sector failed to correct. The
7048c2ecf20Sopenharmony_ci		 * page must have been written and the third sector is really
7058c2ecf20Sopenharmony_ci		 * not correctable anymore.
7068c2ecf20Sopenharmony_ci		 */
7078c2ecf20Sopenharmony_ci		if (fail_sec_flag ^ GENMASK(chip->ecc.steps - 1, 0)) {
7088c2ecf20Sopenharmony_ci			mtd->ecc_stats.failed += hweight8(fail_sec_flag);
7098c2ecf20Sopenharmony_ci			return max_corr_cnt;
7108c2ecf20Sopenharmony_ci		}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		/*
7138c2ecf20Sopenharmony_ci		 * All sectors failed to correct, but the ECC isn't smart
7148c2ecf20Sopenharmony_ci		 * enough to figure out if a page is really just erased.
7158c2ecf20Sopenharmony_ci		 * Read OOB data and check whether data/OOB is completely
7168c2ecf20Sopenharmony_ci		 * erased or if error correction just failed for all sub-
7178c2ecf20Sopenharmony_ci		 * pages.
7188c2ecf20Sopenharmony_ci		 */
7198c2ecf20Sopenharmony_ci		ret = tegra_nand_read_oob(chip, page);
7208c2ecf20Sopenharmony_ci		if (ret < 0)
7218c2ecf20Sopenharmony_ci			return ret;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci		for_each_set_bit(bit, &fail_sec_flag, chip->ecc.steps) {
7248c2ecf20Sopenharmony_ci			u8 *data = buf + (chip->ecc.size * bit);
7258c2ecf20Sopenharmony_ci			u8 *oob = chip->oob_poi + nand->ecc.offset +
7268c2ecf20Sopenharmony_ci				  (chip->ecc.bytes * bit);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci			ret = nand_check_erased_ecc_chunk(data, chip->ecc.size,
7298c2ecf20Sopenharmony_ci							  oob, chip->ecc.bytes,
7308c2ecf20Sopenharmony_ci							  NULL, 0,
7318c2ecf20Sopenharmony_ci							  chip->ecc.strength);
7328c2ecf20Sopenharmony_ci			if (ret < 0) {
7338c2ecf20Sopenharmony_ci				mtd->ecc_stats.failed++;
7348c2ecf20Sopenharmony_ci			} else {
7358c2ecf20Sopenharmony_ci				mtd->ecc_stats.corrected += ret;
7368c2ecf20Sopenharmony_ci				max_bitflips = max(ret, max_bitflips);
7378c2ecf20Sopenharmony_ci			}
7388c2ecf20Sopenharmony_ci		}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		return max_t(unsigned int, max_corr_cnt, max_bitflips);
7418c2ecf20Sopenharmony_ci	} else {
7428c2ecf20Sopenharmony_ci		int corr_sec_flag;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		corr_sec_flag = (dec_stat & DEC_STAT_BUF_CORR_SEC_FLAG_MASK) >>
7458c2ecf20Sopenharmony_ci				DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci		/*
7488c2ecf20Sopenharmony_ci		 * The value returned in the register is the maximum of
7498c2ecf20Sopenharmony_ci		 * bitflips encountered in any of the ECC regions. As there is
7508c2ecf20Sopenharmony_ci		 * no way to get the number of bitflips in a specific regions
7518c2ecf20Sopenharmony_ci		 * we are not able to deliver correct stats but instead
7528c2ecf20Sopenharmony_ci		 * overestimate the number of corrected bitflips by assuming
7538c2ecf20Sopenharmony_ci		 * that all regions where errors have been corrected
7548c2ecf20Sopenharmony_ci		 * encountered the maximum number of bitflips.
7558c2ecf20Sopenharmony_ci		 */
7568c2ecf20Sopenharmony_ci		mtd->ecc_stats.corrected += max_corr_cnt * hweight8(corr_sec_flag);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci		return max_corr_cnt;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_cistatic int tegra_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf,
7638c2ecf20Sopenharmony_ci				       int oob_required, int page)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
7668c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
7678c2ecf20Sopenharmony_ci	void *oob_buf = oob_required ? chip->oob_poi : NULL;
7688c2ecf20Sopenharmony_ci	int ret;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	tegra_nand_hw_ecc(ctrl, chip, true);
7718c2ecf20Sopenharmony_ci	ret = tegra_nand_page_xfer(mtd, chip, (void *)buf, oob_buf,
7728c2ecf20Sopenharmony_ci				   0, page, false);
7738c2ecf20Sopenharmony_ci	tegra_nand_hw_ecc(ctrl, chip, false);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	return ret;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_cistatic void tegra_nand_setup_timing(struct tegra_nand_controller *ctrl,
7798c2ecf20Sopenharmony_ci				    const struct nand_sdr_timings *timings)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	/*
7828c2ecf20Sopenharmony_ci	 * The period (and all other timings in this function) is in ps,
7838c2ecf20Sopenharmony_ci	 * so need to take care here to avoid integer overflows.
7848c2ecf20Sopenharmony_ci	 */
7858c2ecf20Sopenharmony_ci	unsigned int rate = clk_get_rate(ctrl->clk) / 1000000;
7868c2ecf20Sopenharmony_ci	unsigned int period = DIV_ROUND_UP(1000000, rate);
7878c2ecf20Sopenharmony_ci	u32 val, reg = 0;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	val = DIV_ROUND_UP(max3(timings->tAR_min, timings->tRR_min,
7908c2ecf20Sopenharmony_ci				timings->tRC_min), period);
7918c2ecf20Sopenharmony_ci	reg |= TIMING_TCR_TAR_TRR(OFFSET(val, 3));
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	val = DIV_ROUND_UP(max(max(timings->tCS_min, timings->tCH_min),
7948c2ecf20Sopenharmony_ci			       max(timings->tALS_min, timings->tALH_min)),
7958c2ecf20Sopenharmony_ci			   period);
7968c2ecf20Sopenharmony_ci	reg |= TIMING_TCS(OFFSET(val, 2));
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	val = DIV_ROUND_UP(max(timings->tRP_min, timings->tREA_max) + 6000,
7998c2ecf20Sopenharmony_ci			   period);
8008c2ecf20Sopenharmony_ci	reg |= TIMING_TRP(OFFSET(val, 1)) | TIMING_TRP_RESP(OFFSET(val, 1));
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	reg |= TIMING_TWB(OFFSET(DIV_ROUND_UP(timings->tWB_max, period), 1));
8038c2ecf20Sopenharmony_ci	reg |= TIMING_TWHR(OFFSET(DIV_ROUND_UP(timings->tWHR_min, period), 1));
8048c2ecf20Sopenharmony_ci	reg |= TIMING_TWH(OFFSET(DIV_ROUND_UP(timings->tWH_min, period), 1));
8058c2ecf20Sopenharmony_ci	reg |= TIMING_TWP(OFFSET(DIV_ROUND_UP(timings->tWP_min, period), 1));
8068c2ecf20Sopenharmony_ci	reg |= TIMING_TRH(OFFSET(DIV_ROUND_UP(timings->tREH_min, period), 1));
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	writel_relaxed(reg, ctrl->regs + TIMING_1);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	val = DIV_ROUND_UP(timings->tADL_min, period);
8118c2ecf20Sopenharmony_ci	reg = TIMING_TADL(OFFSET(val, 3));
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	writel_relaxed(reg, ctrl->regs + TIMING_2);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistatic int tegra_nand_setup_interface(struct nand_chip *chip, int csline,
8178c2ecf20Sopenharmony_ci				      const struct nand_interface_config *conf)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
8208c2ecf20Sopenharmony_ci	const struct nand_sdr_timings *timings;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	timings = nand_get_sdr_timings(conf);
8238c2ecf20Sopenharmony_ci	if (IS_ERR(timings))
8248c2ecf20Sopenharmony_ci		return PTR_ERR(timings);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
8278c2ecf20Sopenharmony_ci		return 0;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	tegra_nand_setup_timing(ctrl, timings);
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	return 0;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cistatic const int rs_strength_bootable[] = { 4 };
8358c2ecf20Sopenharmony_cistatic const int rs_strength[] = { 4, 6, 8 };
8368c2ecf20Sopenharmony_cistatic const int bch_strength_bootable[] = { 8, 16 };
8378c2ecf20Sopenharmony_cistatic const int bch_strength[] = { 4, 8, 14, 16 };
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_cistatic int tegra_nand_get_strength(struct nand_chip *chip, const int *strength,
8408c2ecf20Sopenharmony_ci				   int strength_len, int bits_per_step,
8418c2ecf20Sopenharmony_ci				   int oobsize)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	struct nand_device *base = mtd_to_nanddev(nand_to_mtd(chip));
8448c2ecf20Sopenharmony_ci	const struct nand_ecc_props *requirements =
8458c2ecf20Sopenharmony_ci		nanddev_get_ecc_requirements(base);
8468c2ecf20Sopenharmony_ci	bool maximize = base->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH;
8478c2ecf20Sopenharmony_ci	int i;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	/*
8508c2ecf20Sopenharmony_ci	 * Loop through available strengths. Backwards in case we try to
8518c2ecf20Sopenharmony_ci	 * maximize the BCH strength.
8528c2ecf20Sopenharmony_ci	 */
8538c2ecf20Sopenharmony_ci	for (i = 0; i < strength_len; i++) {
8548c2ecf20Sopenharmony_ci		int strength_sel, bytes_per_step, bytes_per_page;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci		if (maximize) {
8578c2ecf20Sopenharmony_ci			strength_sel = strength[strength_len - i - 1];
8588c2ecf20Sopenharmony_ci		} else {
8598c2ecf20Sopenharmony_ci			strength_sel = strength[i];
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci			if (strength_sel < requirements->strength)
8628c2ecf20Sopenharmony_ci				continue;
8638c2ecf20Sopenharmony_ci		}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci		bytes_per_step = DIV_ROUND_UP(bits_per_step * strength_sel,
8668c2ecf20Sopenharmony_ci					      BITS_PER_BYTE);
8678c2ecf20Sopenharmony_ci		bytes_per_page = round_up(bytes_per_step * chip->ecc.steps, 4);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		/* Check whether strength fits OOB */
8708c2ecf20Sopenharmony_ci		if (bytes_per_page < (oobsize - SKIP_SPARE_BYTES))
8718c2ecf20Sopenharmony_ci			return strength_sel;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	return -EINVAL;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_cistatic int tegra_nand_select_strength(struct nand_chip *chip, int oobsize)
8788c2ecf20Sopenharmony_ci{
8798c2ecf20Sopenharmony_ci	const int *strength;
8808c2ecf20Sopenharmony_ci	int strength_len, bits_per_step;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	switch (chip->ecc.algo) {
8838c2ecf20Sopenharmony_ci	case NAND_ECC_ALGO_RS:
8848c2ecf20Sopenharmony_ci		bits_per_step = BITS_PER_STEP_RS;
8858c2ecf20Sopenharmony_ci		if (chip->options & NAND_IS_BOOT_MEDIUM) {
8868c2ecf20Sopenharmony_ci			strength = rs_strength_bootable;
8878c2ecf20Sopenharmony_ci			strength_len = ARRAY_SIZE(rs_strength_bootable);
8888c2ecf20Sopenharmony_ci		} else {
8898c2ecf20Sopenharmony_ci			strength = rs_strength;
8908c2ecf20Sopenharmony_ci			strength_len = ARRAY_SIZE(rs_strength);
8918c2ecf20Sopenharmony_ci		}
8928c2ecf20Sopenharmony_ci		break;
8938c2ecf20Sopenharmony_ci	case NAND_ECC_ALGO_BCH:
8948c2ecf20Sopenharmony_ci		bits_per_step = BITS_PER_STEP_BCH;
8958c2ecf20Sopenharmony_ci		if (chip->options & NAND_IS_BOOT_MEDIUM) {
8968c2ecf20Sopenharmony_ci			strength = bch_strength_bootable;
8978c2ecf20Sopenharmony_ci			strength_len = ARRAY_SIZE(bch_strength_bootable);
8988c2ecf20Sopenharmony_ci		} else {
8998c2ecf20Sopenharmony_ci			strength = bch_strength;
9008c2ecf20Sopenharmony_ci			strength_len = ARRAY_SIZE(bch_strength);
9018c2ecf20Sopenharmony_ci		}
9028c2ecf20Sopenharmony_ci		break;
9038c2ecf20Sopenharmony_ci	default:
9048c2ecf20Sopenharmony_ci		return -EINVAL;
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	return tegra_nand_get_strength(chip, strength, strength_len,
9088c2ecf20Sopenharmony_ci				       bits_per_step, oobsize);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int tegra_nand_attach_chip(struct nand_chip *chip)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
9148c2ecf20Sopenharmony_ci	const struct nand_ecc_props *requirements =
9158c2ecf20Sopenharmony_ci		nanddev_get_ecc_requirements(&chip->base);
9168c2ecf20Sopenharmony_ci	struct tegra_nand_chip *nand = to_tegra_chip(chip);
9178c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
9188c2ecf20Sopenharmony_ci	int bits_per_step;
9198c2ecf20Sopenharmony_ci	int ret;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (chip->bbt_options & NAND_BBT_USE_FLASH)
9228c2ecf20Sopenharmony_ci		chip->bbt_options |= NAND_BBT_NO_OOB;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
9258c2ecf20Sopenharmony_ci	chip->ecc.size = 512;
9268c2ecf20Sopenharmony_ci	chip->ecc.steps = mtd->writesize / chip->ecc.size;
9278c2ecf20Sopenharmony_ci	if (requirements->step_size != 512) {
9288c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Unsupported step size %d\n",
9298c2ecf20Sopenharmony_ci			requirements->step_size);
9308c2ecf20Sopenharmony_ci		return -EINVAL;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci	chip->ecc.read_page = tegra_nand_read_page_hwecc;
9348c2ecf20Sopenharmony_ci	chip->ecc.write_page = tegra_nand_write_page_hwecc;
9358c2ecf20Sopenharmony_ci	chip->ecc.read_page_raw = tegra_nand_read_page_raw;
9368c2ecf20Sopenharmony_ci	chip->ecc.write_page_raw = tegra_nand_write_page_raw;
9378c2ecf20Sopenharmony_ci	chip->ecc.read_oob = tegra_nand_read_oob;
9388c2ecf20Sopenharmony_ci	chip->ecc.write_oob = tegra_nand_write_oob;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	if (chip->options & NAND_BUSWIDTH_16)
9418c2ecf20Sopenharmony_ci		nand->config |= CONFIG_BUS_WIDTH_16;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) {
9448c2ecf20Sopenharmony_ci		if (mtd->writesize < 2048)
9458c2ecf20Sopenharmony_ci			chip->ecc.algo = NAND_ECC_ALGO_RS;
9468c2ecf20Sopenharmony_ci		else
9478c2ecf20Sopenharmony_ci			chip->ecc.algo = NAND_ECC_ALGO_BCH;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	if (chip->ecc.algo == NAND_ECC_ALGO_BCH && mtd->writesize < 2048) {
9518c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "BCH supports 2K or 4K page size only\n");
9528c2ecf20Sopenharmony_ci		return -EINVAL;
9538c2ecf20Sopenharmony_ci	}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (!chip->ecc.strength) {
9568c2ecf20Sopenharmony_ci		ret = tegra_nand_select_strength(chip, mtd->oobsize);
9578c2ecf20Sopenharmony_ci		if (ret < 0) {
9588c2ecf20Sopenharmony_ci			dev_err(ctrl->dev,
9598c2ecf20Sopenharmony_ci				"No valid strength found, minimum %d\n",
9608c2ecf20Sopenharmony_ci				requirements->strength);
9618c2ecf20Sopenharmony_ci			return ret;
9628c2ecf20Sopenharmony_ci		}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci		chip->ecc.strength = ret;
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	nand->config_ecc = CONFIG_PIPE_EN | CONFIG_SKIP_SPARE |
9688c2ecf20Sopenharmony_ci			   CONFIG_SKIP_SPARE_SIZE_4;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	switch (chip->ecc.algo) {
9718c2ecf20Sopenharmony_ci	case NAND_ECC_ALGO_RS:
9728c2ecf20Sopenharmony_ci		bits_per_step = BITS_PER_STEP_RS * chip->ecc.strength;
9738c2ecf20Sopenharmony_ci		mtd_set_ooblayout(mtd, &tegra_nand_oob_rs_ops);
9748c2ecf20Sopenharmony_ci		nand->config_ecc |= CONFIG_HW_ECC | CONFIG_ECC_SEL |
9758c2ecf20Sopenharmony_ci				    CONFIG_ERR_COR;
9768c2ecf20Sopenharmony_ci		switch (chip->ecc.strength) {
9778c2ecf20Sopenharmony_ci		case 4:
9788c2ecf20Sopenharmony_ci			nand->config_ecc |= CONFIG_TVAL_4;
9798c2ecf20Sopenharmony_ci			break;
9808c2ecf20Sopenharmony_ci		case 6:
9818c2ecf20Sopenharmony_ci			nand->config_ecc |= CONFIG_TVAL_6;
9828c2ecf20Sopenharmony_ci			break;
9838c2ecf20Sopenharmony_ci		case 8:
9848c2ecf20Sopenharmony_ci			nand->config_ecc |= CONFIG_TVAL_8;
9858c2ecf20Sopenharmony_ci			break;
9868c2ecf20Sopenharmony_ci		default:
9878c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "ECC strength %d not supported\n",
9888c2ecf20Sopenharmony_ci				chip->ecc.strength);
9898c2ecf20Sopenharmony_ci			return -EINVAL;
9908c2ecf20Sopenharmony_ci		}
9918c2ecf20Sopenharmony_ci		break;
9928c2ecf20Sopenharmony_ci	case NAND_ECC_ALGO_BCH:
9938c2ecf20Sopenharmony_ci		bits_per_step = BITS_PER_STEP_BCH * chip->ecc.strength;
9948c2ecf20Sopenharmony_ci		mtd_set_ooblayout(mtd, &tegra_nand_oob_bch_ops);
9958c2ecf20Sopenharmony_ci		nand->bch_config = BCH_ENABLE;
9968c2ecf20Sopenharmony_ci		switch (chip->ecc.strength) {
9978c2ecf20Sopenharmony_ci		case 4:
9988c2ecf20Sopenharmony_ci			nand->bch_config |= BCH_TVAL_4;
9998c2ecf20Sopenharmony_ci			break;
10008c2ecf20Sopenharmony_ci		case 8:
10018c2ecf20Sopenharmony_ci			nand->bch_config |= BCH_TVAL_8;
10028c2ecf20Sopenharmony_ci			break;
10038c2ecf20Sopenharmony_ci		case 14:
10048c2ecf20Sopenharmony_ci			nand->bch_config |= BCH_TVAL_14;
10058c2ecf20Sopenharmony_ci			break;
10068c2ecf20Sopenharmony_ci		case 16:
10078c2ecf20Sopenharmony_ci			nand->bch_config |= BCH_TVAL_16;
10088c2ecf20Sopenharmony_ci			break;
10098c2ecf20Sopenharmony_ci		default:
10108c2ecf20Sopenharmony_ci			dev_err(ctrl->dev, "ECC strength %d not supported\n",
10118c2ecf20Sopenharmony_ci				chip->ecc.strength);
10128c2ecf20Sopenharmony_ci			return -EINVAL;
10138c2ecf20Sopenharmony_ci		}
10148c2ecf20Sopenharmony_ci		break;
10158c2ecf20Sopenharmony_ci	default:
10168c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "ECC algorithm not supported\n");
10178c2ecf20Sopenharmony_ci		return -EINVAL;
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	dev_info(ctrl->dev, "Using %s with strength %d per 512 byte step\n",
10218c2ecf20Sopenharmony_ci		 chip->ecc.algo == NAND_ECC_ALGO_BCH ? "BCH" : "RS",
10228c2ecf20Sopenharmony_ci		 chip->ecc.strength);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	chip->ecc.bytes = DIV_ROUND_UP(bits_per_step, BITS_PER_BYTE);
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	switch (mtd->writesize) {
10278c2ecf20Sopenharmony_ci	case 256:
10288c2ecf20Sopenharmony_ci		nand->config |= CONFIG_PS_256;
10298c2ecf20Sopenharmony_ci		break;
10308c2ecf20Sopenharmony_ci	case 512:
10318c2ecf20Sopenharmony_ci		nand->config |= CONFIG_PS_512;
10328c2ecf20Sopenharmony_ci		break;
10338c2ecf20Sopenharmony_ci	case 1024:
10348c2ecf20Sopenharmony_ci		nand->config |= CONFIG_PS_1024;
10358c2ecf20Sopenharmony_ci		break;
10368c2ecf20Sopenharmony_ci	case 2048:
10378c2ecf20Sopenharmony_ci		nand->config |= CONFIG_PS_2048;
10388c2ecf20Sopenharmony_ci		break;
10398c2ecf20Sopenharmony_ci	case 4096:
10408c2ecf20Sopenharmony_ci		nand->config |= CONFIG_PS_4096;
10418c2ecf20Sopenharmony_ci		break;
10428c2ecf20Sopenharmony_ci	default:
10438c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Unsupported writesize %d\n",
10448c2ecf20Sopenharmony_ci			mtd->writesize);
10458c2ecf20Sopenharmony_ci		return -ENODEV;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/* Store complete configuration for HW ECC in config_ecc */
10498c2ecf20Sopenharmony_ci	nand->config_ecc |= nand->config;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/* Non-HW ECC read/writes complete OOB */
10528c2ecf20Sopenharmony_ci	nand->config |= CONFIG_TAG_BYTE_SIZE(mtd->oobsize - 1);
10538c2ecf20Sopenharmony_ci	writel_relaxed(nand->config, ctrl->regs + CONFIG);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	return 0;
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_cistatic const struct nand_controller_ops tegra_nand_controller_ops = {
10598c2ecf20Sopenharmony_ci	.attach_chip = &tegra_nand_attach_chip,
10608c2ecf20Sopenharmony_ci	.exec_op = tegra_nand_exec_op,
10618c2ecf20Sopenharmony_ci	.setup_interface = tegra_nand_setup_interface,
10628c2ecf20Sopenharmony_ci};
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_cistatic int tegra_nand_chips_init(struct device *dev,
10658c2ecf20Sopenharmony_ci				 struct tegra_nand_controller *ctrl)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
10688c2ecf20Sopenharmony_ci	struct device_node *np_nand;
10698c2ecf20Sopenharmony_ci	int nsels, nchips = of_get_child_count(np);
10708c2ecf20Sopenharmony_ci	struct tegra_nand_chip *nand;
10718c2ecf20Sopenharmony_ci	struct mtd_info *mtd;
10728c2ecf20Sopenharmony_ci	struct nand_chip *chip;
10738c2ecf20Sopenharmony_ci	int ret;
10748c2ecf20Sopenharmony_ci	u32 cs;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (nchips != 1) {
10778c2ecf20Sopenharmony_ci		dev_err(dev, "Currently only one NAND chip supported\n");
10788c2ecf20Sopenharmony_ci		return -EINVAL;
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	np_nand = of_get_next_child(np, NULL);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	nsels = of_property_count_elems_of_size(np_nand, "reg", sizeof(u32));
10848c2ecf20Sopenharmony_ci	if (nsels != 1) {
10858c2ecf20Sopenharmony_ci		dev_err(dev, "Missing/invalid reg property\n");
10868c2ecf20Sopenharmony_ci		return -EINVAL;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	/* Retrieve CS id, currently only single die NAND supported */
10908c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np_nand, "reg", &cs);
10918c2ecf20Sopenharmony_ci	if (ret) {
10928c2ecf20Sopenharmony_ci		dev_err(dev, "could not retrieve reg property: %d\n", ret);
10938c2ecf20Sopenharmony_ci		return ret;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL);
10978c2ecf20Sopenharmony_ci	if (!nand)
10988c2ecf20Sopenharmony_ci		return -ENOMEM;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	nand->cs[0] = cs;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	if (IS_ERR(nand->wp_gpio)) {
11058c2ecf20Sopenharmony_ci		ret = PTR_ERR(nand->wp_gpio);
11068c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to request WP GPIO: %d\n", ret);
11078c2ecf20Sopenharmony_ci		return ret;
11088c2ecf20Sopenharmony_ci	}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	chip = &nand->chip;
11118c2ecf20Sopenharmony_ci	chip->controller = &ctrl->controller;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	mtd = nand_to_mtd(chip);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	mtd->dev.parent = dev;
11168c2ecf20Sopenharmony_ci	mtd->owner = THIS_MODULE;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci	nand_set_flash_node(chip, np_nand);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (!mtd->name)
11218c2ecf20Sopenharmony_ci		mtd->name = "tegra_nand";
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	ret = nand_scan(chip, 1);
11268c2ecf20Sopenharmony_ci	if (ret)
11278c2ecf20Sopenharmony_ci		return ret;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	mtd_ooblayout_ecc(mtd, 0, &nand->ecc);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
11328c2ecf20Sopenharmony_ci	if (ret) {
11338c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to register mtd device: %d\n", ret);
11348c2ecf20Sopenharmony_ci		nand_cleanup(chip);
11358c2ecf20Sopenharmony_ci		return ret;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	ctrl->chip = chip;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	return 0;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic int tegra_nand_probe(struct platform_device *pdev)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct reset_control *rst;
11468c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl;
11478c2ecf20Sopenharmony_ci	struct resource *res;
11488c2ecf20Sopenharmony_ci	int err = 0;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
11518c2ecf20Sopenharmony_ci	if (!ctrl)
11528c2ecf20Sopenharmony_ci		return -ENOMEM;
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	ctrl->dev = &pdev->dev;
11558c2ecf20Sopenharmony_ci	nand_controller_init(&ctrl->controller);
11568c2ecf20Sopenharmony_ci	ctrl->controller.ops = &tegra_nand_controller_ops;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
11598c2ecf20Sopenharmony_ci	ctrl->regs = devm_ioremap_resource(&pdev->dev, res);
11608c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->regs))
11618c2ecf20Sopenharmony_ci		return PTR_ERR(ctrl->regs);
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	rst = devm_reset_control_get(&pdev->dev, "nand");
11648c2ecf20Sopenharmony_ci	if (IS_ERR(rst))
11658c2ecf20Sopenharmony_ci		return PTR_ERR(rst);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	ctrl->clk = devm_clk_get(&pdev->dev, "nand");
11688c2ecf20Sopenharmony_ci	if (IS_ERR(ctrl->clk))
11698c2ecf20Sopenharmony_ci		return PTR_ERR(ctrl->clk);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	err = clk_prepare_enable(ctrl->clk);
11728c2ecf20Sopenharmony_ci	if (err)
11738c2ecf20Sopenharmony_ci		return err;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	err = reset_control_reset(rst);
11768c2ecf20Sopenharmony_ci	if (err) {
11778c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Failed to reset HW: %d\n", err);
11788c2ecf20Sopenharmony_ci		goto err_disable_clk;
11798c2ecf20Sopenharmony_ci	}
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD);
11828c2ecf20Sopenharmony_ci	writel_relaxed(HWSTATUS_MASK_DEFAULT, ctrl->regs + HWSTATUS_MASK);
11838c2ecf20Sopenharmony_ci	writel_relaxed(INT_MASK, ctrl->regs + IER);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	init_completion(&ctrl->command_complete);
11868c2ecf20Sopenharmony_ci	init_completion(&ctrl->dma_complete);
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	ctrl->irq = platform_get_irq(pdev, 0);
11898c2ecf20Sopenharmony_ci	err = devm_request_irq(&pdev->dev, ctrl->irq, tegra_nand_irq, 0,
11908c2ecf20Sopenharmony_ci			       dev_name(&pdev->dev), ctrl);
11918c2ecf20Sopenharmony_ci	if (err) {
11928c2ecf20Sopenharmony_ci		dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err);
11938c2ecf20Sopenharmony_ci		goto err_disable_clk;
11948c2ecf20Sopenharmony_ci	}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	err = tegra_nand_chips_init(ctrl->dev, ctrl);
11998c2ecf20Sopenharmony_ci	if (err)
12008c2ecf20Sopenharmony_ci		goto err_disable_clk;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ctrl);
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	return 0;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cierr_disable_clk:
12078c2ecf20Sopenharmony_ci	clk_disable_unprepare(ctrl->clk);
12088c2ecf20Sopenharmony_ci	return err;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic int tegra_nand_remove(struct platform_device *pdev)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct tegra_nand_controller *ctrl = platform_get_drvdata(pdev);
12148c2ecf20Sopenharmony_ci	struct nand_chip *chip = ctrl->chip;
12158c2ecf20Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
12168c2ecf20Sopenharmony_ci	int ret;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	ret = mtd_device_unregister(mtd);
12198c2ecf20Sopenharmony_ci	if (ret)
12208c2ecf20Sopenharmony_ci		return ret;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	nand_cleanup(chip);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	clk_disable_unprepare(ctrl->clk);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return 0;
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_nand_of_match[] = {
12308c2ecf20Sopenharmony_ci	{ .compatible = "nvidia,tegra20-nand" },
12318c2ecf20Sopenharmony_ci	{ /* sentinel */ }
12328c2ecf20Sopenharmony_ci};
12338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_nand_of_match);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_cistatic struct platform_driver tegra_nand_driver = {
12368c2ecf20Sopenharmony_ci	.driver = {
12378c2ecf20Sopenharmony_ci		.name = "tegra-nand",
12388c2ecf20Sopenharmony_ci		.of_match_table = tegra_nand_of_match,
12398c2ecf20Sopenharmony_ci	},
12408c2ecf20Sopenharmony_ci	.probe = tegra_nand_probe,
12418c2ecf20Sopenharmony_ci	.remove = tegra_nand_remove,
12428c2ecf20Sopenharmony_ci};
12438c2ecf20Sopenharmony_cimodule_platform_driver(tegra_nand_driver);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra NAND driver");
12468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@nvidia.com>");
12478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lucas Stach <dev@lynxeye.de>");
12488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
12498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1250