162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ST Microelectronics
462306a36Sopenharmony_ci * Flexible Static Memory Controller (FSMC)
562306a36Sopenharmony_ci * Driver for NAND portions
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright © 2010 ST Microelectronics
862306a36Sopenharmony_ci * Vipin Kumar <vipin.kumar@st.com>
962306a36Sopenharmony_ci * Ashish Priyadarshi
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8)
1262306a36Sopenharmony_ci *  Copyright © 2007 STMicroelectronics Pvt. Ltd.
1362306a36Sopenharmony_ci *  Copyright © 2009 Alessandro Rubini
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/clk.h>
1762306a36Sopenharmony_ci#include <linux/completion.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/dmaengine.h>
2062306a36Sopenharmony_ci#include <linux/dma-direction.h>
2162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2262306a36Sopenharmony_ci#include <linux/err.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/resource.h>
2662306a36Sopenharmony_ci#include <linux/sched.h>
2762306a36Sopenharmony_ci#include <linux/types.h>
2862306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2962306a36Sopenharmony_ci#include <linux/mtd/nand-ecc-sw-hamming.h>
3062306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
3162306a36Sopenharmony_ci#include <linux/platform_device.h>
3262306a36Sopenharmony_ci#include <linux/of.h>
3362306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
3462306a36Sopenharmony_ci#include <linux/io.h>
3562306a36Sopenharmony_ci#include <linux/slab.h>
3662306a36Sopenharmony_ci#include <linux/amba/bus.h>
3762306a36Sopenharmony_ci#include <mtd/mtd-abi.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* fsmc controller registers for NOR flash */
4062306a36Sopenharmony_ci#define CTRL			0x0
4162306a36Sopenharmony_ci	/* ctrl register definitions */
4262306a36Sopenharmony_ci	#define BANK_ENABLE		BIT(0)
4362306a36Sopenharmony_ci	#define MUXED			BIT(1)
4462306a36Sopenharmony_ci	#define NOR_DEV			(2 << 2)
4562306a36Sopenharmony_ci	#define WIDTH_16		BIT(4)
4662306a36Sopenharmony_ci	#define RSTPWRDWN		BIT(6)
4762306a36Sopenharmony_ci	#define WPROT			BIT(7)
4862306a36Sopenharmony_ci	#define WRT_ENABLE		BIT(12)
4962306a36Sopenharmony_ci	#define WAIT_ENB		BIT(13)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define CTRL_TIM		0x4
5262306a36Sopenharmony_ci	/* ctrl_tim register definitions */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define FSMC_NOR_BANK_SZ	0x8
5562306a36Sopenharmony_ci#define FSMC_NOR_REG_SIZE	0x40
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define FSMC_NOR_REG(base, bank, reg)	((base) +			\
5862306a36Sopenharmony_ci					 (FSMC_NOR_BANK_SZ * (bank)) +	\
5962306a36Sopenharmony_ci					 (reg))
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* fsmc controller registers for NAND flash */
6262306a36Sopenharmony_ci#define FSMC_PC			0x00
6362306a36Sopenharmony_ci	/* pc register definitions */
6462306a36Sopenharmony_ci	#define FSMC_RESET		BIT(0)
6562306a36Sopenharmony_ci	#define FSMC_WAITON		BIT(1)
6662306a36Sopenharmony_ci	#define FSMC_ENABLE		BIT(2)
6762306a36Sopenharmony_ci	#define FSMC_DEVTYPE_NAND	BIT(3)
6862306a36Sopenharmony_ci	#define FSMC_DEVWID_16		BIT(4)
6962306a36Sopenharmony_ci	#define FSMC_ECCEN		BIT(6)
7062306a36Sopenharmony_ci	#define FSMC_ECCPLEN_256	BIT(7)
7162306a36Sopenharmony_ci	#define FSMC_TCLR_SHIFT		(9)
7262306a36Sopenharmony_ci	#define FSMC_TCLR_MASK		(0xF)
7362306a36Sopenharmony_ci	#define FSMC_TAR_SHIFT		(13)
7462306a36Sopenharmony_ci	#define FSMC_TAR_MASK		(0xF)
7562306a36Sopenharmony_ci#define STS			0x04
7662306a36Sopenharmony_ci	/* sts register definitions */
7762306a36Sopenharmony_ci	#define FSMC_CODE_RDY		BIT(15)
7862306a36Sopenharmony_ci#define COMM			0x08
7962306a36Sopenharmony_ci	/* comm register definitions */
8062306a36Sopenharmony_ci	#define FSMC_TSET_SHIFT		0
8162306a36Sopenharmony_ci	#define FSMC_TSET_MASK		0xFF
8262306a36Sopenharmony_ci	#define FSMC_TWAIT_SHIFT	8
8362306a36Sopenharmony_ci	#define FSMC_TWAIT_MASK		0xFF
8462306a36Sopenharmony_ci	#define FSMC_THOLD_SHIFT	16
8562306a36Sopenharmony_ci	#define FSMC_THOLD_MASK		0xFF
8662306a36Sopenharmony_ci	#define FSMC_THIZ_SHIFT		24
8762306a36Sopenharmony_ci	#define FSMC_THIZ_MASK		0xFF
8862306a36Sopenharmony_ci#define ATTRIB			0x0C
8962306a36Sopenharmony_ci#define IOATA			0x10
9062306a36Sopenharmony_ci#define ECC1			0x14
9162306a36Sopenharmony_ci#define ECC2			0x18
9262306a36Sopenharmony_ci#define ECC3			0x1C
9362306a36Sopenharmony_ci#define FSMC_NAND_BANK_SZ	0x20
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define FSMC_BUSY_WAIT_TIMEOUT	(1 * HZ)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * According to SPEAr300 Reference Manual (RM0082)
9962306a36Sopenharmony_ci *  TOUDEL = 7ns (Output delay from the flip-flops to the board)
10062306a36Sopenharmony_ci *  TINDEL = 5ns (Input delay from the board to the flipflop)
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci#define TOUTDEL	7000
10362306a36Sopenharmony_ci#define TINDEL	5000
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct fsmc_nand_timings {
10662306a36Sopenharmony_ci	u8 tclr;
10762306a36Sopenharmony_ci	u8 tar;
10862306a36Sopenharmony_ci	u8 thiz;
10962306a36Sopenharmony_ci	u8 thold;
11062306a36Sopenharmony_ci	u8 twait;
11162306a36Sopenharmony_ci	u8 tset;
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cienum access_mode {
11562306a36Sopenharmony_ci	USE_DMA_ACCESS = 1,
11662306a36Sopenharmony_ci	USE_WORD_ACCESS,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * struct fsmc_nand_data - structure for FSMC NAND device state
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * @base:		Inherit from the nand_controller struct
12362306a36Sopenharmony_ci * @pid:		Part ID on the AMBA PrimeCell format
12462306a36Sopenharmony_ci * @nand:		Chip related info for a NAND flash.
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * @bank:		Bank number for probed device.
12762306a36Sopenharmony_ci * @dev:		Parent device
12862306a36Sopenharmony_ci * @mode:		Access mode
12962306a36Sopenharmony_ci * @clk:		Clock structure for FSMC.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci * @read_dma_chan:	DMA channel for read access
13262306a36Sopenharmony_ci * @write_dma_chan:	DMA channel for write access to NAND
13362306a36Sopenharmony_ci * @dma_access_complete: Completion structure
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci * @dev_timings:	NAND timings
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * @data_pa:		NAND Physical port for Data.
13862306a36Sopenharmony_ci * @data_va:		NAND port for Data.
13962306a36Sopenharmony_ci * @cmd_va:		NAND port for Command.
14062306a36Sopenharmony_ci * @addr_va:		NAND port for Address.
14162306a36Sopenharmony_ci * @regs_va:		Registers base address for a given bank.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cistruct fsmc_nand_data {
14462306a36Sopenharmony_ci	struct nand_controller	base;
14562306a36Sopenharmony_ci	u32			pid;
14662306a36Sopenharmony_ci	struct nand_chip	nand;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	unsigned int		bank;
14962306a36Sopenharmony_ci	struct device		*dev;
15062306a36Sopenharmony_ci	enum access_mode	mode;
15162306a36Sopenharmony_ci	struct clk		*clk;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* DMA related objects */
15462306a36Sopenharmony_ci	struct dma_chan		*read_dma_chan;
15562306a36Sopenharmony_ci	struct dma_chan		*write_dma_chan;
15662306a36Sopenharmony_ci	struct completion	dma_access_complete;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	struct fsmc_nand_timings *dev_timings;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	dma_addr_t		data_pa;
16162306a36Sopenharmony_ci	void __iomem		*data_va;
16262306a36Sopenharmony_ci	void __iomem		*cmd_va;
16362306a36Sopenharmony_ci	void __iomem		*addr_va;
16462306a36Sopenharmony_ci	void __iomem		*regs_va;
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
16862306a36Sopenharmony_ci				   struct mtd_oob_region *oobregion)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
17362306a36Sopenharmony_ci		return -ERANGE;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	oobregion->offset = (section * 16) + 2;
17662306a36Sopenharmony_ci	oobregion->length = 3;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section,
18262306a36Sopenharmony_ci				    struct mtd_oob_region *oobregion)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
18762306a36Sopenharmony_ci		return -ERANGE;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	oobregion->offset = (section * 16) + 8;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (section < chip->ecc.steps - 1)
19262306a36Sopenharmony_ci		oobregion->length = 8;
19362306a36Sopenharmony_ci	else
19462306a36Sopenharmony_ci		oobregion->length = mtd->oobsize - oobregion->offset;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = {
20062306a36Sopenharmony_ci	.ecc = fsmc_ecc1_ooblayout_ecc,
20162306a36Sopenharmony_ci	.free = fsmc_ecc1_ooblayout_free,
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/*
20562306a36Sopenharmony_ci * ECC placement definitions in oobfree type format.
20662306a36Sopenharmony_ci * There are 13 bytes of ecc for every 512 byte block and it has to be read
20762306a36Sopenharmony_ci * consecutively and immediately after the 512 byte data block for hardware to
20862306a36Sopenharmony_ci * generate the error bit offsets in 512 byte data.
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cistatic int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section,
21162306a36Sopenharmony_ci				   struct mtd_oob_region *oobregion)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
21662306a36Sopenharmony_ci		return -ERANGE;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	oobregion->length = chip->ecc.bytes;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (!section && mtd->writesize <= 512)
22162306a36Sopenharmony_ci		oobregion->offset = 0;
22262306a36Sopenharmony_ci	else
22362306a36Sopenharmony_ci		oobregion->offset = (section * 16) + 2;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section,
22962306a36Sopenharmony_ci				    struct mtd_oob_region *oobregion)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (section >= chip->ecc.steps)
23462306a36Sopenharmony_ci		return -ERANGE;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	oobregion->offset = (section * 16) + 15;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (section < chip->ecc.steps - 1)
23962306a36Sopenharmony_ci		oobregion->length = 3;
24062306a36Sopenharmony_ci	else
24162306a36Sopenharmony_ci		oobregion->length = mtd->oobsize - oobregion->offset;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
24762306a36Sopenharmony_ci	.ecc = fsmc_ecc4_ooblayout_ecc,
24862306a36Sopenharmony_ci	.free = fsmc_ecc4_ooblayout_free,
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic inline struct fsmc_nand_data *nand_to_fsmc(struct nand_chip *chip)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	return container_of(chip, struct fsmc_nand_data, nand);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/*
25762306a36Sopenharmony_ci * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * This routine initializes timing parameters related to NAND memory access in
26062306a36Sopenharmony_ci * FSMC registers
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_cistatic void fsmc_nand_setup(struct fsmc_nand_data *host,
26362306a36Sopenharmony_ci			    struct fsmc_nand_timings *tims)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	u32 value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
26662306a36Sopenharmony_ci	u32 tclr, tar, thiz, thold, twait, tset;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
26962306a36Sopenharmony_ci	tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
27062306a36Sopenharmony_ci	thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT;
27162306a36Sopenharmony_ci	thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT;
27262306a36Sopenharmony_ci	twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT;
27362306a36Sopenharmony_ci	tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (host->nand.options & NAND_BUSWIDTH_16)
27662306a36Sopenharmony_ci		value |= FSMC_DEVWID_16;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	writel_relaxed(value | tclr | tar, host->regs_va + FSMC_PC);
27962306a36Sopenharmony_ci	writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM);
28062306a36Sopenharmony_ci	writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int fsmc_calc_timings(struct fsmc_nand_data *host,
28462306a36Sopenharmony_ci			     const struct nand_sdr_timings *sdrt,
28562306a36Sopenharmony_ci			     struct fsmc_nand_timings *tims)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	unsigned long hclk = clk_get_rate(host->clk);
28862306a36Sopenharmony_ci	unsigned long hclkn = NSEC_PER_SEC / hclk;
28962306a36Sopenharmony_ci	u32 thiz, thold, twait, tset, twait_min;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (sdrt->tRC_min < 30000)
29262306a36Sopenharmony_ci		return -EOPNOTSUPP;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1;
29562306a36Sopenharmony_ci	if (tims->tar > FSMC_TAR_MASK)
29662306a36Sopenharmony_ci		tims->tar = FSMC_TAR_MASK;
29762306a36Sopenharmony_ci	tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1;
29862306a36Sopenharmony_ci	if (tims->tclr > FSMC_TCLR_MASK)
29962306a36Sopenharmony_ci		tims->tclr = FSMC_TCLR_MASK;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	thiz = sdrt->tCS_min - sdrt->tWP_min;
30262306a36Sopenharmony_ci	tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	thold = sdrt->tDH_min;
30562306a36Sopenharmony_ci	if (thold < sdrt->tCH_min)
30662306a36Sopenharmony_ci		thold = sdrt->tCH_min;
30762306a36Sopenharmony_ci	if (thold < sdrt->tCLH_min)
30862306a36Sopenharmony_ci		thold = sdrt->tCLH_min;
30962306a36Sopenharmony_ci	if (thold < sdrt->tWH_min)
31062306a36Sopenharmony_ci		thold = sdrt->tWH_min;
31162306a36Sopenharmony_ci	if (thold < sdrt->tALH_min)
31262306a36Sopenharmony_ci		thold = sdrt->tALH_min;
31362306a36Sopenharmony_ci	if (thold < sdrt->tREH_min)
31462306a36Sopenharmony_ci		thold = sdrt->tREH_min;
31562306a36Sopenharmony_ci	tims->thold = DIV_ROUND_UP(thold / 1000, hclkn);
31662306a36Sopenharmony_ci	if (tims->thold == 0)
31762306a36Sopenharmony_ci		tims->thold = 1;
31862306a36Sopenharmony_ci	else if (tims->thold > FSMC_THOLD_MASK)
31962306a36Sopenharmony_ci		tims->thold = FSMC_THOLD_MASK;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	tset = max(sdrt->tCS_min - sdrt->tWP_min,
32262306a36Sopenharmony_ci		   sdrt->tCEA_max - sdrt->tREA_max);
32362306a36Sopenharmony_ci	tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1;
32462306a36Sopenharmony_ci	if (tims->tset == 0)
32562306a36Sopenharmony_ci		tims->tset = 1;
32662306a36Sopenharmony_ci	else if (tims->tset > FSMC_TSET_MASK)
32762306a36Sopenharmony_ci		tims->tset = FSMC_TSET_MASK;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/*
33062306a36Sopenharmony_ci	 * According to SPEAr300 Reference Manual (RM0082) which gives more
33162306a36Sopenharmony_ci	 * information related to FSMSC timings than the SPEAr600 one (RM0305),
33262306a36Sopenharmony_ci	 *   twait >= tCEA - (tset * TCLK) + TOUTDEL + TINDEL
33362306a36Sopenharmony_ci	 */
33462306a36Sopenharmony_ci	twait_min = sdrt->tCEA_max - ((tims->tset + 1) * hclkn * 1000)
33562306a36Sopenharmony_ci		    + TOUTDEL + TINDEL;
33662306a36Sopenharmony_ci	twait = max3(sdrt->tRP_min, sdrt->tWP_min, twait_min);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1;
33962306a36Sopenharmony_ci	if (tims->twait == 0)
34062306a36Sopenharmony_ci		tims->twait = 1;
34162306a36Sopenharmony_ci	else if (tims->twait > FSMC_TWAIT_MASK)
34262306a36Sopenharmony_ci		tims->twait = FSMC_TWAIT_MASK;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return 0;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int fsmc_setup_interface(struct nand_chip *nand, int csline,
34862306a36Sopenharmony_ci				const struct nand_interface_config *conf)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(nand);
35162306a36Sopenharmony_ci	struct fsmc_nand_timings tims;
35262306a36Sopenharmony_ci	const struct nand_sdr_timings *sdrt;
35362306a36Sopenharmony_ci	int ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	sdrt = nand_get_sdr_timings(conf);
35662306a36Sopenharmony_ci	if (IS_ERR(sdrt))
35762306a36Sopenharmony_ci		return PTR_ERR(sdrt);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = fsmc_calc_timings(host, sdrt, &tims);
36062306a36Sopenharmony_ci	if (ret)
36162306a36Sopenharmony_ci		return ret;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
36462306a36Sopenharmony_ci		return 0;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	fsmc_nand_setup(host, &tims);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci/*
37262306a36Sopenharmony_ci * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
37362306a36Sopenharmony_ci */
37462306a36Sopenharmony_cistatic void fsmc_enable_hwecc(struct nand_chip *chip, int mode)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(chip);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCPLEN_256,
37962306a36Sopenharmony_ci		       host->regs_va + FSMC_PC);
38062306a36Sopenharmony_ci	writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCEN,
38162306a36Sopenharmony_ci		       host->regs_va + FSMC_PC);
38262306a36Sopenharmony_ci	writel_relaxed(readl(host->regs_va + FSMC_PC) | FSMC_ECCEN,
38362306a36Sopenharmony_ci		       host->regs_va + FSMC_PC);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci/*
38762306a36Sopenharmony_ci * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
38862306a36Sopenharmony_ci * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to
38962306a36Sopenharmony_ci * max of 8-bits)
39062306a36Sopenharmony_ci */
39162306a36Sopenharmony_cistatic int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const u8 *data,
39262306a36Sopenharmony_ci				u8 *ecc)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(chip);
39562306a36Sopenharmony_ci	u32 ecc_tmp;
39662306a36Sopenharmony_ci	unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	do {
39962306a36Sopenharmony_ci		if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY)
40062306a36Sopenharmony_ci			break;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		cond_resched();
40362306a36Sopenharmony_ci	} while (!time_after_eq(jiffies, deadline));
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (time_after_eq(jiffies, deadline)) {
40662306a36Sopenharmony_ci		dev_err(host->dev, "calculate ecc timed out\n");
40762306a36Sopenharmony_ci		return -ETIMEDOUT;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ecc_tmp = readl_relaxed(host->regs_va + ECC1);
41162306a36Sopenharmony_ci	ecc[0] = ecc_tmp;
41262306a36Sopenharmony_ci	ecc[1] = ecc_tmp >> 8;
41362306a36Sopenharmony_ci	ecc[2] = ecc_tmp >> 16;
41462306a36Sopenharmony_ci	ecc[3] = ecc_tmp >> 24;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	ecc_tmp = readl_relaxed(host->regs_va + ECC2);
41762306a36Sopenharmony_ci	ecc[4] = ecc_tmp;
41862306a36Sopenharmony_ci	ecc[5] = ecc_tmp >> 8;
41962306a36Sopenharmony_ci	ecc[6] = ecc_tmp >> 16;
42062306a36Sopenharmony_ci	ecc[7] = ecc_tmp >> 24;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	ecc_tmp = readl_relaxed(host->regs_va + ECC3);
42362306a36Sopenharmony_ci	ecc[8] = ecc_tmp;
42462306a36Sopenharmony_ci	ecc[9] = ecc_tmp >> 8;
42562306a36Sopenharmony_ci	ecc[10] = ecc_tmp >> 16;
42662306a36Sopenharmony_ci	ecc[11] = ecc_tmp >> 24;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	ecc_tmp = readl_relaxed(host->regs_va + STS);
42962306a36Sopenharmony_ci	ecc[12] = ecc_tmp >> 16;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/*
43562306a36Sopenharmony_ci * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
43662306a36Sopenharmony_ci * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to
43762306a36Sopenharmony_ci * max of 1-bit)
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_cistatic int fsmc_read_hwecc_ecc1(struct nand_chip *chip, const u8 *data,
44062306a36Sopenharmony_ci				u8 *ecc)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(chip);
44362306a36Sopenharmony_ci	u32 ecc_tmp;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ecc_tmp = readl_relaxed(host->regs_va + ECC1);
44662306a36Sopenharmony_ci	ecc[0] = ecc_tmp;
44762306a36Sopenharmony_ci	ecc[1] = ecc_tmp >> 8;
44862306a36Sopenharmony_ci	ecc[2] = ecc_tmp >> 16;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int fsmc_correct_ecc1(struct nand_chip *chip,
45462306a36Sopenharmony_ci			     unsigned char *buf,
45562306a36Sopenharmony_ci			     unsigned char *read_ecc,
45662306a36Sopenharmony_ci			     unsigned char *calc_ecc)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc,
46162306a36Sopenharmony_ci				      chip->ecc.size, sm_order);
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci/* Count the number of 0's in buff upto a max of max_bits */
46562306a36Sopenharmony_cistatic int count_written_bits(u8 *buff, int size, int max_bits)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	int k, written_bits = 0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	for (k = 0; k < size; k++) {
47062306a36Sopenharmony_ci		written_bits += hweight8(~buff[k]);
47162306a36Sopenharmony_ci		if (written_bits > max_bits)
47262306a36Sopenharmony_ci			break;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return written_bits;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void dma_complete(void *param)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct fsmc_nand_data *host = param;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	complete(&host->dma_access_complete);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
48662306a36Sopenharmony_ci		    enum dma_data_direction direction)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct dma_chan *chan;
48962306a36Sopenharmony_ci	struct dma_device *dma_dev;
49062306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
49162306a36Sopenharmony_ci	dma_addr_t dma_dst, dma_src, dma_addr;
49262306a36Sopenharmony_ci	dma_cookie_t cookie;
49362306a36Sopenharmony_ci	unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
49462306a36Sopenharmony_ci	int ret;
49562306a36Sopenharmony_ci	unsigned long time_left;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (direction == DMA_TO_DEVICE)
49862306a36Sopenharmony_ci		chan = host->write_dma_chan;
49962306a36Sopenharmony_ci	else if (direction == DMA_FROM_DEVICE)
50062306a36Sopenharmony_ci		chan = host->read_dma_chan;
50162306a36Sopenharmony_ci	else
50262306a36Sopenharmony_ci		return -EINVAL;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	dma_dev = chan->device;
50562306a36Sopenharmony_ci	dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (direction == DMA_TO_DEVICE) {
50862306a36Sopenharmony_ci		dma_src = dma_addr;
50962306a36Sopenharmony_ci		dma_dst = host->data_pa;
51062306a36Sopenharmony_ci	} else {
51162306a36Sopenharmony_ci		dma_src = host->data_pa;
51262306a36Sopenharmony_ci		dma_dst = dma_addr;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src,
51662306a36Sopenharmony_ci			len, flags);
51762306a36Sopenharmony_ci	if (!tx) {
51862306a36Sopenharmony_ci		dev_err(host->dev, "device_prep_dma_memcpy error\n");
51962306a36Sopenharmony_ci		ret = -EIO;
52062306a36Sopenharmony_ci		goto unmap_dma;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	tx->callback = dma_complete;
52462306a36Sopenharmony_ci	tx->callback_param = host;
52562306a36Sopenharmony_ci	cookie = tx->tx_submit(tx);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	ret = dma_submit_error(cookie);
52862306a36Sopenharmony_ci	if (ret) {
52962306a36Sopenharmony_ci		dev_err(host->dev, "dma_submit_error %d\n", cookie);
53062306a36Sopenharmony_ci		goto unmap_dma;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dma_async_issue_pending(chan);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	time_left =
53662306a36Sopenharmony_ci	wait_for_completion_timeout(&host->dma_access_complete,
53762306a36Sopenharmony_ci				    msecs_to_jiffies(3000));
53862306a36Sopenharmony_ci	if (time_left == 0) {
53962306a36Sopenharmony_ci		dmaengine_terminate_all(chan);
54062306a36Sopenharmony_ci		dev_err(host->dev, "wait_for_completion_timeout\n");
54162306a36Sopenharmony_ci		ret = -ETIMEDOUT;
54262306a36Sopenharmony_ci		goto unmap_dma;
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ret = 0;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciunmap_dma:
54862306a36Sopenharmony_ci	dma_unmap_single(dma_dev->dev, dma_addr, len, direction);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return ret;
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/*
55462306a36Sopenharmony_ci * fsmc_write_buf - write buffer to chip
55562306a36Sopenharmony_ci * @host:	FSMC NAND controller
55662306a36Sopenharmony_ci * @buf:	data buffer
55762306a36Sopenharmony_ci * @len:	number of bytes to write
55862306a36Sopenharmony_ci */
55962306a36Sopenharmony_cistatic void fsmc_write_buf(struct fsmc_nand_data *host, const u8 *buf,
56062306a36Sopenharmony_ci			   int len)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	int i;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) &&
56562306a36Sopenharmony_ci	    IS_ALIGNED(len, sizeof(u32))) {
56662306a36Sopenharmony_ci		u32 *p = (u32 *)buf;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		len = len >> 2;
56962306a36Sopenharmony_ci		for (i = 0; i < len; i++)
57062306a36Sopenharmony_ci			writel_relaxed(p[i], host->data_va);
57162306a36Sopenharmony_ci	} else {
57262306a36Sopenharmony_ci		for (i = 0; i < len; i++)
57362306a36Sopenharmony_ci			writeb_relaxed(buf[i], host->data_va);
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/*
57862306a36Sopenharmony_ci * fsmc_read_buf - read chip data into buffer
57962306a36Sopenharmony_ci * @host:	FSMC NAND controller
58062306a36Sopenharmony_ci * @buf:	buffer to store date
58162306a36Sopenharmony_ci * @len:	number of bytes to read
58262306a36Sopenharmony_ci */
58362306a36Sopenharmony_cistatic void fsmc_read_buf(struct fsmc_nand_data *host, u8 *buf, int len)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	int i;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) &&
58862306a36Sopenharmony_ci	    IS_ALIGNED(len, sizeof(u32))) {
58962306a36Sopenharmony_ci		u32 *p = (u32 *)buf;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		len = len >> 2;
59262306a36Sopenharmony_ci		for (i = 0; i < len; i++)
59362306a36Sopenharmony_ci			p[i] = readl_relaxed(host->data_va);
59462306a36Sopenharmony_ci	} else {
59562306a36Sopenharmony_ci		for (i = 0; i < len; i++)
59662306a36Sopenharmony_ci			buf[i] = readb_relaxed(host->data_va);
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci/*
60162306a36Sopenharmony_ci * fsmc_read_buf_dma - read chip data into buffer
60262306a36Sopenharmony_ci * @host:	FSMC NAND controller
60362306a36Sopenharmony_ci * @buf:	buffer to store date
60462306a36Sopenharmony_ci * @len:	number of bytes to read
60562306a36Sopenharmony_ci */
60662306a36Sopenharmony_cistatic void fsmc_read_buf_dma(struct fsmc_nand_data *host, u8 *buf,
60762306a36Sopenharmony_ci			      int len)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	dma_xfer(host, buf, len, DMA_FROM_DEVICE);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/*
61362306a36Sopenharmony_ci * fsmc_write_buf_dma - write buffer to chip
61462306a36Sopenharmony_ci * @host:	FSMC NAND controller
61562306a36Sopenharmony_ci * @buf:	data buffer
61662306a36Sopenharmony_ci * @len:	number of bytes to write
61762306a36Sopenharmony_ci */
61862306a36Sopenharmony_cistatic void fsmc_write_buf_dma(struct fsmc_nand_data *host, const u8 *buf,
61962306a36Sopenharmony_ci			       int len)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/*
62562306a36Sopenharmony_ci * fsmc_exec_op - hook called by the core to execute NAND operations
62662306a36Sopenharmony_ci *
62762306a36Sopenharmony_ci * This controller is simple enough and thus does not need to use the parser
62862306a36Sopenharmony_ci * provided by the core, instead, handle every situation here.
62962306a36Sopenharmony_ci */
63062306a36Sopenharmony_cistatic int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
63162306a36Sopenharmony_ci			bool check_only)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(chip);
63462306a36Sopenharmony_ci	const struct nand_op_instr *instr = NULL;
63562306a36Sopenharmony_ci	int ret = 0;
63662306a36Sopenharmony_ci	unsigned int op_id;
63762306a36Sopenharmony_ci	int i;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (check_only)
64062306a36Sopenharmony_ci		return 0;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	pr_debug("Executing operation [%d instructions]:\n", op->ninstrs);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	for (op_id = 0; op_id < op->ninstrs; op_id++) {
64562306a36Sopenharmony_ci		instr = &op->instrs[op_id];
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		nand_op_trace("  ", instr);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		switch (instr->type) {
65062306a36Sopenharmony_ci		case NAND_OP_CMD_INSTR:
65162306a36Sopenharmony_ci			writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va);
65262306a36Sopenharmony_ci			break;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci		case NAND_OP_ADDR_INSTR:
65562306a36Sopenharmony_ci			for (i = 0; i < instr->ctx.addr.naddrs; i++)
65662306a36Sopenharmony_ci				writeb_relaxed(instr->ctx.addr.addrs[i],
65762306a36Sopenharmony_ci					       host->addr_va);
65862306a36Sopenharmony_ci			break;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		case NAND_OP_DATA_IN_INSTR:
66162306a36Sopenharmony_ci			if (host->mode == USE_DMA_ACCESS)
66262306a36Sopenharmony_ci				fsmc_read_buf_dma(host, instr->ctx.data.buf.in,
66362306a36Sopenharmony_ci						  instr->ctx.data.len);
66462306a36Sopenharmony_ci			else
66562306a36Sopenharmony_ci				fsmc_read_buf(host, instr->ctx.data.buf.in,
66662306a36Sopenharmony_ci					      instr->ctx.data.len);
66762306a36Sopenharmony_ci			break;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		case NAND_OP_DATA_OUT_INSTR:
67062306a36Sopenharmony_ci			if (host->mode == USE_DMA_ACCESS)
67162306a36Sopenharmony_ci				fsmc_write_buf_dma(host,
67262306a36Sopenharmony_ci						   instr->ctx.data.buf.out,
67362306a36Sopenharmony_ci						   instr->ctx.data.len);
67462306a36Sopenharmony_ci			else
67562306a36Sopenharmony_ci				fsmc_write_buf(host, instr->ctx.data.buf.out,
67662306a36Sopenharmony_ci					       instr->ctx.data.len);
67762306a36Sopenharmony_ci			break;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		case NAND_OP_WAITRDY_INSTR:
68062306a36Sopenharmony_ci			ret = nand_soft_waitrdy(chip,
68162306a36Sopenharmony_ci						instr->ctx.waitrdy.timeout_ms);
68262306a36Sopenharmony_ci			break;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		if (instr->delay_ns)
68662306a36Sopenharmony_ci			ndelay(instr->delay_ns);
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return ret;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci/*
69362306a36Sopenharmony_ci * fsmc_read_page_hwecc
69462306a36Sopenharmony_ci * @chip:	nand chip info structure
69562306a36Sopenharmony_ci * @buf:	buffer to store read data
69662306a36Sopenharmony_ci * @oob_required:	caller expects OOB data read to chip->oob_poi
69762306a36Sopenharmony_ci * @page:	page number to read
69862306a36Sopenharmony_ci *
69962306a36Sopenharmony_ci * This routine is needed for fsmc version 8 as reading from NAND chip has to be
70062306a36Sopenharmony_ci * performed in a strict sequence as follows:
70162306a36Sopenharmony_ci * data(512 byte) -> ecc(13 byte)
70262306a36Sopenharmony_ci * After this read, fsmc hardware generates and reports error data bits(up to a
70362306a36Sopenharmony_ci * max of 8 bits)
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_cistatic int fsmc_read_page_hwecc(struct nand_chip *chip, u8 *buf,
70662306a36Sopenharmony_ci				int oob_required, int page)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
70962306a36Sopenharmony_ci	int i, j, s, stat, eccsize = chip->ecc.size;
71062306a36Sopenharmony_ci	int eccbytes = chip->ecc.bytes;
71162306a36Sopenharmony_ci	int eccsteps = chip->ecc.steps;
71262306a36Sopenharmony_ci	u8 *p = buf;
71362306a36Sopenharmony_ci	u8 *ecc_calc = chip->ecc.calc_buf;
71462306a36Sopenharmony_ci	u8 *ecc_code = chip->ecc.code_buf;
71562306a36Sopenharmony_ci	int off, len, ret, group = 0;
71662306a36Sopenharmony_ci	/*
71762306a36Sopenharmony_ci	 * ecc_oob is intentionally taken as u16. In 16bit devices, we
71862306a36Sopenharmony_ci	 * end up reading 14 bytes (7 words) from oob. The local array is
71962306a36Sopenharmony_ci	 * to maintain word alignment
72062306a36Sopenharmony_ci	 */
72162306a36Sopenharmony_ci	u16 ecc_oob[7];
72262306a36Sopenharmony_ci	u8 *oob = (u8 *)&ecc_oob[0];
72362306a36Sopenharmony_ci	unsigned int max_bitflips = 0;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
72662306a36Sopenharmony_ci		nand_read_page_op(chip, page, s * eccsize, NULL, 0);
72762306a36Sopenharmony_ci		chip->ecc.hwctl(chip, NAND_ECC_READ);
72862306a36Sopenharmony_ci		ret = nand_read_data_op(chip, p, eccsize, false, false);
72962306a36Sopenharmony_ci		if (ret)
73062306a36Sopenharmony_ci			return ret;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		for (j = 0; j < eccbytes;) {
73362306a36Sopenharmony_ci			struct mtd_oob_region oobregion;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci			ret = mtd_ooblayout_ecc(mtd, group++, &oobregion);
73662306a36Sopenharmony_ci			if (ret)
73762306a36Sopenharmony_ci				return ret;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci			off = oobregion.offset;
74062306a36Sopenharmony_ci			len = oobregion.length;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci			/*
74362306a36Sopenharmony_ci			 * length is intentionally kept a higher multiple of 2
74462306a36Sopenharmony_ci			 * to read at least 13 bytes even in case of 16 bit NAND
74562306a36Sopenharmony_ci			 * devices
74662306a36Sopenharmony_ci			 */
74762306a36Sopenharmony_ci			if (chip->options & NAND_BUSWIDTH_16)
74862306a36Sopenharmony_ci				len = roundup(len, 2);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci			nand_read_oob_op(chip, page, off, oob + j, len);
75162306a36Sopenharmony_ci			j += len;
75262306a36Sopenharmony_ci		}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		memcpy(&ecc_code[i], oob, chip->ecc.bytes);
75562306a36Sopenharmony_ci		chip->ecc.calculate(chip, p, &ecc_calc[i]);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]);
75862306a36Sopenharmony_ci		if (stat < 0) {
75962306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
76062306a36Sopenharmony_ci		} else {
76162306a36Sopenharmony_ci			mtd->ecc_stats.corrected += stat;
76262306a36Sopenharmony_ci			max_bitflips = max_t(unsigned int, max_bitflips, stat);
76362306a36Sopenharmony_ci		}
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	return max_bitflips;
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci/*
77062306a36Sopenharmony_ci * fsmc_bch8_correct_data
77162306a36Sopenharmony_ci * @mtd:	mtd info structure
77262306a36Sopenharmony_ci * @dat:	buffer of read data
77362306a36Sopenharmony_ci * @read_ecc:	ecc read from device spare area
77462306a36Sopenharmony_ci * @calc_ecc:	ecc calculated from read data
77562306a36Sopenharmony_ci *
77662306a36Sopenharmony_ci * calc_ecc is a 104 bit information containing maximum of 8 error
77762306a36Sopenharmony_ci * offset information of 13 bits each in 512 bytes of read data.
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_cistatic int fsmc_bch8_correct_data(struct nand_chip *chip, u8 *dat,
78062306a36Sopenharmony_ci				  u8 *read_ecc, u8 *calc_ecc)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(chip);
78362306a36Sopenharmony_ci	u32 err_idx[8];
78462306a36Sopenharmony_ci	u32 num_err, i;
78562306a36Sopenharmony_ci	u32 ecc1, ecc2, ecc3, ecc4;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* no bit flipping */
79062306a36Sopenharmony_ci	if (likely(num_err == 0))
79162306a36Sopenharmony_ci		return 0;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	/* too many errors */
79462306a36Sopenharmony_ci	if (unlikely(num_err > 8)) {
79562306a36Sopenharmony_ci		/*
79662306a36Sopenharmony_ci		 * This is a temporary erase check. A newly erased page read
79762306a36Sopenharmony_ci		 * would result in an ecc error because the oob data is also
79862306a36Sopenharmony_ci		 * erased to FF and the calculated ecc for an FF data is not
79962306a36Sopenharmony_ci		 * FF..FF.
80062306a36Sopenharmony_ci		 * This is a workaround to skip performing correction in case
80162306a36Sopenharmony_ci		 * data is FF..FF
80262306a36Sopenharmony_ci		 *
80362306a36Sopenharmony_ci		 * Logic:
80462306a36Sopenharmony_ci		 * For every page, each bit written as 0 is counted until these
80562306a36Sopenharmony_ci		 * number of bits are greater than 8 (the maximum correction
80662306a36Sopenharmony_ci		 * capability of FSMC for each 512 + 13 bytes)
80762306a36Sopenharmony_ci		 */
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci		int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8);
81062306a36Sopenharmony_ci		int bits_data = count_written_bits(dat, chip->ecc.size, 8);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		if ((bits_ecc + bits_data) <= 8) {
81362306a36Sopenharmony_ci			if (bits_data)
81462306a36Sopenharmony_ci				memset(dat, 0xff, chip->ecc.size);
81562306a36Sopenharmony_ci			return bits_data;
81662306a36Sopenharmony_ci		}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		return -EBADMSG;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/*
82262306a36Sopenharmony_ci	 * ------------------- calc_ecc[] bit wise -----------|--13 bits--|
82362306a36Sopenharmony_ci	 * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
82462306a36Sopenharmony_ci	 *
82562306a36Sopenharmony_ci	 * calc_ecc is a 104 bit information containing maximum of 8 error
82662306a36Sopenharmony_ci	 * offset information of 13 bits each. calc_ecc is copied into a
82762306a36Sopenharmony_ci	 * u64 array and error offset indexes are populated in err_idx
82862306a36Sopenharmony_ci	 * array
82962306a36Sopenharmony_ci	 */
83062306a36Sopenharmony_ci	ecc1 = readl_relaxed(host->regs_va + ECC1);
83162306a36Sopenharmony_ci	ecc2 = readl_relaxed(host->regs_va + ECC2);
83262306a36Sopenharmony_ci	ecc3 = readl_relaxed(host->regs_va + ECC3);
83362306a36Sopenharmony_ci	ecc4 = readl_relaxed(host->regs_va + STS);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	err_idx[0] = (ecc1 >> 0) & 0x1FFF;
83662306a36Sopenharmony_ci	err_idx[1] = (ecc1 >> 13) & 0x1FFF;
83762306a36Sopenharmony_ci	err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F);
83862306a36Sopenharmony_ci	err_idx[3] = (ecc2 >> 7) & 0x1FFF;
83962306a36Sopenharmony_ci	err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF);
84062306a36Sopenharmony_ci	err_idx[5] = (ecc3 >> 1) & 0x1FFF;
84162306a36Sopenharmony_ci	err_idx[6] = (ecc3 >> 14) & 0x1FFF;
84262306a36Sopenharmony_ci	err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	i = 0;
84562306a36Sopenharmony_ci	while (num_err--) {
84662306a36Sopenharmony_ci		err_idx[i] ^= 3;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci		if (err_idx[i] < chip->ecc.size * 8) {
84962306a36Sopenharmony_ci			int err = err_idx[i];
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci			dat[err >> 3] ^= BIT(err & 7);
85262306a36Sopenharmony_ci			i++;
85362306a36Sopenharmony_ci		}
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci	return i;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic bool filter(struct dma_chan *chan, void *slave)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	chan->private = slave;
86162306a36Sopenharmony_ci	return true;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int fsmc_nand_probe_config_dt(struct platform_device *pdev,
86562306a36Sopenharmony_ci				     struct fsmc_nand_data *host,
86662306a36Sopenharmony_ci				     struct nand_chip *nand)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
86962306a36Sopenharmony_ci	u32 val;
87062306a36Sopenharmony_ci	int ret;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	nand->options = 0;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (!of_property_read_u32(np, "bank-width", &val)) {
87562306a36Sopenharmony_ci		if (val == 2) {
87662306a36Sopenharmony_ci			nand->options |= NAND_BUSWIDTH_16;
87762306a36Sopenharmony_ci		} else if (val != 1) {
87862306a36Sopenharmony_ci			dev_err(&pdev->dev, "invalid bank-width %u\n", val);
87962306a36Sopenharmony_ci			return -EINVAL;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (of_property_read_bool(np, "nand-skip-bbtscan"))
88462306a36Sopenharmony_ci		nand->options |= NAND_SKIP_BBTSCAN;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	host->dev_timings = devm_kzalloc(&pdev->dev,
88762306a36Sopenharmony_ci					 sizeof(*host->dev_timings),
88862306a36Sopenharmony_ci					 GFP_KERNEL);
88962306a36Sopenharmony_ci	if (!host->dev_timings)
89062306a36Sopenharmony_ci		return -ENOMEM;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
89362306a36Sopenharmony_ci					sizeof(*host->dev_timings));
89462306a36Sopenharmony_ci	if (ret)
89562306a36Sopenharmony_ci		host->dev_timings = NULL;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* Set default NAND bank to 0 */
89862306a36Sopenharmony_ci	host->bank = 0;
89962306a36Sopenharmony_ci	if (!of_property_read_u32(np, "bank", &val)) {
90062306a36Sopenharmony_ci		if (val > 3) {
90162306a36Sopenharmony_ci			dev_err(&pdev->dev, "invalid bank %u\n", val);
90262306a36Sopenharmony_ci			return -EINVAL;
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci		host->bank = val;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci	return 0;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic int fsmc_nand_attach_chip(struct nand_chip *nand)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
91262306a36Sopenharmony_ci	struct fsmc_nand_data *host = nand_to_fsmc(nand);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID)
91562306a36Sopenharmony_ci		nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (!nand->ecc.size)
91862306a36Sopenharmony_ci		nand->ecc.size = 512;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (AMBA_REV_BITS(host->pid) >= 8) {
92162306a36Sopenharmony_ci		nand->ecc.read_page = fsmc_read_page_hwecc;
92262306a36Sopenharmony_ci		nand->ecc.calculate = fsmc_read_hwecc_ecc4;
92362306a36Sopenharmony_ci		nand->ecc.correct = fsmc_bch8_correct_data;
92462306a36Sopenharmony_ci		nand->ecc.bytes = 13;
92562306a36Sopenharmony_ci		nand->ecc.strength = 8;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (AMBA_REV_BITS(host->pid) >= 8) {
92962306a36Sopenharmony_ci		switch (mtd->oobsize) {
93062306a36Sopenharmony_ci		case 16:
93162306a36Sopenharmony_ci		case 64:
93262306a36Sopenharmony_ci		case 128:
93362306a36Sopenharmony_ci		case 224:
93462306a36Sopenharmony_ci		case 256:
93562306a36Sopenharmony_ci			break;
93662306a36Sopenharmony_ci		default:
93762306a36Sopenharmony_ci			dev_warn(host->dev,
93862306a36Sopenharmony_ci				 "No oob scheme defined for oobsize %d\n",
93962306a36Sopenharmony_ci				 mtd->oobsize);
94062306a36Sopenharmony_ci			return -EINVAL;
94162306a36Sopenharmony_ci		}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		return 0;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	switch (nand->ecc.engine_type) {
94962306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_HOST:
95062306a36Sopenharmony_ci		dev_info(host->dev, "Using 1-bit HW ECC scheme\n");
95162306a36Sopenharmony_ci		nand->ecc.calculate = fsmc_read_hwecc_ecc1;
95262306a36Sopenharmony_ci		nand->ecc.correct = fsmc_correct_ecc1;
95362306a36Sopenharmony_ci		nand->ecc.hwctl = fsmc_enable_hwecc;
95462306a36Sopenharmony_ci		nand->ecc.bytes = 3;
95562306a36Sopenharmony_ci		nand->ecc.strength = 1;
95662306a36Sopenharmony_ci		nand->ecc.options |= NAND_ECC_SOFT_HAMMING_SM_ORDER;
95762306a36Sopenharmony_ci		break;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_SOFT:
96062306a36Sopenharmony_ci		if (nand->ecc.algo == NAND_ECC_ALGO_BCH) {
96162306a36Sopenharmony_ci			dev_info(host->dev,
96262306a36Sopenharmony_ci				 "Using 4-bit SW BCH ECC scheme\n");
96362306a36Sopenharmony_ci			break;
96462306a36Sopenharmony_ci		}
96562306a36Sopenharmony_ci		break;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	case NAND_ECC_ENGINE_TYPE_ON_DIE:
96862306a36Sopenharmony_ci		break;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	default:
97162306a36Sopenharmony_ci		dev_err(host->dev, "Unsupported ECC mode!\n");
97262306a36Sopenharmony_ci		return -ENOTSUPP;
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	/*
97662306a36Sopenharmony_ci	 * Don't set layout for BCH4 SW ECC. This will be
97762306a36Sopenharmony_ci	 * generated later during BCH initialization.
97862306a36Sopenharmony_ci	 */
97962306a36Sopenharmony_ci	if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
98062306a36Sopenharmony_ci		switch (mtd->oobsize) {
98162306a36Sopenharmony_ci		case 16:
98262306a36Sopenharmony_ci		case 64:
98362306a36Sopenharmony_ci		case 128:
98462306a36Sopenharmony_ci			mtd_set_ooblayout(mtd,
98562306a36Sopenharmony_ci					  &fsmc_ecc1_ooblayout_ops);
98662306a36Sopenharmony_ci			break;
98762306a36Sopenharmony_ci		default:
98862306a36Sopenharmony_ci			dev_warn(host->dev,
98962306a36Sopenharmony_ci				 "No oob scheme defined for oobsize %d\n",
99062306a36Sopenharmony_ci				 mtd->oobsize);
99162306a36Sopenharmony_ci			return -EINVAL;
99262306a36Sopenharmony_ci		}
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return 0;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic const struct nand_controller_ops fsmc_nand_controller_ops = {
99962306a36Sopenharmony_ci	.attach_chip = fsmc_nand_attach_chip,
100062306a36Sopenharmony_ci	.exec_op = fsmc_exec_op,
100162306a36Sopenharmony_ci	.setup_interface = fsmc_setup_interface,
100262306a36Sopenharmony_ci};
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci/**
100562306a36Sopenharmony_ci * fsmc_nand_disable() - Disables the NAND bank
100662306a36Sopenharmony_ci * @host: The instance to disable
100762306a36Sopenharmony_ci */
100862306a36Sopenharmony_cistatic void fsmc_nand_disable(struct fsmc_nand_data *host)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	u32 val;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	val = readl(host->regs_va + FSMC_PC);
101362306a36Sopenharmony_ci	val &= ~FSMC_ENABLE;
101462306a36Sopenharmony_ci	writel(val, host->regs_va + FSMC_PC);
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci/*
101862306a36Sopenharmony_ci * fsmc_nand_probe - Probe function
101962306a36Sopenharmony_ci * @pdev:       platform device structure
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_cistatic int __init fsmc_nand_probe(struct platform_device *pdev)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct fsmc_nand_data *host;
102462306a36Sopenharmony_ci	struct mtd_info *mtd;
102562306a36Sopenharmony_ci	struct nand_chip *nand;
102662306a36Sopenharmony_ci	struct resource *res;
102762306a36Sopenharmony_ci	void __iomem *base;
102862306a36Sopenharmony_ci	dma_cap_mask_t mask;
102962306a36Sopenharmony_ci	int ret = 0;
103062306a36Sopenharmony_ci	u32 pid;
103162306a36Sopenharmony_ci	int i;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* Allocate memory for the device structure (and zero it) */
103462306a36Sopenharmony_ci	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
103562306a36Sopenharmony_ci	if (!host)
103662306a36Sopenharmony_ci		return -ENOMEM;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	nand = &host->nand;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	ret = fsmc_nand_probe_config_dt(pdev, host, nand);
104162306a36Sopenharmony_ci	if (ret)
104262306a36Sopenharmony_ci		return ret;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
104562306a36Sopenharmony_ci	host->data_va = devm_ioremap_resource(&pdev->dev, res);
104662306a36Sopenharmony_ci	if (IS_ERR(host->data_va))
104762306a36Sopenharmony_ci		return PTR_ERR(host->data_va);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	host->data_pa = (dma_addr_t)res->start;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
105262306a36Sopenharmony_ci	host->addr_va = devm_ioremap_resource(&pdev->dev, res);
105362306a36Sopenharmony_ci	if (IS_ERR(host->addr_va))
105462306a36Sopenharmony_ci		return PTR_ERR(host->addr_va);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
105762306a36Sopenharmony_ci	host->cmd_va = devm_ioremap_resource(&pdev->dev, res);
105862306a36Sopenharmony_ci	if (IS_ERR(host->cmd_va))
105962306a36Sopenharmony_ci		return PTR_ERR(host->cmd_va);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
106262306a36Sopenharmony_ci	base = devm_ioremap_resource(&pdev->dev, res);
106362306a36Sopenharmony_ci	if (IS_ERR(base))
106462306a36Sopenharmony_ci		return PTR_ERR(base);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	host->regs_va = base + FSMC_NOR_REG_SIZE +
106762306a36Sopenharmony_ci		(host->bank * FSMC_NAND_BANK_SZ);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	host->clk = devm_clk_get_enabled(&pdev->dev, NULL);
107062306a36Sopenharmony_ci	if (IS_ERR(host->clk)) {
107162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to fetch block clock\n");
107262306a36Sopenharmony_ci		return PTR_ERR(host->clk);
107362306a36Sopenharmony_ci	}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/*
107662306a36Sopenharmony_ci	 * This device ID is actually a common AMBA ID as used on the
107762306a36Sopenharmony_ci	 * AMBA PrimeCell bus. However it is not a PrimeCell.
107862306a36Sopenharmony_ci	 */
107962306a36Sopenharmony_ci	for (pid = 0, i = 0; i < 4; i++)
108062306a36Sopenharmony_ci		pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) &
108162306a36Sopenharmony_ci			255) << (i * 8);
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	host->pid = pid;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	dev_info(&pdev->dev,
108662306a36Sopenharmony_ci		 "FSMC device partno %03x, manufacturer %02x, revision %02x, config %02x\n",
108762306a36Sopenharmony_ci		 AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid),
108862306a36Sopenharmony_ci		 AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid));
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	host->dev = &pdev->dev;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	if (host->mode == USE_DMA_ACCESS)
109362306a36Sopenharmony_ci		init_completion(&host->dma_access_complete);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	/* Link all private pointers */
109662306a36Sopenharmony_ci	mtd = nand_to_mtd(&host->nand);
109762306a36Sopenharmony_ci	nand_set_flash_node(nand, pdev->dev.of_node);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	mtd->dev.parent = &pdev->dev;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	nand->badblockbits = 7;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	if (host->mode == USE_DMA_ACCESS) {
110462306a36Sopenharmony_ci		dma_cap_zero(mask);
110562306a36Sopenharmony_ci		dma_cap_set(DMA_MEMCPY, mask);
110662306a36Sopenharmony_ci		host->read_dma_chan = dma_request_channel(mask, filter, NULL);
110762306a36Sopenharmony_ci		if (!host->read_dma_chan) {
110862306a36Sopenharmony_ci			dev_err(&pdev->dev, "Unable to get read dma channel\n");
110962306a36Sopenharmony_ci			ret = -ENODEV;
111062306a36Sopenharmony_ci			goto disable_fsmc;
111162306a36Sopenharmony_ci		}
111262306a36Sopenharmony_ci		host->write_dma_chan = dma_request_channel(mask, filter, NULL);
111362306a36Sopenharmony_ci		if (!host->write_dma_chan) {
111462306a36Sopenharmony_ci			dev_err(&pdev->dev, "Unable to get write dma channel\n");
111562306a36Sopenharmony_ci			ret = -ENODEV;
111662306a36Sopenharmony_ci			goto release_dma_read_chan;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (host->dev_timings) {
112162306a36Sopenharmony_ci		fsmc_nand_setup(host, host->dev_timings);
112262306a36Sopenharmony_ci		nand->options |= NAND_KEEP_TIMINGS;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	nand_controller_init(&host->base);
112662306a36Sopenharmony_ci	host->base.ops = &fsmc_nand_controller_ops;
112762306a36Sopenharmony_ci	nand->controller = &host->base;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	/*
113062306a36Sopenharmony_ci	 * Scan to find existence of the device
113162306a36Sopenharmony_ci	 */
113262306a36Sopenharmony_ci	ret = nand_scan(nand, 1);
113362306a36Sopenharmony_ci	if (ret)
113462306a36Sopenharmony_ci		goto release_dma_write_chan;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	mtd->name = "nand";
113762306a36Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
113862306a36Sopenharmony_ci	if (ret)
113962306a36Sopenharmony_ci		goto cleanup_nand;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
114262306a36Sopenharmony_ci	dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	return 0;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_cicleanup_nand:
114762306a36Sopenharmony_ci	nand_cleanup(nand);
114862306a36Sopenharmony_cirelease_dma_write_chan:
114962306a36Sopenharmony_ci	if (host->mode == USE_DMA_ACCESS)
115062306a36Sopenharmony_ci		dma_release_channel(host->write_dma_chan);
115162306a36Sopenharmony_cirelease_dma_read_chan:
115262306a36Sopenharmony_ci	if (host->mode == USE_DMA_ACCESS)
115362306a36Sopenharmony_ci		dma_release_channel(host->read_dma_chan);
115462306a36Sopenharmony_cidisable_fsmc:
115562306a36Sopenharmony_ci	fsmc_nand_disable(host);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	return ret;
115862306a36Sopenharmony_ci}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci/*
116162306a36Sopenharmony_ci * Clean up routine
116262306a36Sopenharmony_ci */
116362306a36Sopenharmony_cistatic void fsmc_nand_remove(struct platform_device *pdev)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct fsmc_nand_data *host = platform_get_drvdata(pdev);
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (host) {
116862306a36Sopenharmony_ci		struct nand_chip *chip = &host->nand;
116962306a36Sopenharmony_ci		int ret;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		ret = mtd_device_unregister(nand_to_mtd(chip));
117262306a36Sopenharmony_ci		WARN_ON(ret);
117362306a36Sopenharmony_ci		nand_cleanup(chip);
117462306a36Sopenharmony_ci		fsmc_nand_disable(host);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci		if (host->mode == USE_DMA_ACCESS) {
117762306a36Sopenharmony_ci			dma_release_channel(host->write_dma_chan);
117862306a36Sopenharmony_ci			dma_release_channel(host->read_dma_chan);
117962306a36Sopenharmony_ci		}
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
118462306a36Sopenharmony_cistatic int fsmc_nand_suspend(struct device *dev)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	struct fsmc_nand_data *host = dev_get_drvdata(dev);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (host)
118962306a36Sopenharmony_ci		clk_disable_unprepare(host->clk);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	return 0;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic int fsmc_nand_resume(struct device *dev)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct fsmc_nand_data *host = dev_get_drvdata(dev);
119762306a36Sopenharmony_ci	int ret;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	if (host) {
120062306a36Sopenharmony_ci		ret = clk_prepare_enable(host->clk);
120162306a36Sopenharmony_ci		if (ret) {
120262306a36Sopenharmony_ci			dev_err(dev, "failed to enable clk\n");
120362306a36Sopenharmony_ci			return ret;
120462306a36Sopenharmony_ci		}
120562306a36Sopenharmony_ci		if (host->dev_timings)
120662306a36Sopenharmony_ci			fsmc_nand_setup(host, host->dev_timings);
120762306a36Sopenharmony_ci		nand_reset(&host->nand, 0);
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	return 0;
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci#endif
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic const struct of_device_id fsmc_nand_id_table[] = {
121762306a36Sopenharmony_ci	{ .compatible = "st,spear600-fsmc-nand" },
121862306a36Sopenharmony_ci	{ .compatible = "stericsson,fsmc-nand" },
121962306a36Sopenharmony_ci	{}
122062306a36Sopenharmony_ci};
122162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic struct platform_driver fsmc_nand_driver = {
122462306a36Sopenharmony_ci	.remove_new = fsmc_nand_remove,
122562306a36Sopenharmony_ci	.driver = {
122662306a36Sopenharmony_ci		.name = "fsmc-nand",
122762306a36Sopenharmony_ci		.of_match_table = fsmc_nand_id_table,
122862306a36Sopenharmony_ci		.pm = &fsmc_nand_pm_ops,
122962306a36Sopenharmony_ci	},
123062306a36Sopenharmony_ci};
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cimodule_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
123562306a36Sopenharmony_ciMODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
123662306a36Sopenharmony_ciMODULE_DESCRIPTION("NAND driver for SPEAr Platforms");
1237