162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Amlogic Meson Nand Flash Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2018 Amlogic, inc.
662306a36Sopenharmony_ci * Author: Liang Yang <liang.yang@amlogic.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/platform_device.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/interrupt.h>
1262306a36Sopenharmony_ci#include <linux/clk.h>
1362306a36Sopenharmony_ci#include <linux/clk-provider.h>
1462306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
1562306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1662306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1762306a36Sopenharmony_ci#include <linux/regmap.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/iopoll.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/sched/task_stack.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define NFC_REG_CMD		0x00
2562306a36Sopenharmony_ci#define NFC_CMD_IDLE		(0xc << 14)
2662306a36Sopenharmony_ci#define NFC_CMD_CLE		(0x5 << 14)
2762306a36Sopenharmony_ci#define NFC_CMD_ALE		(0x6 << 14)
2862306a36Sopenharmony_ci#define NFC_CMD_ADL		((0 << 16) | (3 << 20))
2962306a36Sopenharmony_ci#define NFC_CMD_ADH		((1 << 16) | (3 << 20))
3062306a36Sopenharmony_ci#define NFC_CMD_AIL		((2 << 16) | (3 << 20))
3162306a36Sopenharmony_ci#define NFC_CMD_AIH		((3 << 16) | (3 << 20))
3262306a36Sopenharmony_ci#define NFC_CMD_SEED		((8 << 16) | (3 << 20))
3362306a36Sopenharmony_ci#define NFC_CMD_M2N		((0 << 17) | (2 << 20))
3462306a36Sopenharmony_ci#define NFC_CMD_N2M		((1 << 17) | (2 << 20))
3562306a36Sopenharmony_ci#define NFC_CMD_RB		BIT(20)
3662306a36Sopenharmony_ci#define NFC_CMD_SCRAMBLER_ENABLE	BIT(19)
3762306a36Sopenharmony_ci#define NFC_CMD_SCRAMBLER_DISABLE	0
3862306a36Sopenharmony_ci#define NFC_CMD_SHORTMODE_DISABLE	0
3962306a36Sopenharmony_ci#define NFC_CMD_RB_INT		BIT(14)
4062306a36Sopenharmony_ci#define NFC_CMD_RB_INT_NO_PIN	((0xb << 10) | BIT(18) | BIT(16))
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define NFC_CMD_GET_SIZE(x)	(((x) >> 22) & GENMASK(4, 0))
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define NFC_REG_CFG		0x04
4562306a36Sopenharmony_ci#define NFC_REG_DADR		0x08
4662306a36Sopenharmony_ci#define NFC_REG_IADR		0x0c
4762306a36Sopenharmony_ci#define NFC_REG_BUF		0x10
4862306a36Sopenharmony_ci#define NFC_REG_INFO		0x14
4962306a36Sopenharmony_ci#define NFC_REG_DC		0x18
5062306a36Sopenharmony_ci#define NFC_REG_ADR		0x1c
5162306a36Sopenharmony_ci#define NFC_REG_DL		0x20
5262306a36Sopenharmony_ci#define NFC_REG_DH		0x24
5362306a36Sopenharmony_ci#define NFC_REG_CADR		0x28
5462306a36Sopenharmony_ci#define NFC_REG_SADR		0x2c
5562306a36Sopenharmony_ci#define NFC_REG_PINS		0x30
5662306a36Sopenharmony_ci#define NFC_REG_VER		0x38
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define NFC_RB_IRQ_EN		BIT(21)
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define CLK_DIV_SHIFT		0
6162306a36Sopenharmony_ci#define CLK_DIV_WIDTH		6
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages)	\
6462306a36Sopenharmony_ci	(								\
6562306a36Sopenharmony_ci		(cmd_dir)			|			\
6662306a36Sopenharmony_ci		(ran)				|			\
6762306a36Sopenharmony_ci		((bch) << 14)			|			\
6862306a36Sopenharmony_ci		((short_mode) << 13)		|			\
6962306a36Sopenharmony_ci		(((page_size) & 0x7f) << 6)	|			\
7062306a36Sopenharmony_ci		((pages) & 0x3f)					\
7162306a36Sopenharmony_ci	)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define GENCMDDADDRL(adl, addr)		((adl) | ((addr) & 0xffff))
7462306a36Sopenharmony_ci#define GENCMDDADDRH(adh, addr)		((adh) | (((addr) >> 16) & 0xffff))
7562306a36Sopenharmony_ci#define GENCMDIADDRL(ail, addr)		((ail) | ((addr) & 0xffff))
7662306a36Sopenharmony_ci#define GENCMDIADDRH(aih, addr)		((aih) | (((addr) >> 16) & 0xffff))
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define DMA_DIR(dir)		((dir) ? NFC_CMD_N2M : NFC_CMD_M2N)
7962306a36Sopenharmony_ci#define DMA_ADDR_ALIGN		8
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define ECC_CHECK_RETURN_FF	(-1)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define NAND_CE0		(0xe << 10)
8462306a36Sopenharmony_ci#define NAND_CE1		(0xd << 10)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define DMA_BUSY_TIMEOUT	0x100000
8762306a36Sopenharmony_ci#define CMD_FIFO_EMPTY_TIMEOUT	1000
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define MAX_CE_NUM		2
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* eMMC clock register, misc control */
9262306a36Sopenharmony_ci#define CLK_SELECT_NAND		BIT(31)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define NFC_CLK_CYCLE		6
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci/* nand flash controller delay 3 ns */
9762306a36Sopenharmony_ci#define NFC_DEFAULT_DELAY	3000
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define ROW_ADDER(page, index)	(((page) >> (8 * (index))) & 0xff)
10062306a36Sopenharmony_ci#define MAX_CYCLE_ADDRS		5
10162306a36Sopenharmony_ci#define DIRREAD			1
10262306a36Sopenharmony_ci#define DIRWRITE		0
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define ECC_PARITY_BCH8_512B	14
10562306a36Sopenharmony_ci#define ECC_COMPLETE            BIT(31)
10662306a36Sopenharmony_ci#define ECC_ERR_CNT(x)		(((x) >> 24) & GENMASK(5, 0))
10762306a36Sopenharmony_ci#define ECC_ZERO_CNT(x)		(((x) >> 16) & GENMASK(5, 0))
10862306a36Sopenharmony_ci#define ECC_UNCORRECTABLE	0x3f
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci#define PER_INFO_BYTE		8
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define NFC_CMD_RAW_LEN	GENMASK(13, 0)
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define NFC_COLUMN_ADDR_0	0
11562306a36Sopenharmony_ci#define NFC_COLUMN_ADDR_1	0
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct meson_nfc_nand_chip {
11862306a36Sopenharmony_ci	struct list_head node;
11962306a36Sopenharmony_ci	struct nand_chip nand;
12062306a36Sopenharmony_ci	unsigned long clk_rate;
12162306a36Sopenharmony_ci	unsigned long level1_divider;
12262306a36Sopenharmony_ci	u32 bus_timing;
12362306a36Sopenharmony_ci	u32 twb;
12462306a36Sopenharmony_ci	u32 tadl;
12562306a36Sopenharmony_ci	u32 tbers_max;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	u32 bch_mode;
12862306a36Sopenharmony_ci	u8 *data_buf;
12962306a36Sopenharmony_ci	__le64 *info_buf;
13062306a36Sopenharmony_ci	u32 nsels;
13162306a36Sopenharmony_ci	u8 sels[];
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistruct meson_nand_ecc {
13562306a36Sopenharmony_ci	u32 bch;
13662306a36Sopenharmony_ci	u32 strength;
13762306a36Sopenharmony_ci	u32 size;
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistruct meson_nfc_data {
14162306a36Sopenharmony_ci	const struct nand_ecc_caps *ecc_caps;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistruct meson_nfc_param {
14562306a36Sopenharmony_ci	u32 chip_select;
14662306a36Sopenharmony_ci	u32 rb_select;
14762306a36Sopenharmony_ci};
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistruct nand_rw_cmd {
15062306a36Sopenharmony_ci	u32 cmd0;
15162306a36Sopenharmony_ci	u32 addrs[MAX_CYCLE_ADDRS];
15262306a36Sopenharmony_ci	u32 cmd1;
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistruct nand_timing {
15662306a36Sopenharmony_ci	u32 twb;
15762306a36Sopenharmony_ci	u32 tadl;
15862306a36Sopenharmony_ci	u32 tbers_max;
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistruct meson_nfc {
16262306a36Sopenharmony_ci	struct nand_controller controller;
16362306a36Sopenharmony_ci	struct clk *core_clk;
16462306a36Sopenharmony_ci	struct clk *device_clk;
16562306a36Sopenharmony_ci	struct clk *nand_clk;
16662306a36Sopenharmony_ci	struct clk_divider nand_divider;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	unsigned long clk_rate;
16962306a36Sopenharmony_ci	u32 bus_timing;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	struct device *dev;
17262306a36Sopenharmony_ci	void __iomem *reg_base;
17362306a36Sopenharmony_ci	void __iomem *reg_clk;
17462306a36Sopenharmony_ci	struct completion completion;
17562306a36Sopenharmony_ci	struct list_head chips;
17662306a36Sopenharmony_ci	const struct meson_nfc_data *data;
17762306a36Sopenharmony_ci	struct meson_nfc_param param;
17862306a36Sopenharmony_ci	struct nand_timing timing;
17962306a36Sopenharmony_ci	union {
18062306a36Sopenharmony_ci		int cmd[32];
18162306a36Sopenharmony_ci		struct nand_rw_cmd rw;
18262306a36Sopenharmony_ci	} cmdfifo;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	dma_addr_t daddr;
18562306a36Sopenharmony_ci	dma_addr_t iaddr;
18662306a36Sopenharmony_ci	u32 info_bytes;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	unsigned long assigned_cs;
18962306a36Sopenharmony_ci	bool no_rb_pin;
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cienum {
19362306a36Sopenharmony_ci	NFC_ECC_BCH8_512	= 1,
19462306a36Sopenharmony_ci	NFC_ECC_BCH8_1K,
19562306a36Sopenharmony_ci	NFC_ECC_BCH24_1K,
19662306a36Sopenharmony_ci	NFC_ECC_BCH30_1K,
19762306a36Sopenharmony_ci	NFC_ECC_BCH40_1K,
19862306a36Sopenharmony_ci	NFC_ECC_BCH50_1K,
19962306a36Sopenharmony_ci	NFC_ECC_BCH60_1K,
20062306a36Sopenharmony_ci};
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci#define MESON_ECC_DATA(b, s, sz)	{ .bch = (b), .strength = (s), .size = (sz) }
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct meson_nand_ecc meson_ecc[] = {
20562306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH8_512, 8,  512),
20662306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH8_1K,  8,  1024),
20762306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24, 1024),
20862306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30, 1024),
20962306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40, 1024),
21062306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50, 1024),
21162306a36Sopenharmony_ci	MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60, 1024),
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic int meson_nand_calc_ecc_bytes(int step_size, int strength)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	int ecc_bytes;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (step_size == 512 && strength == 8)
21962306a36Sopenharmony_ci		return ECC_PARITY_BCH8_512B;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ecc_bytes = DIV_ROUND_UP(strength * fls(step_size * 8), 8);
22262306a36Sopenharmony_ci	ecc_bytes = ALIGN(ecc_bytes, 2);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return ecc_bytes;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ciNAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps,
22862306a36Sopenharmony_ci		     meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic const int axg_stepinfo_strengths[] = { 8 };
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic const struct nand_ecc_step_info axg_stepinfo[] = {
23362306a36Sopenharmony_ci	{
23462306a36Sopenharmony_ci		.stepsize = 1024,
23562306a36Sopenharmony_ci		.strengths = axg_stepinfo_strengths,
23662306a36Sopenharmony_ci		.nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
23762306a36Sopenharmony_ci	},
23862306a36Sopenharmony_ci	{
23962306a36Sopenharmony_ci		.stepsize = 512,
24062306a36Sopenharmony_ci		.strengths = axg_stepinfo_strengths,
24162306a36Sopenharmony_ci		.nstrengths = ARRAY_SIZE(axg_stepinfo_strengths)
24262306a36Sopenharmony_ci	},
24362306a36Sopenharmony_ci};
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic const struct nand_ecc_caps meson_axg_ecc_caps = {
24662306a36Sopenharmony_ci	.stepinfos = axg_stepinfo,
24762306a36Sopenharmony_ci	.nstepinfos = ARRAY_SIZE(axg_stepinfo),
24862306a36Sopenharmony_ci	.calc_ecc_bytes = meson_nand_calc_ecc_bytes,
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	return container_of(nand, struct meson_nfc_nand_chip, nand);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void meson_nfc_select_chip(struct nand_chip *nand, int chip)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
25962306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
26062306a36Sopenharmony_ci	int ret, value;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (chip < 0 || WARN_ON_ONCE(chip >= meson_chip->nsels))
26362306a36Sopenharmony_ci		return;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	nfc->param.chip_select = meson_chip->sels[chip] ? NAND_CE1 : NAND_CE0;
26662306a36Sopenharmony_ci	nfc->param.rb_select = nfc->param.chip_select;
26762306a36Sopenharmony_ci	nfc->timing.twb = meson_chip->twb;
26862306a36Sopenharmony_ci	nfc->timing.tadl = meson_chip->tadl;
26962306a36Sopenharmony_ci	nfc->timing.tbers_max = meson_chip->tbers_max;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (nfc->clk_rate != meson_chip->clk_rate) {
27262306a36Sopenharmony_ci		ret = clk_set_rate(nfc->nand_clk, meson_chip->clk_rate);
27362306a36Sopenharmony_ci		if (ret) {
27462306a36Sopenharmony_ci			dev_err(nfc->dev, "failed to set clock rate\n");
27562306a36Sopenharmony_ci			return;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci		nfc->clk_rate = meson_chip->clk_rate;
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci	if (nfc->bus_timing != meson_chip->bus_timing) {
28062306a36Sopenharmony_ci		value = (NFC_CLK_CYCLE - 1) | (meson_chip->bus_timing << 5);
28162306a36Sopenharmony_ci		writel(value, nfc->reg_base + NFC_REG_CFG);
28262306a36Sopenharmony_ci		writel((1 << 31), nfc->reg_base + NFC_REG_CMD);
28362306a36Sopenharmony_ci		nfc->bus_timing =  meson_chip->bus_timing;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic void meson_nfc_cmd_idle(struct meson_nfc *nfc, u32 time)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	writel(nfc->param.chip_select | NFC_CMD_IDLE | (time & 0x3ff),
29062306a36Sopenharmony_ci	       nfc->reg_base + NFC_REG_CMD);
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void meson_nfc_cmd_seed(struct meson_nfc *nfc, u32 seed)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	writel(NFC_CMD_SEED | (0xc2 + (seed & 0x7fff)),
29662306a36Sopenharmony_ci	       nfc->reg_base + NFC_REG_CMD);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void meson_nfc_cmd_access(struct nand_chip *nand, int raw, bool dir,
30062306a36Sopenharmony_ci				 int scrambler)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
30362306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
30462306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
30562306a36Sopenharmony_ci	u32 bch = meson_chip->bch_mode, cmd;
30662306a36Sopenharmony_ci	int len = mtd->writesize, pagesize, pages;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	pagesize = nand->ecc.size;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (raw) {
31162306a36Sopenharmony_ci		len = mtd->writesize + mtd->oobsize;
31262306a36Sopenharmony_ci		cmd = len | scrambler | DMA_DIR(dir);
31362306a36Sopenharmony_ci		writel(cmd, nfc->reg_base + NFC_REG_CMD);
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	pages = len / nand->ecc.size;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	cmd = CMDRWGEN(DMA_DIR(dir), scrambler, bch,
32062306a36Sopenharmony_ci		       NFC_CMD_SHORTMODE_DISABLE, pagesize, pages);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic void meson_nfc_drain_cmd(struct meson_nfc *nfc)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	/*
32862306a36Sopenharmony_ci	 * Insert two commands to make sure all valid commands are finished.
32962306a36Sopenharmony_ci	 *
33062306a36Sopenharmony_ci	 * The Nand flash controller is designed as two stages pipleline -
33162306a36Sopenharmony_ci	 *  a) fetch and b) excute.
33262306a36Sopenharmony_ci	 * There might be cases when the driver see command queue is empty,
33362306a36Sopenharmony_ci	 * but the Nand flash controller still has two commands buffered,
33462306a36Sopenharmony_ci	 * one is fetched into NFC request queue (ready to run), and another
33562306a36Sopenharmony_ci	 * is actively executing. So pushing 2 "IDLE" commands guarantees that
33662306a36Sopenharmony_ci	 * the pipeline is emptied.
33762306a36Sopenharmony_ci	 */
33862306a36Sopenharmony_ci	meson_nfc_cmd_idle(nfc, 0);
33962306a36Sopenharmony_ci	meson_nfc_cmd_idle(nfc, 0);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int meson_nfc_wait_cmd_finish(struct meson_nfc *nfc,
34362306a36Sopenharmony_ci				     unsigned int timeout_ms)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	u32 cmd_size = 0;
34662306a36Sopenharmony_ci	int ret;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* wait cmd fifo is empty */
34962306a36Sopenharmony_ci	ret = readl_relaxed_poll_timeout(nfc->reg_base + NFC_REG_CMD, cmd_size,
35062306a36Sopenharmony_ci					 !NFC_CMD_GET_SIZE(cmd_size),
35162306a36Sopenharmony_ci					 10, timeout_ms * 1000);
35262306a36Sopenharmony_ci	if (ret)
35362306a36Sopenharmony_ci		dev_err(nfc->dev, "wait for empty CMD FIFO time out\n");
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return ret;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int meson_nfc_wait_dma_finish(struct meson_nfc *nfc)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	meson_nfc_drain_cmd(nfc);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return meson_nfc_wait_cmd_finish(nfc, DMA_BUSY_TIMEOUT);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic u8 *meson_nfc_oob_ptr(struct nand_chip *nand, int i)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
36862306a36Sopenharmony_ci	int len;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	len = nand->ecc.size * (i + 1) + (nand->ecc.bytes + 2) * i;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return meson_chip->data_buf + len;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic u8 *meson_nfc_data_ptr(struct nand_chip *nand, int i)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
37862306a36Sopenharmony_ci	int len, temp;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	temp = nand->ecc.size + nand->ecc.bytes;
38162306a36Sopenharmony_ci	len = (temp + 2) * i;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return meson_chip->data_buf + len;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void meson_nfc_get_data_oob(struct nand_chip *nand,
38762306a36Sopenharmony_ci				   u8 *buf, u8 *oobbuf)
38862306a36Sopenharmony_ci{
38962306a36Sopenharmony_ci	int i, oob_len = 0;
39062306a36Sopenharmony_ci	u8 *dsrc, *osrc;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	oob_len = nand->ecc.bytes + 2;
39362306a36Sopenharmony_ci	for (i = 0; i < nand->ecc.steps; i++) {
39462306a36Sopenharmony_ci		if (buf) {
39562306a36Sopenharmony_ci			dsrc = meson_nfc_data_ptr(nand, i);
39662306a36Sopenharmony_ci			memcpy(buf, dsrc, nand->ecc.size);
39762306a36Sopenharmony_ci			buf += nand->ecc.size;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci		osrc = meson_nfc_oob_ptr(nand, i);
40062306a36Sopenharmony_ci		memcpy(oobbuf, osrc, oob_len);
40162306a36Sopenharmony_ci		oobbuf += oob_len;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic void meson_nfc_set_data_oob(struct nand_chip *nand,
40662306a36Sopenharmony_ci				   const u8 *buf, u8 *oobbuf)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	int i, oob_len = 0;
40962306a36Sopenharmony_ci	u8 *dsrc, *osrc;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	oob_len = nand->ecc.bytes + 2;
41262306a36Sopenharmony_ci	for (i = 0; i < nand->ecc.steps; i++) {
41362306a36Sopenharmony_ci		if (buf) {
41462306a36Sopenharmony_ci			dsrc = meson_nfc_data_ptr(nand, i);
41562306a36Sopenharmony_ci			memcpy(dsrc, buf, nand->ecc.size);
41662306a36Sopenharmony_ci			buf += nand->ecc.size;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci		osrc = meson_nfc_oob_ptr(nand, i);
41962306a36Sopenharmony_ci		memcpy(osrc, oobbuf, oob_len);
42062306a36Sopenharmony_ci		oobbuf += oob_len;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int meson_nfc_wait_no_rb_pin(struct nand_chip *nand, int timeout_ms,
42562306a36Sopenharmony_ci				    bool need_cmd_read0)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
42862306a36Sopenharmony_ci	u32 cmd, cfg;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
43162306a36Sopenharmony_ci	meson_nfc_drain_cmd(nfc);
43262306a36Sopenharmony_ci	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	cfg = readl(nfc->reg_base + NFC_REG_CFG);
43562306a36Sopenharmony_ci	cfg |= NFC_RB_IRQ_EN;
43662306a36Sopenharmony_ci	writel(cfg, nfc->reg_base + NFC_REG_CFG);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	reinit_completion(&nfc->completion);
43962306a36Sopenharmony_ci	nand_status_op(nand, NULL);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* use the max erase time as the maximum clock for waiting R/B */
44262306a36Sopenharmony_ci	cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max;
44362306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&nfc->completion,
44662306a36Sopenharmony_ci					 msecs_to_jiffies(timeout_ms)))
44762306a36Sopenharmony_ci		return -ETIMEDOUT;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (need_cmd_read0)
45062306a36Sopenharmony_ci		nand_exit_status_op(nand);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	u32 cmd, cfg;
45862306a36Sopenharmony_ci	int ret = 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	meson_nfc_cmd_idle(nfc, nfc->timing.twb);
46162306a36Sopenharmony_ci	meson_nfc_drain_cmd(nfc);
46262306a36Sopenharmony_ci	meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	cfg = readl(nfc->reg_base + NFC_REG_CFG);
46562306a36Sopenharmony_ci	cfg |= NFC_RB_IRQ_EN;
46662306a36Sopenharmony_ci	writel(cfg, nfc->reg_base + NFC_REG_CFG);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	reinit_completion(&nfc->completion);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* use the max erase time as the maximum clock for waiting R/B */
47162306a36Sopenharmony_ci	cmd = NFC_CMD_RB | NFC_CMD_RB_INT
47262306a36Sopenharmony_ci		| nfc->param.chip_select | nfc->timing.tbers_max;
47362306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = wait_for_completion_timeout(&nfc->completion,
47662306a36Sopenharmony_ci					  msecs_to_jiffies(timeout_ms));
47762306a36Sopenharmony_ci	if (ret == 0)
47862306a36Sopenharmony_ci		ret = -1;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return ret;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int meson_nfc_queue_rb(struct nand_chip *nand, int timeout_ms,
48462306a36Sopenharmony_ci			      bool need_cmd_read0)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (nfc->no_rb_pin) {
48962306a36Sopenharmony_ci		/* This mode is used when there is no wired R/B pin.
49062306a36Sopenharmony_ci		 * It works like 'nand_soft_waitrdy()', but instead of
49162306a36Sopenharmony_ci		 * polling NAND_CMD_STATUS bit in the software loop,
49262306a36Sopenharmony_ci		 * it will wait for interrupt - controllers checks IO
49362306a36Sopenharmony_ci		 * bus and when it detects NAND_CMD_STATUS on it, it
49462306a36Sopenharmony_ci		 * raises interrupt. After interrupt, NAND_CMD_READ0 is
49562306a36Sopenharmony_ci		 * sent as terminator of the ready waiting procedure if
49662306a36Sopenharmony_ci		 * needed (for all cases except page programming - this
49762306a36Sopenharmony_ci		 * is reason of 'need_cmd_read0' flag).
49862306a36Sopenharmony_ci		 */
49962306a36Sopenharmony_ci		return meson_nfc_wait_no_rb_pin(nand, timeout_ms,
50062306a36Sopenharmony_ci						need_cmd_read0);
50162306a36Sopenharmony_ci	} else {
50262306a36Sopenharmony_ci		return meson_nfc_wait_rb_pin(nfc, timeout_ms);
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
50962306a36Sopenharmony_ci	__le64 *info;
51062306a36Sopenharmony_ci	int i, count;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
51362306a36Sopenharmony_ci		info = &meson_chip->info_buf[i];
51462306a36Sopenharmony_ci		*info |= oob_buf[count];
51562306a36Sopenharmony_ci		*info |= oob_buf[count + 1] << 8;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
52262306a36Sopenharmony_ci	__le64 *info;
52362306a36Sopenharmony_ci	int i, count;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) {
52662306a36Sopenharmony_ci		info = &meson_chip->info_buf[i];
52762306a36Sopenharmony_ci		oob_buf[count] = *info;
52862306a36Sopenharmony_ci		oob_buf[count + 1] = *info >> 8;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int meson_nfc_ecc_correct(struct nand_chip *nand, u32 *bitflips,
53362306a36Sopenharmony_ci				 u64 *correct_bitmap)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
53662306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
53762306a36Sopenharmony_ci	__le64 *info;
53862306a36Sopenharmony_ci	int ret = 0, i;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	for (i = 0; i < nand->ecc.steps; i++) {
54162306a36Sopenharmony_ci		info = &meson_chip->info_buf[i];
54262306a36Sopenharmony_ci		if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) {
54362306a36Sopenharmony_ci			mtd->ecc_stats.corrected += ECC_ERR_CNT(*info);
54462306a36Sopenharmony_ci			*bitflips = max_t(u32, *bitflips, ECC_ERR_CNT(*info));
54562306a36Sopenharmony_ci			*correct_bitmap |= BIT_ULL(i);
54662306a36Sopenharmony_ci			continue;
54762306a36Sopenharmony_ci		}
54862306a36Sopenharmony_ci		if ((nand->options & NAND_NEED_SCRAMBLING) &&
54962306a36Sopenharmony_ci		    ECC_ZERO_CNT(*info) < nand->ecc.strength) {
55062306a36Sopenharmony_ci			mtd->ecc_stats.corrected += ECC_ZERO_CNT(*info);
55162306a36Sopenharmony_ci			*bitflips = max_t(u32, *bitflips,
55262306a36Sopenharmony_ci					  ECC_ZERO_CNT(*info));
55362306a36Sopenharmony_ci			ret = ECC_CHECK_RETURN_FF;
55462306a36Sopenharmony_ci		} else {
55562306a36Sopenharmony_ci			ret = -EBADMSG;
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci	return ret;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic int meson_nfc_dma_buffer_setup(struct nand_chip *nand, void *databuf,
56262306a36Sopenharmony_ci				      int datalen, void *infobuf, int infolen,
56362306a36Sopenharmony_ci				      enum dma_data_direction dir)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
56662306a36Sopenharmony_ci	u32 cmd;
56762306a36Sopenharmony_ci	int ret = 0;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	nfc->daddr = dma_map_single(nfc->dev, databuf, datalen, dir);
57062306a36Sopenharmony_ci	ret = dma_mapping_error(nfc->dev, nfc->daddr);
57162306a36Sopenharmony_ci	if (ret) {
57262306a36Sopenharmony_ci		dev_err(nfc->dev, "DMA mapping error\n");
57362306a36Sopenharmony_ci		return ret;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	cmd = GENCMDDADDRL(NFC_CMD_ADL, nfc->daddr);
57662306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	cmd = GENCMDDADDRH(NFC_CMD_ADH, nfc->daddr);
57962306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (infobuf) {
58262306a36Sopenharmony_ci		nfc->iaddr = dma_map_single(nfc->dev, infobuf, infolen, dir);
58362306a36Sopenharmony_ci		ret = dma_mapping_error(nfc->dev, nfc->iaddr);
58462306a36Sopenharmony_ci		if (ret) {
58562306a36Sopenharmony_ci			dev_err(nfc->dev, "DMA mapping error\n");
58662306a36Sopenharmony_ci			dma_unmap_single(nfc->dev,
58762306a36Sopenharmony_ci					 nfc->daddr, datalen, dir);
58862306a36Sopenharmony_ci			return ret;
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci		nfc->info_bytes = infolen;
59162306a36Sopenharmony_ci		cmd = GENCMDIADDRL(NFC_CMD_AIL, nfc->iaddr);
59262306a36Sopenharmony_ci		writel(cmd, nfc->reg_base + NFC_REG_CMD);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		cmd = GENCMDIADDRH(NFC_CMD_AIH, nfc->iaddr);
59562306a36Sopenharmony_ci		writel(cmd, nfc->reg_base + NFC_REG_CMD);
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	return ret;
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void meson_nfc_dma_buffer_release(struct nand_chip *nand,
60262306a36Sopenharmony_ci					 int datalen, int infolen,
60362306a36Sopenharmony_ci					 enum dma_data_direction dir)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	dma_unmap_single(nfc->dev, nfc->daddr, datalen, dir);
60862306a36Sopenharmony_ci	if (infolen) {
60962306a36Sopenharmony_ci		dma_unmap_single(nfc->dev, nfc->iaddr, infolen, dir);
61062306a36Sopenharmony_ci		nfc->info_bytes = 0;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int meson_nfc_read_buf(struct nand_chip *nand, u8 *buf, int len)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
61762306a36Sopenharmony_ci	int ret = 0;
61862306a36Sopenharmony_ci	u32 cmd;
61962306a36Sopenharmony_ci	u8 *info;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	info = kzalloc(PER_INFO_BYTE, GFP_KERNEL);
62262306a36Sopenharmony_ci	if (!info)
62362306a36Sopenharmony_ci		return -ENOMEM;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	ret = meson_nfc_dma_buffer_setup(nand, buf, len, info,
62662306a36Sopenharmony_ci					 PER_INFO_BYTE, DMA_FROM_DEVICE);
62762306a36Sopenharmony_ci	if (ret)
62862306a36Sopenharmony_ci		goto out;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	cmd = NFC_CMD_N2M | len;
63162306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	meson_nfc_drain_cmd(nfc);
63462306a36Sopenharmony_ci	meson_nfc_wait_cmd_finish(nfc, 1000);
63562306a36Sopenharmony_ci	meson_nfc_dma_buffer_release(nand, len, PER_INFO_BYTE, DMA_FROM_DEVICE);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ciout:
63862306a36Sopenharmony_ci	kfree(info);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	return ret;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int meson_nfc_write_buf(struct nand_chip *nand, u8 *buf, int len)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
64662306a36Sopenharmony_ci	int ret = 0;
64762306a36Sopenharmony_ci	u32 cmd;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	ret = meson_nfc_dma_buffer_setup(nand, buf, len, NULL,
65062306a36Sopenharmony_ci					 0, DMA_TO_DEVICE);
65162306a36Sopenharmony_ci	if (ret)
65262306a36Sopenharmony_ci		return ret;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	cmd = NFC_CMD_M2N | len;
65562306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	meson_nfc_drain_cmd(nfc);
65862306a36Sopenharmony_ci	meson_nfc_wait_cmd_finish(nfc, 1000);
65962306a36Sopenharmony_ci	meson_nfc_dma_buffer_release(nand, len, 0, DMA_TO_DEVICE);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return ret;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand,
66562306a36Sopenharmony_ci						int page, bool in)
66662306a36Sopenharmony_ci{
66762306a36Sopenharmony_ci	const struct nand_sdr_timings *sdr =
66862306a36Sopenharmony_ci		nand_get_sdr_timings(nand_get_interface_config(nand));
66962306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
67062306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
67162306a36Sopenharmony_ci	u32 *addrs = nfc->cmdfifo.rw.addrs;
67262306a36Sopenharmony_ci	u32 cs = nfc->param.chip_select;
67362306a36Sopenharmony_ci	u32 cmd0, cmd_num, row_start;
67462306a36Sopenharmony_ci	int i;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	cmd_num = sizeof(struct nand_rw_cmd) / sizeof(int);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	cmd0 = in ? NAND_CMD_READ0 : NAND_CMD_SEQIN;
67962306a36Sopenharmony_ci	nfc->cmdfifo.rw.cmd0 = cs | NFC_CMD_CLE | cmd0;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	addrs[0] = cs | NFC_CMD_ALE | NFC_COLUMN_ADDR_0;
68262306a36Sopenharmony_ci	if (mtd->writesize <= 512) {
68362306a36Sopenharmony_ci		cmd_num--;
68462306a36Sopenharmony_ci		row_start = 1;
68562306a36Sopenharmony_ci	} else {
68662306a36Sopenharmony_ci		addrs[1] = cs | NFC_CMD_ALE | NFC_COLUMN_ADDR_1;
68762306a36Sopenharmony_ci		row_start = 2;
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	addrs[row_start] = cs | NFC_CMD_ALE | ROW_ADDER(page, 0);
69162306a36Sopenharmony_ci	addrs[row_start + 1] = cs | NFC_CMD_ALE | ROW_ADDER(page, 1);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (nand->options & NAND_ROW_ADDR_3)
69462306a36Sopenharmony_ci		addrs[row_start + 2] =
69562306a36Sopenharmony_ci			cs | NFC_CMD_ALE | ROW_ADDER(page, 2);
69662306a36Sopenharmony_ci	else
69762306a36Sopenharmony_ci		cmd_num--;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* subtract cmd1 */
70062306a36Sopenharmony_ci	cmd_num--;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	for (i = 0; i < cmd_num; i++)
70362306a36Sopenharmony_ci		writel_relaxed(nfc->cmdfifo.cmd[i],
70462306a36Sopenharmony_ci			       nfc->reg_base + NFC_REG_CMD);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (in) {
70762306a36Sopenharmony_ci		nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART;
70862306a36Sopenharmony_ci		writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD);
70962306a36Sopenharmony_ci		meson_nfc_queue_rb(nand, PSEC_TO_MSEC(sdr->tR_max), true);
71062306a36Sopenharmony_ci	} else {
71162306a36Sopenharmony_ci		meson_nfc_cmd_idle(nfc, nfc->timing.tadl);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	return 0;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int meson_nfc_write_page_sub(struct nand_chip *nand,
71862306a36Sopenharmony_ci				    int page, int raw)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	const struct nand_sdr_timings *sdr =
72162306a36Sopenharmony_ci		nand_get_sdr_timings(nand_get_interface_config(nand));
72262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
72362306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
72462306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
72562306a36Sopenharmony_ci	int data_len, info_len;
72662306a36Sopenharmony_ci	u32 cmd;
72762306a36Sopenharmony_ci	int ret;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	meson_nfc_select_chip(nand, nand->cur_cs);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	data_len =  mtd->writesize + mtd->oobsize;
73262306a36Sopenharmony_ci	info_len = nand->ecc.steps * PER_INFO_BYTE;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRWRITE);
73562306a36Sopenharmony_ci	if (ret)
73662306a36Sopenharmony_ci		return ret;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
73962306a36Sopenharmony_ci					 data_len, meson_chip->info_buf,
74062306a36Sopenharmony_ci					 info_len, DMA_TO_DEVICE);
74162306a36Sopenharmony_ci	if (ret)
74262306a36Sopenharmony_ci		return ret;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (nand->options & NAND_NEED_SCRAMBLING) {
74562306a36Sopenharmony_ci		meson_nfc_cmd_seed(nfc, page);
74662306a36Sopenharmony_ci		meson_nfc_cmd_access(nand, raw, DIRWRITE,
74762306a36Sopenharmony_ci				     NFC_CMD_SCRAMBLER_ENABLE);
74862306a36Sopenharmony_ci	} else {
74962306a36Sopenharmony_ci		meson_nfc_cmd_access(nand, raw, DIRWRITE,
75062306a36Sopenharmony_ci				     NFC_CMD_SCRAMBLER_DISABLE);
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG;
75462306a36Sopenharmony_ci	writel(cmd, nfc->reg_base + NFC_REG_CMD);
75562306a36Sopenharmony_ci	meson_nfc_queue_rb(nand, PSEC_TO_MSEC(sdr->tPROG_max), false);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return ret;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int meson_nfc_write_page_raw(struct nand_chip *nand, const u8 *buf,
76362306a36Sopenharmony_ci				    int oob_required, int page)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	u8 *oob_buf = nand->oob_poi;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	meson_nfc_set_data_oob(nand, buf, oob_buf);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	return meson_nfc_write_page_sub(nand, page, 1);
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic int meson_nfc_write_page_hwecc(struct nand_chip *nand,
77362306a36Sopenharmony_ci				      const u8 *buf, int oob_required, int page)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
77662306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
77762306a36Sopenharmony_ci	u8 *oob_buf = nand->oob_poi;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	memcpy(meson_chip->data_buf, buf, mtd->writesize);
78062306a36Sopenharmony_ci	memset(meson_chip->info_buf, 0, nand->ecc.steps * PER_INFO_BYTE);
78162306a36Sopenharmony_ci	meson_nfc_set_user_byte(nand, oob_buf);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	return meson_nfc_write_page_sub(nand, page, 0);
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic void meson_nfc_check_ecc_pages_valid(struct meson_nfc *nfc,
78762306a36Sopenharmony_ci					    struct nand_chip *nand, int raw)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
79062306a36Sopenharmony_ci	__le64 *info;
79162306a36Sopenharmony_ci	u32 neccpages;
79262306a36Sopenharmony_ci	int ret;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	neccpages = raw ? 1 : nand->ecc.steps;
79562306a36Sopenharmony_ci	info = &meson_chip->info_buf[neccpages - 1];
79662306a36Sopenharmony_ci	do {
79762306a36Sopenharmony_ci		usleep_range(10, 15);
79862306a36Sopenharmony_ci		/* info is updated by nfc dma engine*/
79962306a36Sopenharmony_ci		smp_rmb();
80062306a36Sopenharmony_ci		dma_sync_single_for_cpu(nfc->dev, nfc->iaddr, nfc->info_bytes,
80162306a36Sopenharmony_ci					DMA_FROM_DEVICE);
80262306a36Sopenharmony_ci		ret = *info & ECC_COMPLETE;
80362306a36Sopenharmony_ci	} while (!ret);
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int meson_nfc_read_page_sub(struct nand_chip *nand,
80762306a36Sopenharmony_ci				   int page, int raw)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
81062306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
81162306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
81262306a36Sopenharmony_ci	int data_len, info_len;
81362306a36Sopenharmony_ci	int ret;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	meson_nfc_select_chip(nand, nand->cur_cs);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	data_len =  mtd->writesize + mtd->oobsize;
81862306a36Sopenharmony_ci	info_len = nand->ecc.steps * PER_INFO_BYTE;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	ret = meson_nfc_rw_cmd_prepare_and_execute(nand, page, DIRREAD);
82162306a36Sopenharmony_ci	if (ret)
82262306a36Sopenharmony_ci		return ret;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	ret = meson_nfc_dma_buffer_setup(nand, meson_chip->data_buf,
82562306a36Sopenharmony_ci					 data_len, meson_chip->info_buf,
82662306a36Sopenharmony_ci					 info_len, DMA_FROM_DEVICE);
82762306a36Sopenharmony_ci	if (ret)
82862306a36Sopenharmony_ci		return ret;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (nand->options & NAND_NEED_SCRAMBLING) {
83162306a36Sopenharmony_ci		meson_nfc_cmd_seed(nfc, page);
83262306a36Sopenharmony_ci		meson_nfc_cmd_access(nand, raw, DIRREAD,
83362306a36Sopenharmony_ci				     NFC_CMD_SCRAMBLER_ENABLE);
83462306a36Sopenharmony_ci	} else {
83562306a36Sopenharmony_ci		meson_nfc_cmd_access(nand, raw, DIRREAD,
83662306a36Sopenharmony_ci				     NFC_CMD_SCRAMBLER_DISABLE);
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	ret = meson_nfc_wait_dma_finish(nfc);
84062306a36Sopenharmony_ci	meson_nfc_check_ecc_pages_valid(nfc, nand, raw);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_FROM_DEVICE);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return ret;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic int meson_nfc_read_page_raw(struct nand_chip *nand, u8 *buf,
84862306a36Sopenharmony_ci				   int oob_required, int page)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	u8 *oob_buf = nand->oob_poi;
85162306a36Sopenharmony_ci	int ret;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	ret = meson_nfc_read_page_sub(nand, page, 1);
85462306a36Sopenharmony_ci	if (ret)
85562306a36Sopenharmony_ci		return ret;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	meson_nfc_get_data_oob(nand, buf, oob_buf);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return 0;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int meson_nfc_read_page_hwecc(struct nand_chip *nand, u8 *buf,
86362306a36Sopenharmony_ci				     int oob_required, int page)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
86662306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
86762306a36Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &nand->ecc;
86862306a36Sopenharmony_ci	u64 correct_bitmap = 0;
86962306a36Sopenharmony_ci	u32 bitflips = 0;
87062306a36Sopenharmony_ci	u8 *oob_buf = nand->oob_poi;
87162306a36Sopenharmony_ci	int ret, i;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	ret = meson_nfc_read_page_sub(nand, page, 0);
87462306a36Sopenharmony_ci	if (ret)
87562306a36Sopenharmony_ci		return ret;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	meson_nfc_get_user_byte(nand, oob_buf);
87862306a36Sopenharmony_ci	ret = meson_nfc_ecc_correct(nand, &bitflips, &correct_bitmap);
87962306a36Sopenharmony_ci	if (ret == ECC_CHECK_RETURN_FF) {
88062306a36Sopenharmony_ci		if (buf)
88162306a36Sopenharmony_ci			memset(buf, 0xff, mtd->writesize);
88262306a36Sopenharmony_ci		memset(oob_buf, 0xff, mtd->oobsize);
88362306a36Sopenharmony_ci	} else if (ret < 0) {
88462306a36Sopenharmony_ci		if ((nand->options & NAND_NEED_SCRAMBLING) || !buf) {
88562306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
88662306a36Sopenharmony_ci			return bitflips;
88762306a36Sopenharmony_ci		}
88862306a36Sopenharmony_ci		ret  = meson_nfc_read_page_raw(nand, buf, 0, page);
88962306a36Sopenharmony_ci		if (ret)
89062306a36Sopenharmony_ci			return ret;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci		for (i = 0; i < nand->ecc.steps ; i++) {
89362306a36Sopenharmony_ci			u8 *data = buf + i * ecc->size;
89462306a36Sopenharmony_ci			u8 *oob = nand->oob_poi + i * (ecc->bytes + 2);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci			if (correct_bitmap & BIT_ULL(i))
89762306a36Sopenharmony_ci				continue;
89862306a36Sopenharmony_ci			ret = nand_check_erased_ecc_chunk(data,	ecc->size,
89962306a36Sopenharmony_ci							  oob, ecc->bytes + 2,
90062306a36Sopenharmony_ci							  NULL, 0,
90162306a36Sopenharmony_ci							  ecc->strength);
90262306a36Sopenharmony_ci			if (ret < 0) {
90362306a36Sopenharmony_ci				mtd->ecc_stats.failed++;
90462306a36Sopenharmony_ci			} else {
90562306a36Sopenharmony_ci				mtd->ecc_stats.corrected += ret;
90662306a36Sopenharmony_ci				bitflips =  max_t(u32, bitflips, ret);
90762306a36Sopenharmony_ci			}
90862306a36Sopenharmony_ci		}
90962306a36Sopenharmony_ci	} else if (buf && buf != meson_chip->data_buf) {
91062306a36Sopenharmony_ci		memcpy(buf, meson_chip->data_buf, mtd->writesize);
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	return bitflips;
91462306a36Sopenharmony_ci}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_cistatic int meson_nfc_read_oob_raw(struct nand_chip *nand, int page)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	return meson_nfc_read_page_raw(nand, NULL, 1, page);
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_cistatic int meson_nfc_read_oob(struct nand_chip *nand, int page)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	return meson_nfc_read_page_hwecc(nand, NULL, 1, page);
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic bool meson_nfc_is_buffer_dma_safe(const void *buffer)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	if ((uintptr_t)buffer % DMA_ADDR_ALIGN)
92962306a36Sopenharmony_ci		return false;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (virt_addr_valid(buffer) && (!object_is_on_stack(buffer)))
93262306a36Sopenharmony_ci		return true;
93362306a36Sopenharmony_ci	return false;
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_cistatic void *
93762306a36Sopenharmony_cimeson_nand_op_get_dma_safe_input_buf(const struct nand_op_instr *instr)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR))
94062306a36Sopenharmony_ci		return NULL;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (meson_nfc_is_buffer_dma_safe(instr->ctx.data.buf.in))
94362306a36Sopenharmony_ci		return instr->ctx.data.buf.in;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return kzalloc(instr->ctx.data.len, GFP_KERNEL);
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void
94962306a36Sopenharmony_cimeson_nand_op_put_dma_safe_input_buf(const struct nand_op_instr *instr,
95062306a36Sopenharmony_ci				     void *buf)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	if (WARN_ON(instr->type != NAND_OP_DATA_IN_INSTR) ||
95362306a36Sopenharmony_ci	    WARN_ON(!buf))
95462306a36Sopenharmony_ci		return;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (buf == instr->ctx.data.buf.in)
95762306a36Sopenharmony_ci		return;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	memcpy(instr->ctx.data.buf.in, buf, instr->ctx.data.len);
96062306a36Sopenharmony_ci	kfree(buf);
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic void *
96462306a36Sopenharmony_cimeson_nand_op_get_dma_safe_output_buf(const struct nand_op_instr *instr)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR))
96762306a36Sopenharmony_ci		return NULL;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	if (meson_nfc_is_buffer_dma_safe(instr->ctx.data.buf.out))
97062306a36Sopenharmony_ci		return (void *)instr->ctx.data.buf.out;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return kmemdup(instr->ctx.data.buf.out,
97362306a36Sopenharmony_ci		       instr->ctx.data.len, GFP_KERNEL);
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic void
97762306a36Sopenharmony_cimeson_nand_op_put_dma_safe_output_buf(const struct nand_op_instr *instr,
97862306a36Sopenharmony_ci				      const void *buf)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	if (WARN_ON(instr->type != NAND_OP_DATA_OUT_INSTR) ||
98162306a36Sopenharmony_ci	    WARN_ON(!buf))
98262306a36Sopenharmony_ci		return;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (buf != instr->ctx.data.buf.out)
98562306a36Sopenharmony_ci		kfree(buf);
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int meson_nfc_check_op(struct nand_chip *chip,
98962306a36Sopenharmony_ci			      const struct nand_operation *op)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	int op_id;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	for (op_id = 0; op_id < op->ninstrs; op_id++) {
99462306a36Sopenharmony_ci		const struct nand_op_instr *instr;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci		instr = &op->instrs[op_id];
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		switch (instr->type) {
99962306a36Sopenharmony_ci		case NAND_OP_DATA_IN_INSTR:
100062306a36Sopenharmony_ci		case NAND_OP_DATA_OUT_INSTR:
100162306a36Sopenharmony_ci			if (instr->ctx.data.len > NFC_CMD_RAW_LEN)
100262306a36Sopenharmony_ci				return -ENOTSUPP;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci			break;
100562306a36Sopenharmony_ci		default:
100662306a36Sopenharmony_ci			break;
100762306a36Sopenharmony_ci		}
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	return 0;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic int meson_nfc_exec_op(struct nand_chip *nand,
101462306a36Sopenharmony_ci			     const struct nand_operation *op, bool check_only)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
101762306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
101862306a36Sopenharmony_ci	const struct nand_op_instr *instr = NULL;
101962306a36Sopenharmony_ci	void *buf;
102062306a36Sopenharmony_ci	u32 op_id, delay_idle, cmd;
102162306a36Sopenharmony_ci	int err;
102262306a36Sopenharmony_ci	int i;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	err = meson_nfc_check_op(nand, op);
102562306a36Sopenharmony_ci	if (err)
102662306a36Sopenharmony_ci		return err;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (check_only)
102962306a36Sopenharmony_ci		return 0;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	meson_nfc_select_chip(nand, op->cs);
103262306a36Sopenharmony_ci	for (op_id = 0; op_id < op->ninstrs; op_id++) {
103362306a36Sopenharmony_ci		instr = &op->instrs[op_id];
103462306a36Sopenharmony_ci		delay_idle = DIV_ROUND_UP(PSEC_TO_NSEC(instr->delay_ns),
103562306a36Sopenharmony_ci					  meson_chip->level1_divider *
103662306a36Sopenharmony_ci					  NFC_CLK_CYCLE);
103762306a36Sopenharmony_ci		switch (instr->type) {
103862306a36Sopenharmony_ci		case NAND_OP_CMD_INSTR:
103962306a36Sopenharmony_ci			cmd = nfc->param.chip_select | NFC_CMD_CLE;
104062306a36Sopenharmony_ci			cmd |= instr->ctx.cmd.opcode & 0xff;
104162306a36Sopenharmony_ci			writel(cmd, nfc->reg_base + NFC_REG_CMD);
104262306a36Sopenharmony_ci			meson_nfc_cmd_idle(nfc, delay_idle);
104362306a36Sopenharmony_ci			break;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci		case NAND_OP_ADDR_INSTR:
104662306a36Sopenharmony_ci			for (i = 0; i < instr->ctx.addr.naddrs; i++) {
104762306a36Sopenharmony_ci				cmd = nfc->param.chip_select | NFC_CMD_ALE;
104862306a36Sopenharmony_ci				cmd |= instr->ctx.addr.addrs[i] & 0xff;
104962306a36Sopenharmony_ci				writel(cmd, nfc->reg_base + NFC_REG_CMD);
105062306a36Sopenharmony_ci			}
105162306a36Sopenharmony_ci			meson_nfc_cmd_idle(nfc, delay_idle);
105262306a36Sopenharmony_ci			break;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		case NAND_OP_DATA_IN_INSTR:
105562306a36Sopenharmony_ci			buf = meson_nand_op_get_dma_safe_input_buf(instr);
105662306a36Sopenharmony_ci			if (!buf)
105762306a36Sopenharmony_ci				return -ENOMEM;
105862306a36Sopenharmony_ci			meson_nfc_read_buf(nand, buf, instr->ctx.data.len);
105962306a36Sopenharmony_ci			meson_nand_op_put_dma_safe_input_buf(instr, buf);
106062306a36Sopenharmony_ci			break;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		case NAND_OP_DATA_OUT_INSTR:
106362306a36Sopenharmony_ci			buf = meson_nand_op_get_dma_safe_output_buf(instr);
106462306a36Sopenharmony_ci			if (!buf)
106562306a36Sopenharmony_ci				return -ENOMEM;
106662306a36Sopenharmony_ci			meson_nfc_write_buf(nand, buf, instr->ctx.data.len);
106762306a36Sopenharmony_ci			meson_nand_op_put_dma_safe_output_buf(instr, buf);
106862306a36Sopenharmony_ci			break;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		case NAND_OP_WAITRDY_INSTR:
107162306a36Sopenharmony_ci			meson_nfc_queue_rb(nand, instr->ctx.waitrdy.timeout_ms,
107262306a36Sopenharmony_ci					   true);
107362306a36Sopenharmony_ci			if (instr->delay_ns)
107462306a36Sopenharmony_ci				meson_nfc_cmd_idle(nfc, delay_idle);
107562306a36Sopenharmony_ci			break;
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci	meson_nfc_wait_cmd_finish(nfc, 1000);
107962306a36Sopenharmony_ci	return 0;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic int meson_ooblayout_ecc(struct mtd_info *mtd, int section,
108362306a36Sopenharmony_ci			       struct mtd_oob_region *oobregion)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	struct nand_chip *nand = mtd_to_nand(mtd);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (section >= nand->ecc.steps)
108862306a36Sopenharmony_ci		return -ERANGE;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	oobregion->offset =  2 + (section * (2 + nand->ecc.bytes));
109162306a36Sopenharmony_ci	oobregion->length = nand->ecc.bytes;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return 0;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic int meson_ooblayout_free(struct mtd_info *mtd, int section,
109762306a36Sopenharmony_ci				struct mtd_oob_region *oobregion)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct nand_chip *nand = mtd_to_nand(mtd);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (section >= nand->ecc.steps)
110262306a36Sopenharmony_ci		return -ERANGE;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	oobregion->offset = section * (2 + nand->ecc.bytes);
110562306a36Sopenharmony_ci	oobregion->length = 2;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	return 0;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops meson_ooblayout_ops = {
111162306a36Sopenharmony_ci	.ecc = meson_ooblayout_ecc,
111262306a36Sopenharmony_ci	.free = meson_ooblayout_free,
111362306a36Sopenharmony_ci};
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic int meson_nfc_clk_init(struct meson_nfc *nfc)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	struct clk_parent_data nfc_divider_parent_data[1] = {0};
111862306a36Sopenharmony_ci	struct clk_init_data init = {0};
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	/* request core clock */
112262306a36Sopenharmony_ci	nfc->core_clk = devm_clk_get(nfc->dev, "core");
112362306a36Sopenharmony_ci	if (IS_ERR(nfc->core_clk)) {
112462306a36Sopenharmony_ci		dev_err(nfc->dev, "failed to get core clock\n");
112562306a36Sopenharmony_ci		return PTR_ERR(nfc->core_clk);
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	nfc->device_clk = devm_clk_get(nfc->dev, "device");
112962306a36Sopenharmony_ci	if (IS_ERR(nfc->device_clk)) {
113062306a36Sopenharmony_ci		dev_err(nfc->dev, "failed to get device clock\n");
113162306a36Sopenharmony_ci		return PTR_ERR(nfc->device_clk);
113262306a36Sopenharmony_ci	}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	init.name = devm_kasprintf(nfc->dev,
113562306a36Sopenharmony_ci				   GFP_KERNEL, "%s#div",
113662306a36Sopenharmony_ci				   dev_name(nfc->dev));
113762306a36Sopenharmony_ci	if (!init.name)
113862306a36Sopenharmony_ci		return -ENOMEM;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	init.ops = &clk_divider_ops;
114162306a36Sopenharmony_ci	nfc_divider_parent_data[0].fw_name = "device";
114262306a36Sopenharmony_ci	init.parent_data = nfc_divider_parent_data;
114362306a36Sopenharmony_ci	init.num_parents = 1;
114462306a36Sopenharmony_ci	nfc->nand_divider.reg = nfc->reg_clk;
114562306a36Sopenharmony_ci	nfc->nand_divider.shift = CLK_DIV_SHIFT;
114662306a36Sopenharmony_ci	nfc->nand_divider.width = CLK_DIV_WIDTH;
114762306a36Sopenharmony_ci	nfc->nand_divider.hw.init = &init;
114862306a36Sopenharmony_ci	nfc->nand_divider.flags = CLK_DIVIDER_ONE_BASED |
114962306a36Sopenharmony_ci				  CLK_DIVIDER_ROUND_CLOSEST |
115062306a36Sopenharmony_ci				  CLK_DIVIDER_ALLOW_ZERO;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	nfc->nand_clk = devm_clk_register(nfc->dev, &nfc->nand_divider.hw);
115362306a36Sopenharmony_ci	if (IS_ERR(nfc->nand_clk))
115462306a36Sopenharmony_ci		return PTR_ERR(nfc->nand_clk);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
115762306a36Sopenharmony_ci	writel(CLK_SELECT_NAND | readl(nfc->reg_clk),
115862306a36Sopenharmony_ci	       nfc->reg_clk);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	ret = clk_prepare_enable(nfc->core_clk);
116162306a36Sopenharmony_ci	if (ret) {
116262306a36Sopenharmony_ci		dev_err(nfc->dev, "failed to enable core clock\n");
116362306a36Sopenharmony_ci		return ret;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	ret = clk_prepare_enable(nfc->device_clk);
116762306a36Sopenharmony_ci	if (ret) {
116862306a36Sopenharmony_ci		dev_err(nfc->dev, "failed to enable device clock\n");
116962306a36Sopenharmony_ci		goto err_device_clk;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	ret = clk_prepare_enable(nfc->nand_clk);
117362306a36Sopenharmony_ci	if (ret) {
117462306a36Sopenharmony_ci		dev_err(nfc->dev, "pre enable NFC divider fail\n");
117562306a36Sopenharmony_ci		goto err_nand_clk;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	ret = clk_set_rate(nfc->nand_clk, 24000000);
117962306a36Sopenharmony_ci	if (ret)
118062306a36Sopenharmony_ci		goto err_disable_clk;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	return 0;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cierr_disable_clk:
118562306a36Sopenharmony_ci	clk_disable_unprepare(nfc->nand_clk);
118662306a36Sopenharmony_cierr_nand_clk:
118762306a36Sopenharmony_ci	clk_disable_unprepare(nfc->device_clk);
118862306a36Sopenharmony_cierr_device_clk:
118962306a36Sopenharmony_ci	clk_disable_unprepare(nfc->core_clk);
119062306a36Sopenharmony_ci	return ret;
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic void meson_nfc_disable_clk(struct meson_nfc *nfc)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	clk_disable_unprepare(nfc->nand_clk);
119662306a36Sopenharmony_ci	clk_disable_unprepare(nfc->device_clk);
119762306a36Sopenharmony_ci	clk_disable_unprepare(nfc->core_clk);
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic void meson_nfc_free_buffer(struct nand_chip *nand)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	kfree(meson_chip->info_buf);
120562306a36Sopenharmony_ci	kfree(meson_chip->data_buf);
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic int meson_chip_buffer_init(struct nand_chip *nand)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
121162306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
121262306a36Sopenharmony_ci	u32 page_bytes, info_bytes, nsectors;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	nsectors = mtd->writesize / nand->ecc.size;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	page_bytes =  mtd->writesize + mtd->oobsize;
121762306a36Sopenharmony_ci	info_bytes = nsectors * PER_INFO_BYTE;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	meson_chip->data_buf = kmalloc(page_bytes, GFP_KERNEL);
122062306a36Sopenharmony_ci	if (!meson_chip->data_buf)
122162306a36Sopenharmony_ci		return -ENOMEM;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	meson_chip->info_buf = kmalloc(info_bytes, GFP_KERNEL);
122462306a36Sopenharmony_ci	if (!meson_chip->info_buf) {
122562306a36Sopenharmony_ci		kfree(meson_chip->data_buf);
122662306a36Sopenharmony_ci		return -ENOMEM;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	return 0;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic
123362306a36Sopenharmony_ciint meson_nfc_setup_interface(struct nand_chip *nand, int csline,
123462306a36Sopenharmony_ci			      const struct nand_interface_config *conf)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
123762306a36Sopenharmony_ci	const struct nand_sdr_timings *timings;
123862306a36Sopenharmony_ci	u32 div, bt_min, bt_max, tbers_clocks;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	timings = nand_get_sdr_timings(conf);
124162306a36Sopenharmony_ci	if (IS_ERR(timings))
124262306a36Sopenharmony_ci		return -ENOTSUPP;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (csline == NAND_DATA_IFACE_CHECK_ONLY)
124562306a36Sopenharmony_ci		return 0;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	div = DIV_ROUND_UP((timings->tRC_min / 1000), NFC_CLK_CYCLE);
124862306a36Sopenharmony_ci	bt_min = (timings->tREA_max + NFC_DEFAULT_DELAY) / div;
124962306a36Sopenharmony_ci	bt_max = (NFC_DEFAULT_DELAY + timings->tRHOH_min +
125062306a36Sopenharmony_ci		  timings->tRC_min / 2) / div;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	meson_chip->twb = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tWB_max),
125362306a36Sopenharmony_ci				       div * NFC_CLK_CYCLE);
125462306a36Sopenharmony_ci	meson_chip->tadl = DIV_ROUND_UP(PSEC_TO_NSEC(timings->tADL_min),
125562306a36Sopenharmony_ci					div * NFC_CLK_CYCLE);
125662306a36Sopenharmony_ci	tbers_clocks = DIV_ROUND_UP_ULL(PSEC_TO_NSEC(timings->tBERS_max),
125762306a36Sopenharmony_ci					div * NFC_CLK_CYCLE);
125862306a36Sopenharmony_ci	meson_chip->tbers_max = ilog2(tbers_clocks);
125962306a36Sopenharmony_ci	if (!is_power_of_2(tbers_clocks))
126062306a36Sopenharmony_ci		meson_chip->tbers_max++;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	bt_min = DIV_ROUND_UP(bt_min, 1000);
126362306a36Sopenharmony_ci	bt_max = DIV_ROUND_UP(bt_max, 1000);
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (bt_max < bt_min)
126662306a36Sopenharmony_ci		return -EINVAL;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	meson_chip->level1_divider = div;
126962306a36Sopenharmony_ci	meson_chip->clk_rate = 1000000000 / meson_chip->level1_divider;
127062306a36Sopenharmony_ci	meson_chip->bus_timing = (bt_min + bt_max) / 2 + 1;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return 0;
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cistatic int meson_nand_bch_mode(struct nand_chip *nand)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
127862306a36Sopenharmony_ci	int i;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	if (nand->ecc.strength > 60 || nand->ecc.strength < 8)
128162306a36Sopenharmony_ci		return -EINVAL;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) {
128462306a36Sopenharmony_ci		if (meson_ecc[i].strength == nand->ecc.strength &&
128562306a36Sopenharmony_ci		    meson_ecc[i].size == nand->ecc.size) {
128662306a36Sopenharmony_ci			meson_chip->bch_mode = meson_ecc[i].bch;
128762306a36Sopenharmony_ci			return 0;
128862306a36Sopenharmony_ci		}
128962306a36Sopenharmony_ci	}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return -EINVAL;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic void meson_nand_detach_chip(struct nand_chip *nand)
129562306a36Sopenharmony_ci{
129662306a36Sopenharmony_ci	meson_nfc_free_buffer(nand);
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cistatic int meson_nand_attach_chip(struct nand_chip *nand)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct meson_nfc *nfc = nand_get_controller_data(nand);
130262306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip = to_meson_nand(nand);
130362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(nand);
130462306a36Sopenharmony_ci	int raw_writesize;
130562306a36Sopenharmony_ci	int ret;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (!mtd->name) {
130862306a36Sopenharmony_ci		mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
130962306a36Sopenharmony_ci					   "%s:nand%d",
131062306a36Sopenharmony_ci					   dev_name(nfc->dev),
131162306a36Sopenharmony_ci					   meson_chip->sels[0]);
131262306a36Sopenharmony_ci		if (!mtd->name)
131362306a36Sopenharmony_ci			return -ENOMEM;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	raw_writesize = mtd->writesize + mtd->oobsize;
131762306a36Sopenharmony_ci	if (raw_writesize > NFC_CMD_RAW_LEN) {
131862306a36Sopenharmony_ci		dev_err(nfc->dev, "too big write size in raw mode: %d > %ld\n",
131962306a36Sopenharmony_ci			raw_writesize, NFC_CMD_RAW_LEN);
132062306a36Sopenharmony_ci		return -EINVAL;
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (nand->bbt_options & NAND_BBT_USE_FLASH)
132462306a36Sopenharmony_ci		nand->bbt_options |= NAND_BBT_NO_OOB;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	nand->options |= NAND_NO_SUBPAGE_WRITE;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	ret = nand_ecc_choose_conf(nand, nfc->data->ecc_caps,
132962306a36Sopenharmony_ci				   mtd->oobsize - 2);
133062306a36Sopenharmony_ci	if (ret) {
133162306a36Sopenharmony_ci		dev_err(nfc->dev, "failed to ECC init\n");
133262306a36Sopenharmony_ci		return -EINVAL;
133362306a36Sopenharmony_ci	}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	mtd_set_ooblayout(mtd, &meson_ooblayout_ops);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	ret = meson_nand_bch_mode(nand);
133862306a36Sopenharmony_ci	if (ret)
133962306a36Sopenharmony_ci		return -EINVAL;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
134262306a36Sopenharmony_ci	nand->ecc.write_page_raw = meson_nfc_write_page_raw;
134362306a36Sopenharmony_ci	nand->ecc.write_page = meson_nfc_write_page_hwecc;
134462306a36Sopenharmony_ci	nand->ecc.write_oob_raw = nand_write_oob_std;
134562306a36Sopenharmony_ci	nand->ecc.write_oob = nand_write_oob_std;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	nand->ecc.read_page_raw = meson_nfc_read_page_raw;
134862306a36Sopenharmony_ci	nand->ecc.read_page = meson_nfc_read_page_hwecc;
134962306a36Sopenharmony_ci	nand->ecc.read_oob_raw = meson_nfc_read_oob_raw;
135062306a36Sopenharmony_ci	nand->ecc.read_oob = meson_nfc_read_oob;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (nand->options & NAND_BUSWIDTH_16) {
135362306a36Sopenharmony_ci		dev_err(nfc->dev, "16bits bus width not supported");
135462306a36Sopenharmony_ci		return -EINVAL;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci	ret = meson_chip_buffer_init(nand);
135762306a36Sopenharmony_ci	if (ret)
135862306a36Sopenharmony_ci		return -ENOMEM;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	return ret;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_cistatic const struct nand_controller_ops meson_nand_controller_ops = {
136462306a36Sopenharmony_ci	.attach_chip = meson_nand_attach_chip,
136562306a36Sopenharmony_ci	.detach_chip = meson_nand_detach_chip,
136662306a36Sopenharmony_ci	.setup_interface = meson_nfc_setup_interface,
136762306a36Sopenharmony_ci	.exec_op = meson_nfc_exec_op,
136862306a36Sopenharmony_ci};
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic int
137162306a36Sopenharmony_cimeson_nfc_nand_chip_init(struct device *dev,
137262306a36Sopenharmony_ci			 struct meson_nfc *nfc, struct device_node *np)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip;
137562306a36Sopenharmony_ci	struct nand_chip *nand;
137662306a36Sopenharmony_ci	struct mtd_info *mtd;
137762306a36Sopenharmony_ci	int ret, i;
137862306a36Sopenharmony_ci	u32 tmp, nsels;
137962306a36Sopenharmony_ci	u32 nand_rb_val = 0;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32));
138262306a36Sopenharmony_ci	if (!nsels || nsels > MAX_CE_NUM) {
138362306a36Sopenharmony_ci		dev_err(dev, "invalid register property size\n");
138462306a36Sopenharmony_ci		return -EINVAL;
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	meson_chip = devm_kzalloc(dev, struct_size(meson_chip, sels, nsels),
138862306a36Sopenharmony_ci				  GFP_KERNEL);
138962306a36Sopenharmony_ci	if (!meson_chip)
139062306a36Sopenharmony_ci		return -ENOMEM;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	meson_chip->nsels = nsels;
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	for (i = 0; i < nsels; i++) {
139562306a36Sopenharmony_ci		ret = of_property_read_u32_index(np, "reg", i, &tmp);
139662306a36Sopenharmony_ci		if (ret) {
139762306a36Sopenharmony_ci			dev_err(dev, "could not retrieve register property: %d\n",
139862306a36Sopenharmony_ci				ret);
139962306a36Sopenharmony_ci			return ret;
140062306a36Sopenharmony_ci		}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci		if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
140362306a36Sopenharmony_ci			dev_err(dev, "CS %d already assigned\n", tmp);
140462306a36Sopenharmony_ci			return -EINVAL;
140562306a36Sopenharmony_ci		}
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	nand = &meson_chip->nand;
140962306a36Sopenharmony_ci	nand->controller = &nfc->controller;
141062306a36Sopenharmony_ci	nand->controller->ops = &meson_nand_controller_ops;
141162306a36Sopenharmony_ci	nand_set_flash_node(nand, np);
141262306a36Sopenharmony_ci	nand_set_controller_data(nand, nfc);
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	nand->options |= NAND_USES_DMA;
141562306a36Sopenharmony_ci	mtd = nand_to_mtd(nand);
141662306a36Sopenharmony_ci	mtd->owner = THIS_MODULE;
141762306a36Sopenharmony_ci	mtd->dev.parent = dev;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	ret = of_property_read_u32(np, "nand-rb", &nand_rb_val);
142062306a36Sopenharmony_ci	if (ret == -EINVAL)
142162306a36Sopenharmony_ci		nfc->no_rb_pin = true;
142262306a36Sopenharmony_ci	else if (ret)
142362306a36Sopenharmony_ci		return ret;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	if (nand_rb_val)
142662306a36Sopenharmony_ci		return -EINVAL;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	ret = nand_scan(nand, nsels);
142962306a36Sopenharmony_ci	if (ret)
143062306a36Sopenharmony_ci		return ret;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
143362306a36Sopenharmony_ci	if (ret) {
143462306a36Sopenharmony_ci		dev_err(dev, "failed to register MTD device: %d\n", ret);
143562306a36Sopenharmony_ci		nand_cleanup(nand);
143662306a36Sopenharmony_ci		return ret;
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	list_add_tail(&meson_chip->node, &nfc->chips);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	return 0;
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic void meson_nfc_nand_chip_cleanup(struct meson_nfc *nfc)
144562306a36Sopenharmony_ci{
144662306a36Sopenharmony_ci	struct meson_nfc_nand_chip *meson_chip;
144762306a36Sopenharmony_ci	struct mtd_info *mtd;
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	while (!list_empty(&nfc->chips)) {
145062306a36Sopenharmony_ci		meson_chip = list_first_entry(&nfc->chips,
145162306a36Sopenharmony_ci					      struct meson_nfc_nand_chip, node);
145262306a36Sopenharmony_ci		mtd = nand_to_mtd(&meson_chip->nand);
145362306a36Sopenharmony_ci		WARN_ON(mtd_device_unregister(mtd));
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		nand_cleanup(&meson_chip->nand);
145662306a36Sopenharmony_ci		list_del(&meson_chip->node);
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_cistatic int meson_nfc_nand_chips_init(struct device *dev,
146162306a36Sopenharmony_ci				     struct meson_nfc *nfc)
146262306a36Sopenharmony_ci{
146362306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
146462306a36Sopenharmony_ci	struct device_node *nand_np;
146562306a36Sopenharmony_ci	int ret;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	for_each_child_of_node(np, nand_np) {
146862306a36Sopenharmony_ci		ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
146962306a36Sopenharmony_ci		if (ret) {
147062306a36Sopenharmony_ci			meson_nfc_nand_chip_cleanup(nfc);
147162306a36Sopenharmony_ci			of_node_put(nand_np);
147262306a36Sopenharmony_ci			return ret;
147362306a36Sopenharmony_ci		}
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	return 0;
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_cistatic irqreturn_t meson_nfc_irq(int irq, void *id)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	struct meson_nfc *nfc = id;
148262306a36Sopenharmony_ci	u32 cfg;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	cfg = readl(nfc->reg_base + NFC_REG_CFG);
148562306a36Sopenharmony_ci	if (!(cfg & NFC_RB_IRQ_EN))
148662306a36Sopenharmony_ci		return IRQ_NONE;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	cfg &= ~(NFC_RB_IRQ_EN);
148962306a36Sopenharmony_ci	writel(cfg, nfc->reg_base + NFC_REG_CFG);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	complete(&nfc->completion);
149262306a36Sopenharmony_ci	return IRQ_HANDLED;
149362306a36Sopenharmony_ci}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_cistatic const struct meson_nfc_data meson_gxl_data = {
149662306a36Sopenharmony_ci	.ecc_caps = &meson_gxl_ecc_caps,
149762306a36Sopenharmony_ci};
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cistatic const struct meson_nfc_data meson_axg_data = {
150062306a36Sopenharmony_ci	.ecc_caps = &meson_axg_ecc_caps,
150162306a36Sopenharmony_ci};
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic const struct of_device_id meson_nfc_id_table[] = {
150462306a36Sopenharmony_ci	{
150562306a36Sopenharmony_ci		.compatible = "amlogic,meson-gxl-nfc",
150662306a36Sopenharmony_ci		.data = &meson_gxl_data,
150762306a36Sopenharmony_ci	}, {
150862306a36Sopenharmony_ci		.compatible = "amlogic,meson-axg-nfc",
150962306a36Sopenharmony_ci		.data = &meson_axg_data,
151062306a36Sopenharmony_ci	},
151162306a36Sopenharmony_ci	{}
151262306a36Sopenharmony_ci};
151362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, meson_nfc_id_table);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic int meson_nfc_probe(struct platform_device *pdev)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
151862306a36Sopenharmony_ci	struct meson_nfc *nfc;
151962306a36Sopenharmony_ci	int ret, irq;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
152262306a36Sopenharmony_ci	if (!nfc)
152362306a36Sopenharmony_ci		return -ENOMEM;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	nfc->data = of_device_get_match_data(&pdev->dev);
152662306a36Sopenharmony_ci	if (!nfc->data)
152762306a36Sopenharmony_ci		return -ENODEV;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	nand_controller_init(&nfc->controller);
153062306a36Sopenharmony_ci	INIT_LIST_HEAD(&nfc->chips);
153162306a36Sopenharmony_ci	init_completion(&nfc->completion);
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	nfc->dev = dev;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	nfc->reg_base = devm_platform_ioremap_resource_byname(pdev, "nfc");
153662306a36Sopenharmony_ci	if (IS_ERR(nfc->reg_base))
153762306a36Sopenharmony_ci		return PTR_ERR(nfc->reg_base);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	nfc->reg_clk = devm_platform_ioremap_resource_byname(pdev, "emmc");
154062306a36Sopenharmony_ci	if (IS_ERR(nfc->reg_clk))
154162306a36Sopenharmony_ci		return PTR_ERR(nfc->reg_clk);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
154462306a36Sopenharmony_ci	if (irq < 0)
154562306a36Sopenharmony_ci		return -EINVAL;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	ret = meson_nfc_clk_init(nfc);
154862306a36Sopenharmony_ci	if (ret) {
154962306a36Sopenharmony_ci		dev_err(dev, "failed to initialize NAND clock\n");
155062306a36Sopenharmony_ci		return ret;
155162306a36Sopenharmony_ci	}
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	writel(0, nfc->reg_base + NFC_REG_CFG);
155462306a36Sopenharmony_ci	ret = devm_request_irq(dev, irq, meson_nfc_irq, 0, dev_name(dev), nfc);
155562306a36Sopenharmony_ci	if (ret) {
155662306a36Sopenharmony_ci		dev_err(dev, "failed to request NFC IRQ\n");
155762306a36Sopenharmony_ci		ret = -EINVAL;
155862306a36Sopenharmony_ci		goto err_clk;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
156262306a36Sopenharmony_ci	if (ret) {
156362306a36Sopenharmony_ci		dev_err(dev, "failed to set DMA mask\n");
156462306a36Sopenharmony_ci		goto err_clk;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	platform_set_drvdata(pdev, nfc);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	ret = meson_nfc_nand_chips_init(dev, nfc);
157062306a36Sopenharmony_ci	if (ret) {
157162306a36Sopenharmony_ci		dev_err(dev, "failed to init NAND chips\n");
157262306a36Sopenharmony_ci		goto err_clk;
157362306a36Sopenharmony_ci	}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	return 0;
157662306a36Sopenharmony_cierr_clk:
157762306a36Sopenharmony_ci	meson_nfc_disable_clk(nfc);
157862306a36Sopenharmony_ci	return ret;
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic void meson_nfc_remove(struct platform_device *pdev)
158262306a36Sopenharmony_ci{
158362306a36Sopenharmony_ci	struct meson_nfc *nfc = platform_get_drvdata(pdev);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	meson_nfc_nand_chip_cleanup(nfc);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	meson_nfc_disable_clk(nfc);
158862306a36Sopenharmony_ci}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_cistatic struct platform_driver meson_nfc_driver = {
159162306a36Sopenharmony_ci	.probe  = meson_nfc_probe,
159262306a36Sopenharmony_ci	.remove_new = meson_nfc_remove,
159362306a36Sopenharmony_ci	.driver = {
159462306a36Sopenharmony_ci		.name  = "meson-nand",
159562306a36Sopenharmony_ci		.of_match_table = meson_nfc_id_table,
159662306a36Sopenharmony_ci	},
159762306a36Sopenharmony_ci};
159862306a36Sopenharmony_cimodule_platform_driver(meson_nfc_driver);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL");
160162306a36Sopenharmony_ciMODULE_AUTHOR("Liang Yang <liang.yang@amlogic.com>");
160262306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic's Meson NAND Flash Controller driver");
1603