162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Freescale GPMI NAND Flash Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010-2015 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci * Copyright (C) 2008 Embedded Alley Solutions, Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/sched/task_stack.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1862306a36Sopenharmony_ci#include <linux/dma/mxs-dma.h>
1962306a36Sopenharmony_ci#include "gpmi-nand.h"
2062306a36Sopenharmony_ci#include "gpmi-regs.h"
2162306a36Sopenharmony_ci#include "bch-regs.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Resource names for the GPMI NAND driver. */
2462306a36Sopenharmony_ci#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME  "gpmi-nand"
2562306a36Sopenharmony_ci#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME   "bch"
2662306a36Sopenharmony_ci#define GPMI_NAND_BCH_INTERRUPT_RES_NAME   "bch"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Converts time to clock cycles */
2962306a36Sopenharmony_ci#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MXS_SET_ADDR		0x4
3262306a36Sopenharmony_ci#define MXS_CLR_ADDR		0x8
3362306a36Sopenharmony_ci/*
3462306a36Sopenharmony_ci * Clear the bit and poll it cleared.  This is usually called with
3562306a36Sopenharmony_ci * a reset address and mask being either SFTRST(bit 31) or CLKGATE
3662306a36Sopenharmony_ci * (bit 30).
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_cistatic int clear_poll_bit(void __iomem *addr, u32 mask)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int timeout = 0x400;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* clear the bit */
4362306a36Sopenharmony_ci	writel(mask, addr + MXS_CLR_ADDR);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * SFTRST needs 3 GPMI clocks to settle, the reference manual
4762306a36Sopenharmony_ci	 * recommends to wait 1us.
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	udelay(1);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* poll the bit becoming clear */
5262306a36Sopenharmony_ci	while ((readl(addr) & mask) && --timeout)
5362306a36Sopenharmony_ci		/* nothing */;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return !timeout;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define MODULE_CLKGATE		(1 << 30)
5962306a36Sopenharmony_ci#define MODULE_SFTRST		(1 << 31)
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * The current mxs_reset_block() will do two things:
6262306a36Sopenharmony_ci *  [1] enable the module.
6362306a36Sopenharmony_ci *  [2] reset the module.
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * In most of the cases, it's ok.
6662306a36Sopenharmony_ci * But in MX23, there is a hardware bug in the BCH block (see erratum #2847).
6762306a36Sopenharmony_ci * If you try to soft reset the BCH block, it becomes unusable until
6862306a36Sopenharmony_ci * the next hard reset. This case occurs in the NAND boot mode. When the board
6962306a36Sopenharmony_ci * boots by NAND, the ROM of the chip will initialize the BCH blocks itself.
7062306a36Sopenharmony_ci * So If the driver tries to reset the BCH again, the BCH will not work anymore.
7162306a36Sopenharmony_ci * You will see a DMA timeout in this case. The bug has been fixed
7262306a36Sopenharmony_ci * in the following chips, such as MX28.
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * To avoid this bug, just add a new parameter `just_enable` for
7562306a36Sopenharmony_ci * the mxs_reset_block(), and rewrite it here.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cistatic int gpmi_reset_block(void __iomem *reset_addr, bool just_enable)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int ret;
8062306a36Sopenharmony_ci	int timeout = 0x400;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* clear and poll SFTRST */
8362306a36Sopenharmony_ci	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
8462306a36Sopenharmony_ci	if (unlikely(ret))
8562306a36Sopenharmony_ci		goto error;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* clear CLKGATE */
8862306a36Sopenharmony_ci	writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!just_enable) {
9162306a36Sopenharmony_ci		/* set SFTRST to reset the block */
9262306a36Sopenharmony_ci		writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR);
9362306a36Sopenharmony_ci		udelay(1);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		/* poll CLKGATE becoming set */
9662306a36Sopenharmony_ci		while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout)
9762306a36Sopenharmony_ci			/* nothing */;
9862306a36Sopenharmony_ci		if (unlikely(!timeout))
9962306a36Sopenharmony_ci			goto error;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* clear and poll SFTRST */
10362306a36Sopenharmony_ci	ret = clear_poll_bit(reset_addr, MODULE_SFTRST);
10462306a36Sopenharmony_ci	if (unlikely(ret))
10562306a36Sopenharmony_ci		goto error;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* clear and poll CLKGATE */
10862306a36Sopenharmony_ci	ret = clear_poll_bit(reset_addr, MODULE_CLKGATE);
10962306a36Sopenharmony_ci	if (unlikely(ret))
11062306a36Sopenharmony_ci		goto error;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cierror:
11562306a36Sopenharmony_ci	pr_err("%s(%p): module reset timeout\n", __func__, reset_addr);
11662306a36Sopenharmony_ci	return -ETIMEDOUT;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	struct clk *clk;
12262306a36Sopenharmony_ci	int ret;
12362306a36Sopenharmony_ci	int i;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	for (i = 0; i < GPMI_CLK_MAX; i++) {
12662306a36Sopenharmony_ci		clk = this->resources.clock[i];
12762306a36Sopenharmony_ci		if (!clk)
12862306a36Sopenharmony_ci			break;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (v) {
13162306a36Sopenharmony_ci			ret = clk_prepare_enable(clk);
13262306a36Sopenharmony_ci			if (ret)
13362306a36Sopenharmony_ci				goto err_clk;
13462306a36Sopenharmony_ci		} else {
13562306a36Sopenharmony_ci			clk_disable_unprepare(clk);
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cierr_clk:
14162306a36Sopenharmony_ci	for (; i > 0; i--)
14262306a36Sopenharmony_ci		clk_disable_unprepare(this->resources.clock[i - 1]);
14362306a36Sopenharmony_ci	return ret;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int gpmi_init(struct gpmi_nand_data *this)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct resources *r = &this->resources;
14962306a36Sopenharmony_ci	int ret;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(this->dev);
15262306a36Sopenharmony_ci	if (ret < 0)
15362306a36Sopenharmony_ci		return ret;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = gpmi_reset_block(r->gpmi_regs, false);
15662306a36Sopenharmony_ci	if (ret)
15762306a36Sopenharmony_ci		goto err_out;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/*
16062306a36Sopenharmony_ci	 * Reset BCH here, too. We got failures otherwise :(
16162306a36Sopenharmony_ci	 * See later BCH reset for explanation of MX23 and MX28 handling
16262306a36Sopenharmony_ci	 */
16362306a36Sopenharmony_ci	ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this));
16462306a36Sopenharmony_ci	if (ret)
16562306a36Sopenharmony_ci		goto err_out;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* Choose NAND mode. */
16862306a36Sopenharmony_ci	writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* Set the IRQ polarity. */
17162306a36Sopenharmony_ci	writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
17262306a36Sopenharmony_ci				r->gpmi_regs + HW_GPMI_CTRL1_SET);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Disable Write-Protection. */
17562306a36Sopenharmony_ci	writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	/* Select BCH ECC. */
17862306a36Sopenharmony_ci	writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/*
18162306a36Sopenharmony_ci	 * Decouple the chip select from dma channel. We use dma0 for all
18262306a36Sopenharmony_ci	 * the chips, force all NAND RDY_BUSY inputs to be sourced from
18362306a36Sopenharmony_ci	 * RDY_BUSY0.
18462306a36Sopenharmony_ci	 */
18562306a36Sopenharmony_ci	writel(BM_GPMI_CTRL1_DECOUPLE_CS | BM_GPMI_CTRL1_GANGED_RDYBUSY,
18662306a36Sopenharmony_ci	       r->gpmi_regs + HW_GPMI_CTRL1_SET);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cierr_out:
18962306a36Sopenharmony_ci	pm_runtime_mark_last_busy(this->dev);
19062306a36Sopenharmony_ci	pm_runtime_put_autosuspend(this->dev);
19162306a36Sopenharmony_ci	return ret;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* This function is very useful. It is called only when the bug occur. */
19562306a36Sopenharmony_cistatic void gpmi_dump_info(struct gpmi_nand_data *this)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct resources *r = &this->resources;
19862306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
19962306a36Sopenharmony_ci	u32 reg;
20062306a36Sopenharmony_ci	int i;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dev_err(this->dev, "Show GPMI registers :\n");
20362306a36Sopenharmony_ci	for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) {
20462306a36Sopenharmony_ci		reg = readl(r->gpmi_regs + i * 0x10);
20562306a36Sopenharmony_ci		dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* start to print out the BCH info */
20962306a36Sopenharmony_ci	dev_err(this->dev, "Show BCH registers :\n");
21062306a36Sopenharmony_ci	for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) {
21162306a36Sopenharmony_ci		reg = readl(r->bch_regs + i * 0x10);
21262306a36Sopenharmony_ci		dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	dev_err(this->dev, "BCH Geometry :\n"
21562306a36Sopenharmony_ci		"GF length              : %u\n"
21662306a36Sopenharmony_ci		"ECC Strength           : %u\n"
21762306a36Sopenharmony_ci		"Page Size in Bytes     : %u\n"
21862306a36Sopenharmony_ci		"Metadata Size in Bytes : %u\n"
21962306a36Sopenharmony_ci		"ECC0 Chunk Size in Bytes: %u\n"
22062306a36Sopenharmony_ci		"ECCn Chunk Size in Bytes: %u\n"
22162306a36Sopenharmony_ci		"ECC Chunk Count        : %u\n"
22262306a36Sopenharmony_ci		"Payload Size in Bytes  : %u\n"
22362306a36Sopenharmony_ci		"Auxiliary Size in Bytes: %u\n"
22462306a36Sopenharmony_ci		"Auxiliary Status Offset: %u\n"
22562306a36Sopenharmony_ci		"Block Mark Byte Offset : %u\n"
22662306a36Sopenharmony_ci		"Block Mark Bit Offset  : %u\n",
22762306a36Sopenharmony_ci		geo->gf_len,
22862306a36Sopenharmony_ci		geo->ecc_strength,
22962306a36Sopenharmony_ci		geo->page_size,
23062306a36Sopenharmony_ci		geo->metadata_size,
23162306a36Sopenharmony_ci		geo->ecc0_chunk_size,
23262306a36Sopenharmony_ci		geo->eccn_chunk_size,
23362306a36Sopenharmony_ci		geo->ecc_chunk_count,
23462306a36Sopenharmony_ci		geo->payload_size,
23562306a36Sopenharmony_ci		geo->auxiliary_size,
23662306a36Sopenharmony_ci		geo->auxiliary_status_offset,
23762306a36Sopenharmony_ci		geo->block_mark_byte_offset,
23862306a36Sopenharmony_ci		geo->block_mark_bit_offset);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic bool gpmi_check_ecc(struct gpmi_nand_data *this)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
24462306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
24562306a36Sopenharmony_ci	struct nand_device *nand = &chip->base;
24662306a36Sopenharmony_ci	struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	conf->step_size = geo->eccn_chunk_size;
24962306a36Sopenharmony_ci	conf->strength = geo->ecc_strength;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Do the sanity check. */
25262306a36Sopenharmony_ci	if (GPMI_IS_MXS(this)) {
25362306a36Sopenharmony_ci		/* The mx23/mx28 only support the GF13. */
25462306a36Sopenharmony_ci		if (geo->gf_len == 14)
25562306a36Sopenharmony_ci			return false;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (geo->ecc_strength > this->devdata->bch_max_ecc_strength)
25962306a36Sopenharmony_ci		return false;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (!nand_ecc_is_strong_enough(nand))
26262306a36Sopenharmony_ci		return false;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return true;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* check if bbm locates in data chunk rather than ecc chunk */
26862306a36Sopenharmony_cistatic bool bbm_in_data_chunk(struct gpmi_nand_data *this,
26962306a36Sopenharmony_ci			unsigned int *chunk_num)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
27262306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
27362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
27462306a36Sopenharmony_ci	unsigned int i, j;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (geo->ecc0_chunk_size != geo->eccn_chunk_size) {
27762306a36Sopenharmony_ci		dev_err(this->dev,
27862306a36Sopenharmony_ci			"The size of ecc0_chunk must equal to eccn_chunk\n");
27962306a36Sopenharmony_ci		return false;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	i = (mtd->writesize * 8 - geo->metadata_size * 8) /
28362306a36Sopenharmony_ci		(geo->gf_len * geo->ecc_strength +
28462306a36Sopenharmony_ci			geo->eccn_chunk_size * 8);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	j = (mtd->writesize * 8 - geo->metadata_size * 8) -
28762306a36Sopenharmony_ci		(geo->gf_len * geo->ecc_strength +
28862306a36Sopenharmony_ci			geo->eccn_chunk_size * 8) * i;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (j < geo->eccn_chunk_size * 8) {
29162306a36Sopenharmony_ci		*chunk_num = i+1;
29262306a36Sopenharmony_ci		dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n",
29362306a36Sopenharmony_ci				geo->ecc_strength, *chunk_num);
29462306a36Sopenharmony_ci		return true;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return false;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*
30162306a36Sopenharmony_ci * If we can get the ECC information from the nand chip, we do not
30262306a36Sopenharmony_ci * need to calculate them ourselves.
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * We may have available oob space in this case.
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_cistatic int set_geometry_by_ecc_info(struct gpmi_nand_data *this,
30762306a36Sopenharmony_ci				    unsigned int ecc_strength,
30862306a36Sopenharmony_ci				    unsigned int ecc_step)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
31162306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
31262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
31362306a36Sopenharmony_ci	unsigned int block_mark_bit_offset;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	switch (ecc_step) {
31662306a36Sopenharmony_ci	case SZ_512:
31762306a36Sopenharmony_ci		geo->gf_len = 13;
31862306a36Sopenharmony_ci		break;
31962306a36Sopenharmony_ci	case SZ_1K:
32062306a36Sopenharmony_ci		geo->gf_len = 14;
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	default:
32362306a36Sopenharmony_ci		dev_err(this->dev,
32462306a36Sopenharmony_ci			"unsupported nand chip. ecc bits : %d, ecc size : %d\n",
32562306a36Sopenharmony_ci			nanddev_get_ecc_requirements(&chip->base)->strength,
32662306a36Sopenharmony_ci			nanddev_get_ecc_requirements(&chip->base)->step_size);
32762306a36Sopenharmony_ci		return -EINVAL;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	geo->ecc0_chunk_size = ecc_step;
33062306a36Sopenharmony_ci	geo->eccn_chunk_size = ecc_step;
33162306a36Sopenharmony_ci	geo->ecc_strength = round_up(ecc_strength, 2);
33262306a36Sopenharmony_ci	if (!gpmi_check_ecc(this))
33362306a36Sopenharmony_ci		return -EINVAL;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Keep the C >= O */
33662306a36Sopenharmony_ci	if (geo->eccn_chunk_size < mtd->oobsize) {
33762306a36Sopenharmony_ci		dev_err(this->dev,
33862306a36Sopenharmony_ci			"unsupported nand chip. ecc size: %d, oob size : %d\n",
33962306a36Sopenharmony_ci			ecc_step, mtd->oobsize);
34062306a36Sopenharmony_ci		return -EINVAL;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* The default value, see comment in the legacy_set_geometry(). */
34462306a36Sopenharmony_ci	geo->metadata_size = 10;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/*
34962306a36Sopenharmony_ci	 * Now, the NAND chip with 2K page(data chunk is 512byte) shows below:
35062306a36Sopenharmony_ci	 *
35162306a36Sopenharmony_ci	 *    |                          P                            |
35262306a36Sopenharmony_ci	 *    |<----------------------------------------------------->|
35362306a36Sopenharmony_ci	 *    |                                                       |
35462306a36Sopenharmony_ci	 *    |                                        (Block Mark)   |
35562306a36Sopenharmony_ci	 *    |                      P'                      |      | |     |
35662306a36Sopenharmony_ci	 *    |<-------------------------------------------->|  D   | |  O' |
35762306a36Sopenharmony_ci	 *    |                                              |<---->| |<--->|
35862306a36Sopenharmony_ci	 *    V                                              V      V V     V
35962306a36Sopenharmony_ci	 *    +---+----------+-+----------+-+----------+-+----------+-+-----+
36062306a36Sopenharmony_ci	 *    | M |   data   |E|   data   |E|   data   |E|   data   |E|     |
36162306a36Sopenharmony_ci	 *    +---+----------+-+----------+-+----------+-+----------+-+-----+
36262306a36Sopenharmony_ci	 *                                                   ^              ^
36362306a36Sopenharmony_ci	 *                                                   |      O       |
36462306a36Sopenharmony_ci	 *                                                   |<------------>|
36562306a36Sopenharmony_ci	 *                                                   |              |
36662306a36Sopenharmony_ci	 *
36762306a36Sopenharmony_ci	 *	P : the page size for BCH module.
36862306a36Sopenharmony_ci	 *	E : The ECC strength.
36962306a36Sopenharmony_ci	 *	G : the length of Galois Field.
37062306a36Sopenharmony_ci	 *	N : The chunk count of per page.
37162306a36Sopenharmony_ci	 *	M : the metasize of per page.
37262306a36Sopenharmony_ci	 *	C : the ecc chunk size, aka the "data" above.
37362306a36Sopenharmony_ci	 *	P': the nand chip's page size.
37462306a36Sopenharmony_ci	 *	O : the nand chip's oob size.
37562306a36Sopenharmony_ci	 *	O': the free oob.
37662306a36Sopenharmony_ci	 *
37762306a36Sopenharmony_ci	 *	The formula for P is :
37862306a36Sopenharmony_ci	 *
37962306a36Sopenharmony_ci	 *	            E * G * N
38062306a36Sopenharmony_ci	 *	       P = ------------ + P' + M
38162306a36Sopenharmony_ci	 *                      8
38262306a36Sopenharmony_ci	 *
38362306a36Sopenharmony_ci	 * The position of block mark moves forward in the ECC-based view
38462306a36Sopenharmony_ci	 * of page, and the delta is:
38562306a36Sopenharmony_ci	 *
38662306a36Sopenharmony_ci	 *                   E * G * (N - 1)
38762306a36Sopenharmony_ci	 *             D = (---------------- + M)
38862306a36Sopenharmony_ci	 *                          8
38962306a36Sopenharmony_ci	 *
39062306a36Sopenharmony_ci	 * Please see the comment in legacy_set_geometry().
39162306a36Sopenharmony_ci	 * With the condition C >= O , we still can get same result.
39262306a36Sopenharmony_ci	 * So the bit position of the physical block mark within the ECC-based
39362306a36Sopenharmony_ci	 * view of the page is :
39462306a36Sopenharmony_ci	 *             (P' - D) * 8
39562306a36Sopenharmony_ci	 */
39662306a36Sopenharmony_ci	geo->page_size = mtd->writesize + geo->metadata_size +
39762306a36Sopenharmony_ci		(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	geo->payload_size = mtd->writesize;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
40262306a36Sopenharmony_ci	geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
40362306a36Sopenharmony_ci				+ ALIGN(geo->ecc_chunk_count, 4);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!this->swap_block_mark)
40662306a36Sopenharmony_ci		return 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* For bit swap. */
40962306a36Sopenharmony_ci	block_mark_bit_offset = mtd->writesize * 8 -
41062306a36Sopenharmony_ci		(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
41162306a36Sopenharmony_ci				+ geo->metadata_size * 8);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	geo->block_mark_byte_offset = block_mark_bit_offset / 8;
41462306a36Sopenharmony_ci	geo->block_mark_bit_offset  = block_mark_bit_offset % 8;
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/*
41962306a36Sopenharmony_ci *  Calculate the ECC strength by hand:
42062306a36Sopenharmony_ci *	E : The ECC strength.
42162306a36Sopenharmony_ci *	G : the length of Galois Field.
42262306a36Sopenharmony_ci *	N : The chunk count of per page.
42362306a36Sopenharmony_ci *	O : the oobsize of the NAND chip.
42462306a36Sopenharmony_ci *	M : the metasize of per page.
42562306a36Sopenharmony_ci *
42662306a36Sopenharmony_ci *	The formula is :
42762306a36Sopenharmony_ci *		E * G * N
42862306a36Sopenharmony_ci *	      ------------ <= (O - M)
42962306a36Sopenharmony_ci *                  8
43062306a36Sopenharmony_ci *
43162306a36Sopenharmony_ci *      So, we get E by:
43262306a36Sopenharmony_ci *                    (O - M) * 8
43362306a36Sopenharmony_ci *              E <= -------------
43462306a36Sopenharmony_ci *                       G * N
43562306a36Sopenharmony_ci */
43662306a36Sopenharmony_cistatic inline int get_ecc_strength(struct gpmi_nand_data *this)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
43962306a36Sopenharmony_ci	struct mtd_info	*mtd = nand_to_mtd(&this->nand);
44062306a36Sopenharmony_ci	int ecc_strength;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8)
44362306a36Sopenharmony_ci			/ (geo->gf_len * geo->ecc_chunk_count);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* We need the minor even number. */
44662306a36Sopenharmony_ci	return round_down(ecc_strength, 2);
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic int set_geometry_for_large_oob(struct gpmi_nand_data *this)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
45262306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
45362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
45462306a36Sopenharmony_ci	const struct nand_ecc_props *requirements =
45562306a36Sopenharmony_ci		nanddev_get_ecc_requirements(&chip->base);
45662306a36Sopenharmony_ci	unsigned int block_mark_bit_offset;
45762306a36Sopenharmony_ci	unsigned int max_ecc;
45862306a36Sopenharmony_ci	unsigned int bbm_chunk;
45962306a36Sopenharmony_ci	unsigned int i;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* sanity check for the minimum ecc nand required */
46262306a36Sopenharmony_ci	if (!(requirements->strength > 0 &&
46362306a36Sopenharmony_ci	      requirements->step_size > 0))
46462306a36Sopenharmony_ci		return -EINVAL;
46562306a36Sopenharmony_ci	geo->ecc_strength = requirements->strength;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* check if platform can support this nand */
46862306a36Sopenharmony_ci	if (!gpmi_check_ecc(this)) {
46962306a36Sopenharmony_ci		dev_err(this->dev,
47062306a36Sopenharmony_ci			"unsupported NAND chip, minimum ecc required %d\n",
47162306a36Sopenharmony_ci			geo->ecc_strength);
47262306a36Sopenharmony_ci		return -EINVAL;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* calculate the maximum ecc platform can support*/
47662306a36Sopenharmony_ci	geo->metadata_size = 10;
47762306a36Sopenharmony_ci	geo->gf_len = 14;
47862306a36Sopenharmony_ci	geo->ecc0_chunk_size = 1024;
47962306a36Sopenharmony_ci	geo->eccn_chunk_size = 1024;
48062306a36Sopenharmony_ci	geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
48162306a36Sopenharmony_ci	max_ecc = min(get_ecc_strength(this),
48262306a36Sopenharmony_ci		      this->devdata->bch_max_ecc_strength);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/*
48562306a36Sopenharmony_ci	 * search a supported ecc strength that makes bbm
48662306a36Sopenharmony_ci	 * located in data chunk
48762306a36Sopenharmony_ci	 */
48862306a36Sopenharmony_ci	geo->ecc_strength = max_ecc;
48962306a36Sopenharmony_ci	while (!(geo->ecc_strength < requirements->strength)) {
49062306a36Sopenharmony_ci		if (bbm_in_data_chunk(this, &bbm_chunk))
49162306a36Sopenharmony_ci			goto geo_setting;
49262306a36Sopenharmony_ci		geo->ecc_strength -= 2;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* if none of them works, keep using the minimum ecc */
49662306a36Sopenharmony_ci	/* nand required but changing ecc page layout  */
49762306a36Sopenharmony_ci	geo->ecc_strength = requirements->strength;
49862306a36Sopenharmony_ci	/* add extra ecc for meta data */
49962306a36Sopenharmony_ci	geo->ecc0_chunk_size = 0;
50062306a36Sopenharmony_ci	geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1;
50162306a36Sopenharmony_ci	geo->ecc_for_meta = 1;
50262306a36Sopenharmony_ci	/* check if oob can afford this extra ecc chunk */
50362306a36Sopenharmony_ci	if (mtd->oobsize * 8 < geo->metadata_size * 8 +
50462306a36Sopenharmony_ci	    geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) {
50562306a36Sopenharmony_ci		dev_err(this->dev, "unsupported NAND chip with new layout\n");
50662306a36Sopenharmony_ci		return -EINVAL;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* calculate in which chunk bbm located */
51062306a36Sopenharmony_ci	bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 -
51162306a36Sopenharmony_ci		     geo->gf_len * geo->ecc_strength) /
51262306a36Sopenharmony_ci		     (geo->gf_len * geo->ecc_strength +
51362306a36Sopenharmony_ci		     geo->eccn_chunk_size * 8) + 1;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cigeo_setting:
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	geo->page_size = mtd->writesize + geo->metadata_size +
51862306a36Sopenharmony_ci		(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
51962306a36Sopenharmony_ci	geo->payload_size = mtd->writesize;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/*
52262306a36Sopenharmony_ci	 * The auxiliary buffer contains the metadata and the ECC status. The
52362306a36Sopenharmony_ci	 * metadata is padded to the nearest 32-bit boundary. The ECC status
52462306a36Sopenharmony_ci	 * contains one byte for every ECC chunk, and is also padded to the
52562306a36Sopenharmony_ci	 * nearest 32-bit boundary.
52662306a36Sopenharmony_ci	 */
52762306a36Sopenharmony_ci	geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4);
52862306a36Sopenharmony_ci	geo->auxiliary_size = ALIGN(geo->metadata_size, 4)
52962306a36Sopenharmony_ci				    + ALIGN(geo->ecc_chunk_count, 4);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (!this->swap_block_mark)
53262306a36Sopenharmony_ci		return 0;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* calculate the number of ecc chunk behind the bbm */
53562306a36Sopenharmony_ci	i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	block_mark_bit_offset = mtd->writesize * 8 -
53862306a36Sopenharmony_ci		(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i)
53962306a36Sopenharmony_ci		+ geo->metadata_size * 8);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	geo->block_mark_byte_offset = block_mark_bit_offset / 8;
54262306a36Sopenharmony_ci	geo->block_mark_bit_offset  = block_mark_bit_offset % 8;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	dev_dbg(this->dev, "BCH Geometry :\n"
54562306a36Sopenharmony_ci		"GF length              : %u\n"
54662306a36Sopenharmony_ci		"ECC Strength           : %u\n"
54762306a36Sopenharmony_ci		"Page Size in Bytes     : %u\n"
54862306a36Sopenharmony_ci		"Metadata Size in Bytes : %u\n"
54962306a36Sopenharmony_ci		"ECC0 Chunk Size in Bytes: %u\n"
55062306a36Sopenharmony_ci		"ECCn Chunk Size in Bytes: %u\n"
55162306a36Sopenharmony_ci		"ECC Chunk Count        : %u\n"
55262306a36Sopenharmony_ci		"Payload Size in Bytes  : %u\n"
55362306a36Sopenharmony_ci		"Auxiliary Size in Bytes: %u\n"
55462306a36Sopenharmony_ci		"Auxiliary Status Offset: %u\n"
55562306a36Sopenharmony_ci		"Block Mark Byte Offset : %u\n"
55662306a36Sopenharmony_ci		"Block Mark Bit Offset  : %u\n"
55762306a36Sopenharmony_ci		"Block Mark in chunk	: %u\n"
55862306a36Sopenharmony_ci		"Ecc for Meta data	: %u\n",
55962306a36Sopenharmony_ci		geo->gf_len,
56062306a36Sopenharmony_ci		geo->ecc_strength,
56162306a36Sopenharmony_ci		geo->page_size,
56262306a36Sopenharmony_ci		geo->metadata_size,
56362306a36Sopenharmony_ci		geo->ecc0_chunk_size,
56462306a36Sopenharmony_ci		geo->eccn_chunk_size,
56562306a36Sopenharmony_ci		geo->ecc_chunk_count,
56662306a36Sopenharmony_ci		geo->payload_size,
56762306a36Sopenharmony_ci		geo->auxiliary_size,
56862306a36Sopenharmony_ci		geo->auxiliary_status_offset,
56962306a36Sopenharmony_ci		geo->block_mark_byte_offset,
57062306a36Sopenharmony_ci		geo->block_mark_bit_offset,
57162306a36Sopenharmony_ci		bbm_chunk,
57262306a36Sopenharmony_ci		geo->ecc_for_meta);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return 0;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int legacy_set_geometry(struct gpmi_nand_data *this)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
58062306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&this->nand);
58162306a36Sopenharmony_ci	unsigned int metadata_size;
58262306a36Sopenharmony_ci	unsigned int status_size;
58362306a36Sopenharmony_ci	unsigned int block_mark_bit_offset;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * The size of the metadata can be changed, though we set it to 10
58762306a36Sopenharmony_ci	 * bytes now. But it can't be too large, because we have to save
58862306a36Sopenharmony_ci	 * enough space for BCH.
58962306a36Sopenharmony_ci	 */
59062306a36Sopenharmony_ci	geo->metadata_size = 10;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* The default for the length of Galois Field. */
59362306a36Sopenharmony_ci	geo->gf_len = 13;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* The default for chunk size. */
59662306a36Sopenharmony_ci	geo->ecc0_chunk_size = 512;
59762306a36Sopenharmony_ci	geo->eccn_chunk_size = 512;
59862306a36Sopenharmony_ci	while (geo->eccn_chunk_size < mtd->oobsize) {
59962306a36Sopenharmony_ci		geo->ecc0_chunk_size *= 2; /* keep C >= O */
60062306a36Sopenharmony_ci		geo->eccn_chunk_size *= 2; /* keep C >= O */
60162306a36Sopenharmony_ci		geo->gf_len = 14;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* We use the same ECC strength for all chunks. */
60762306a36Sopenharmony_ci	geo->ecc_strength = get_ecc_strength(this);
60862306a36Sopenharmony_ci	if (!gpmi_check_ecc(this)) {
60962306a36Sopenharmony_ci		dev_err(this->dev,
61062306a36Sopenharmony_ci			"ecc strength: %d cannot be supported by the controller (%d)\n"
61162306a36Sopenharmony_ci			"try to use minimum ecc strength that NAND chip required\n",
61262306a36Sopenharmony_ci			geo->ecc_strength,
61362306a36Sopenharmony_ci			this->devdata->bch_max_ecc_strength);
61462306a36Sopenharmony_ci		return -EINVAL;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	geo->page_size = mtd->writesize + geo->metadata_size +
61862306a36Sopenharmony_ci		(geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8;
61962306a36Sopenharmony_ci	geo->payload_size = mtd->writesize;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/*
62262306a36Sopenharmony_ci	 * The auxiliary buffer contains the metadata and the ECC status. The
62362306a36Sopenharmony_ci	 * metadata is padded to the nearest 32-bit boundary. The ECC status
62462306a36Sopenharmony_ci	 * contains one byte for every ECC chunk, and is also padded to the
62562306a36Sopenharmony_ci	 * nearest 32-bit boundary.
62662306a36Sopenharmony_ci	 */
62762306a36Sopenharmony_ci	metadata_size = ALIGN(geo->metadata_size, 4);
62862306a36Sopenharmony_ci	status_size   = ALIGN(geo->ecc_chunk_count, 4);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	geo->auxiliary_size = metadata_size + status_size;
63162306a36Sopenharmony_ci	geo->auxiliary_status_offset = metadata_size;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!this->swap_block_mark)
63462306a36Sopenharmony_ci		return 0;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	/*
63762306a36Sopenharmony_ci	 * We need to compute the byte and bit offsets of
63862306a36Sopenharmony_ci	 * the physical block mark within the ECC-based view of the page.
63962306a36Sopenharmony_ci	 *
64062306a36Sopenharmony_ci	 * NAND chip with 2K page shows below:
64162306a36Sopenharmony_ci	 *                                             (Block Mark)
64262306a36Sopenharmony_ci	 *                                                   |      |
64362306a36Sopenharmony_ci	 *                                                   |  D   |
64462306a36Sopenharmony_ci	 *                                                   |<---->|
64562306a36Sopenharmony_ci	 *                                                   V      V
64662306a36Sopenharmony_ci	 *    +---+----------+-+----------+-+----------+-+----------+-+
64762306a36Sopenharmony_ci	 *    | M |   data   |E|   data   |E|   data   |E|   data   |E|
64862306a36Sopenharmony_ci	 *    +---+----------+-+----------+-+----------+-+----------+-+
64962306a36Sopenharmony_ci	 *
65062306a36Sopenharmony_ci	 * The position of block mark moves forward in the ECC-based view
65162306a36Sopenharmony_ci	 * of page, and the delta is:
65262306a36Sopenharmony_ci	 *
65362306a36Sopenharmony_ci	 *                   E * G * (N - 1)
65462306a36Sopenharmony_ci	 *             D = (---------------- + M)
65562306a36Sopenharmony_ci	 *                          8
65662306a36Sopenharmony_ci	 *
65762306a36Sopenharmony_ci	 * With the formula to compute the ECC strength, and the condition
65862306a36Sopenharmony_ci	 *       : C >= O         (C is the ecc chunk size)
65962306a36Sopenharmony_ci	 *
66062306a36Sopenharmony_ci	 * It's easy to deduce to the following result:
66162306a36Sopenharmony_ci	 *
66262306a36Sopenharmony_ci	 *         E * G       (O - M)      C - M         C - M
66362306a36Sopenharmony_ci	 *      ----------- <= ------- <=  --------  <  ---------
66462306a36Sopenharmony_ci	 *           8            N           N          (N - 1)
66562306a36Sopenharmony_ci	 *
66662306a36Sopenharmony_ci	 *  So, we get:
66762306a36Sopenharmony_ci	 *
66862306a36Sopenharmony_ci	 *                   E * G * (N - 1)
66962306a36Sopenharmony_ci	 *             D = (---------------- + M) < C
67062306a36Sopenharmony_ci	 *                          8
67162306a36Sopenharmony_ci	 *
67262306a36Sopenharmony_ci	 *  The above inequality means the position of block mark
67362306a36Sopenharmony_ci	 *  within the ECC-based view of the page is still in the data chunk,
67462306a36Sopenharmony_ci	 *  and it's NOT in the ECC bits of the chunk.
67562306a36Sopenharmony_ci	 *
67662306a36Sopenharmony_ci	 *  Use the following to compute the bit position of the
67762306a36Sopenharmony_ci	 *  physical block mark within the ECC-based view of the page:
67862306a36Sopenharmony_ci	 *          (page_size - D) * 8
67962306a36Sopenharmony_ci	 *
68062306a36Sopenharmony_ci	 *  --Huang Shijie
68162306a36Sopenharmony_ci	 */
68262306a36Sopenharmony_ci	block_mark_bit_offset = mtd->writesize * 8 -
68362306a36Sopenharmony_ci		(geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1)
68462306a36Sopenharmony_ci				+ geo->metadata_size * 8);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	geo->block_mark_byte_offset = block_mark_bit_offset / 8;
68762306a36Sopenharmony_ci	geo->block_mark_bit_offset  = block_mark_bit_offset % 8;
68862306a36Sopenharmony_ci	return 0;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic int common_nfc_set_geometry(struct gpmi_nand_data *this)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
69462306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&this->nand);
69562306a36Sopenharmony_ci	const struct nand_ecc_props *requirements =
69662306a36Sopenharmony_ci		nanddev_get_ecc_requirements(&chip->base);
69762306a36Sopenharmony_ci	bool use_minimun_ecc;
69862306a36Sopenharmony_ci	int err;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	use_minimun_ecc = of_property_read_bool(this->dev->of_node,
70162306a36Sopenharmony_ci						"fsl,use-minimum-ecc");
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* use legacy bch geometry settings by default*/
70462306a36Sopenharmony_ci	if ((!use_minimun_ecc && mtd->oobsize < 1024) ||
70562306a36Sopenharmony_ci	    !(requirements->strength > 0 && requirements->step_size > 0)) {
70662306a36Sopenharmony_ci		dev_dbg(this->dev, "use legacy bch geometry\n");
70762306a36Sopenharmony_ci		err = legacy_set_geometry(this);
70862306a36Sopenharmony_ci		if (!err)
70962306a36Sopenharmony_ci			return 0;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* for large oob nand */
71362306a36Sopenharmony_ci	if (mtd->oobsize > 1024) {
71462306a36Sopenharmony_ci		dev_dbg(this->dev, "use large oob bch geometry\n");
71562306a36Sopenharmony_ci		err = set_geometry_for_large_oob(this);
71662306a36Sopenharmony_ci		if (!err)
71762306a36Sopenharmony_ci			return 0;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* otherwise use the minimum ecc nand chip required */
72162306a36Sopenharmony_ci	dev_dbg(this->dev, "use minimum ecc bch geometry\n");
72262306a36Sopenharmony_ci	err = set_geometry_by_ecc_info(this, requirements->strength,
72362306a36Sopenharmony_ci					requirements->step_size);
72462306a36Sopenharmony_ci	if (err)
72562306a36Sopenharmony_ci		dev_err(this->dev, "none of the bch geometry setting works\n");
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return err;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/* Configures the geometry for BCH.  */
73162306a36Sopenharmony_cistatic int bch_set_geometry(struct gpmi_nand_data *this)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct resources *r = &this->resources;
73462306a36Sopenharmony_ci	int ret;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	ret = common_nfc_set_geometry(this);
73762306a36Sopenharmony_ci	if (ret)
73862306a36Sopenharmony_ci		return ret;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	ret = pm_runtime_get_sync(this->dev);
74162306a36Sopenharmony_ci	if (ret < 0) {
74262306a36Sopenharmony_ci		pm_runtime_put_autosuspend(this->dev);
74362306a36Sopenharmony_ci		return ret;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	/*
74762306a36Sopenharmony_ci	* Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this
74862306a36Sopenharmony_ci	* chip, otherwise it will lock up. So we skip resetting BCH on the MX23.
74962306a36Sopenharmony_ci	* and MX28.
75062306a36Sopenharmony_ci	*/
75162306a36Sopenharmony_ci	ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this));
75262306a36Sopenharmony_ci	if (ret)
75362306a36Sopenharmony_ci		goto err_out;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* Set *all* chip selects to use layout 0. */
75662306a36Sopenharmony_ci	writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	ret = 0;
75962306a36Sopenharmony_cierr_out:
76062306a36Sopenharmony_ci	pm_runtime_mark_last_busy(this->dev);
76162306a36Sopenharmony_ci	pm_runtime_put_autosuspend(this->dev);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	return ret;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci/*
76762306a36Sopenharmony_ci * <1> Firstly, we should know what's the GPMI-clock means.
76862306a36Sopenharmony_ci *     The GPMI-clock is the internal clock in the gpmi nand controller.
76962306a36Sopenharmony_ci *     If you set 100MHz to gpmi nand controller, the GPMI-clock's period
77062306a36Sopenharmony_ci *     is 10ns. Mark the GPMI-clock's period as GPMI-clock-period.
77162306a36Sopenharmony_ci *
77262306a36Sopenharmony_ci * <2> Secondly, we should know what's the frequency on the nand chip pins.
77362306a36Sopenharmony_ci *     The frequency on the nand chip pins is derived from the GPMI-clock.
77462306a36Sopenharmony_ci *     We can get it from the following equation:
77562306a36Sopenharmony_ci *
77662306a36Sopenharmony_ci *         F = G / (DS + DH)
77762306a36Sopenharmony_ci *
77862306a36Sopenharmony_ci *         F  : the frequency on the nand chip pins.
77962306a36Sopenharmony_ci *         G  : the GPMI clock, such as 100MHz.
78062306a36Sopenharmony_ci *         DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP
78162306a36Sopenharmony_ci *         DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD
78262306a36Sopenharmony_ci *
78362306a36Sopenharmony_ci * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz,
78462306a36Sopenharmony_ci *     the nand EDO(extended Data Out) timing could be applied.
78562306a36Sopenharmony_ci *     The GPMI implements a feedback read strobe to sample the read data.
78662306a36Sopenharmony_ci *     The feedback read strobe can be delayed to support the nand EDO timing
78762306a36Sopenharmony_ci *     where the read strobe may deasserts before the read data is valid, and
78862306a36Sopenharmony_ci *     read data is valid for some time after read strobe.
78962306a36Sopenharmony_ci *
79062306a36Sopenharmony_ci *     The following figure illustrates some aspects of a NAND Flash read:
79162306a36Sopenharmony_ci *
79262306a36Sopenharmony_ci *                   |<---tREA---->|
79362306a36Sopenharmony_ci *                   |             |
79462306a36Sopenharmony_ci *                   |         |   |
79562306a36Sopenharmony_ci *                   |<--tRP-->|   |
79662306a36Sopenharmony_ci *                   |         |   |
79762306a36Sopenharmony_ci *                  __          ___|__________________________________
79862306a36Sopenharmony_ci *     RDN            \________/   |
79962306a36Sopenharmony_ci *                                 |
80062306a36Sopenharmony_ci *                                 /---------\
80162306a36Sopenharmony_ci *     Read Data    --------------<           >---------
80262306a36Sopenharmony_ci *                                 \---------/
80362306a36Sopenharmony_ci *                                |     |
80462306a36Sopenharmony_ci *                                |<-D->|
80562306a36Sopenharmony_ci *     FeedbackRDN  ________             ____________
80662306a36Sopenharmony_ci *                          \___________/
80762306a36Sopenharmony_ci *
80862306a36Sopenharmony_ci *          D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY.
80962306a36Sopenharmony_ci *
81062306a36Sopenharmony_ci *
81162306a36Sopenharmony_ci * <4> Now, we begin to describe how to compute the right RDN_DELAY.
81262306a36Sopenharmony_ci *
81362306a36Sopenharmony_ci *  4.1) From the aspect of the nand chip pins:
81462306a36Sopenharmony_ci *        Delay = (tREA + C - tRP)               {1}
81562306a36Sopenharmony_ci *
81662306a36Sopenharmony_ci *        tREA : the maximum read access time.
81762306a36Sopenharmony_ci *        C    : a constant to adjust the delay. default is 4000ps.
81862306a36Sopenharmony_ci *        tRP  : the read pulse width, which is exactly:
81962306a36Sopenharmony_ci *                   tRP = (GPMI-clock-period) * DATA_SETUP
82062306a36Sopenharmony_ci *
82162306a36Sopenharmony_ci *  4.2) From the aspect of the GPMI nand controller:
82262306a36Sopenharmony_ci *         Delay = RDN_DELAY * 0.125 * RP        {2}
82362306a36Sopenharmony_ci *
82462306a36Sopenharmony_ci *         RP   : the DLL reference period.
82562306a36Sopenharmony_ci *            if (GPMI-clock-period > DLL_THRETHOLD)
82662306a36Sopenharmony_ci *                   RP = GPMI-clock-period / 2;
82762306a36Sopenharmony_ci *            else
82862306a36Sopenharmony_ci *                   RP = GPMI-clock-period;
82962306a36Sopenharmony_ci *
83062306a36Sopenharmony_ci *            Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period
83162306a36Sopenharmony_ci *            is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD
83262306a36Sopenharmony_ci *            is 16000ps, but in mx6q, we use 12000ps.
83362306a36Sopenharmony_ci *
83462306a36Sopenharmony_ci *  4.3) since {1} equals {2}, we get:
83562306a36Sopenharmony_ci *
83662306a36Sopenharmony_ci *                     (tREA + 4000 - tRP) * 8
83762306a36Sopenharmony_ci *         RDN_DELAY = -----------------------     {3}
83862306a36Sopenharmony_ci *                           RP
83962306a36Sopenharmony_ci */
84062306a36Sopenharmony_cistatic int gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
84162306a36Sopenharmony_ci				    const struct nand_sdr_timings *sdr)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct gpmi_nfc_hardware_timing *hw = &this->hw;
84462306a36Sopenharmony_ci	struct resources *r = &this->resources;
84562306a36Sopenharmony_ci	unsigned int dll_threshold_ps = this->devdata->max_chain_delay;
84662306a36Sopenharmony_ci	unsigned int period_ps, reference_period_ps;
84762306a36Sopenharmony_ci	unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles;
84862306a36Sopenharmony_ci	unsigned int tRP_ps;
84962306a36Sopenharmony_ci	bool use_half_period;
85062306a36Sopenharmony_ci	int sample_delay_ps, sample_delay_factor;
85162306a36Sopenharmony_ci	unsigned int busy_timeout_cycles;
85262306a36Sopenharmony_ci	u8 wrn_dly_sel;
85362306a36Sopenharmony_ci	unsigned long clk_rate, min_rate;
85462306a36Sopenharmony_ci	u64 busy_timeout_ps;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (sdr->tRC_min >= 30000) {
85762306a36Sopenharmony_ci		/* ONFI non-EDO modes [0-3] */
85862306a36Sopenharmony_ci		hw->clk_rate = 22000000;
85962306a36Sopenharmony_ci		min_rate = 0;
86062306a36Sopenharmony_ci		wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS;
86162306a36Sopenharmony_ci	} else if (sdr->tRC_min >= 25000) {
86262306a36Sopenharmony_ci		/* ONFI EDO mode 4 */
86362306a36Sopenharmony_ci		hw->clk_rate = 80000000;
86462306a36Sopenharmony_ci		min_rate = 22000000;
86562306a36Sopenharmony_ci		wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
86662306a36Sopenharmony_ci	} else {
86762306a36Sopenharmony_ci		/* ONFI EDO mode 5 */
86862306a36Sopenharmony_ci		hw->clk_rate = 100000000;
86962306a36Sopenharmony_ci		min_rate = 80000000;
87062306a36Sopenharmony_ci		wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	clk_rate = clk_round_rate(r->clock[0], hw->clk_rate);
87462306a36Sopenharmony_ci	if (clk_rate <= min_rate) {
87562306a36Sopenharmony_ci		dev_err(this->dev, "clock setting: expected %ld, got %ld\n",
87662306a36Sopenharmony_ci			hw->clk_rate, clk_rate);
87762306a36Sopenharmony_ci		return -ENOTSUPP;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	hw->clk_rate = clk_rate;
88162306a36Sopenharmony_ci	/* SDR core timings are given in picoseconds */
88262306a36Sopenharmony_ci	period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps);
88562306a36Sopenharmony_ci	data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps);
88662306a36Sopenharmony_ci	data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps);
88762306a36Sopenharmony_ci	busy_timeout_ps = max(sdr->tBERS_max, sdr->tPROG_max);
88862306a36Sopenharmony_ci	busy_timeout_cycles = TO_CYCLES(busy_timeout_ps, period_ps);
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) |
89162306a36Sopenharmony_ci		      BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) |
89262306a36Sopenharmony_ci		      BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles);
89362306a36Sopenharmony_ci	hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(DIV_ROUND_UP(busy_timeout_cycles, 4096));
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/*
89662306a36Sopenharmony_ci	 * Derive NFC ideal delay from {3}:
89762306a36Sopenharmony_ci	 *
89862306a36Sopenharmony_ci	 *                     (tREA + 4000 - tRP) * 8
89962306a36Sopenharmony_ci	 *         RDN_DELAY = -----------------------
90062306a36Sopenharmony_ci	 *                                RP
90162306a36Sopenharmony_ci	 */
90262306a36Sopenharmony_ci	if (period_ps > dll_threshold_ps) {
90362306a36Sopenharmony_ci		use_half_period = true;
90462306a36Sopenharmony_ci		reference_period_ps = period_ps / 2;
90562306a36Sopenharmony_ci	} else {
90662306a36Sopenharmony_ci		use_half_period = false;
90762306a36Sopenharmony_ci		reference_period_ps = period_ps;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	tRP_ps = data_setup_cycles * period_ps;
91162306a36Sopenharmony_ci	sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8;
91262306a36Sopenharmony_ci	if (sample_delay_ps > 0)
91362306a36Sopenharmony_ci		sample_delay_factor = sample_delay_ps / reference_period_ps;
91462306a36Sopenharmony_ci	else
91562306a36Sopenharmony_ci		sample_delay_factor = 0;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel);
91862306a36Sopenharmony_ci	if (sample_delay_factor)
91962306a36Sopenharmony_ci		hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) |
92062306a36Sopenharmony_ci			      BM_GPMI_CTRL1_DLL_ENABLE |
92162306a36Sopenharmony_ci			      (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
92262306a36Sopenharmony_ci	return 0;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic int gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	struct gpmi_nfc_hardware_timing *hw = &this->hw;
92862306a36Sopenharmony_ci	struct resources *r = &this->resources;
92962306a36Sopenharmony_ci	void __iomem *gpmi_regs = r->gpmi_regs;
93062306a36Sopenharmony_ci	unsigned int dll_wait_time_us;
93162306a36Sopenharmony_ci	int ret;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* Clock dividers do NOT guarantee a clean clock signal on its output
93462306a36Sopenharmony_ci	 * during the change of the divide factor on i.MX6Q/UL/SX. On i.MX7/8,
93562306a36Sopenharmony_ci	 * all clock dividers provide these guarantee.
93662306a36Sopenharmony_ci	 */
93762306a36Sopenharmony_ci	if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this))
93862306a36Sopenharmony_ci		clk_disable_unprepare(r->clock[0]);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	ret = clk_set_rate(r->clock[0], hw->clk_rate);
94162306a36Sopenharmony_ci	if (ret) {
94262306a36Sopenharmony_ci		dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret);
94362306a36Sopenharmony_ci		return ret;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) {
94762306a36Sopenharmony_ci		ret = clk_prepare_enable(r->clock[0]);
94862306a36Sopenharmony_ci		if (ret)
94962306a36Sopenharmony_ci			return ret;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0);
95362306a36Sopenharmony_ci	writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/*
95662306a36Sopenharmony_ci	 * Clear several CTRL1 fields, DLL must be disabled when setting
95762306a36Sopenharmony_ci	 * RDN_DELAY or HALF_PERIOD.
95862306a36Sopenharmony_ci	 */
95962306a36Sopenharmony_ci	writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR);
96062306a36Sopenharmony_ci	writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* Wait 64 clock cycles before using the GPMI after enabling the DLL */
96362306a36Sopenharmony_ci	dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64;
96462306a36Sopenharmony_ci	if (!dll_wait_time_us)
96562306a36Sopenharmony_ci		dll_wait_time_us = 1;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	/* Wait for the DLL to settle. */
96862306a36Sopenharmony_ci	udelay(dll_wait_time_us);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return 0;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
97462306a36Sopenharmony_ci				const struct nand_interface_config *conf)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
97762306a36Sopenharmony_ci	const struct nand_sdr_timings *sdr;
97862306a36Sopenharmony_ci	int ret;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	/* Retrieve required NAND timings */
98162306a36Sopenharmony_ci	sdr = nand_get_sdr_timings(conf);
98262306a36Sopenharmony_ci	if (IS_ERR(sdr))
98362306a36Sopenharmony_ci		return PTR_ERR(sdr);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Only MX28/MX6 GPMI controller can reach EDO timings */
98662306a36Sopenharmony_ci	if (sdr->tRC_min <= 25000 && !GPMI_IS_MX28(this) && !GPMI_IS_MX6(this))
98762306a36Sopenharmony_ci		return -ENOTSUPP;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* Stop here if this call was just a check */
99062306a36Sopenharmony_ci	if (chipnr < 0)
99162306a36Sopenharmony_ci		return 0;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* Do the actual derivation of the controller timings */
99462306a36Sopenharmony_ci	ret = gpmi_nfc_compute_timings(this, sdr);
99562306a36Sopenharmony_ci	if (ret)
99662306a36Sopenharmony_ci		return ret;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	this->hw.must_apply_timings = true;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/* Clears a BCH interrupt. */
100462306a36Sopenharmony_cistatic void gpmi_clear_bch(struct gpmi_nand_data *this)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct resources *r = &this->resources;
100762306a36Sopenharmony_ci	writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR);
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic struct dma_chan *get_dma_chan(struct gpmi_nand_data *this)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	/* We use the DMA channel 0 to access all the nand chips. */
101362306a36Sopenharmony_ci	return this->dma_chans[0];
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci/* This will be called after the DMA operation is finished. */
101762306a36Sopenharmony_cistatic void dma_irq_callback(void *param)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct gpmi_nand_data *this = param;
102062306a36Sopenharmony_ci	struct completion *dma_c = &this->dma_done;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	complete(dma_c);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_cistatic irqreturn_t bch_irq(int irq, void *cookie)
102662306a36Sopenharmony_ci{
102762306a36Sopenharmony_ci	struct gpmi_nand_data *this = cookie;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	gpmi_clear_bch(this);
103062306a36Sopenharmony_ci	complete(&this->bch_done);
103162306a36Sopenharmony_ci	return IRQ_HANDLED;
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_cistatic int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	/*
103762306a36Sopenharmony_ci	 * raw_len is the length to read/write including bch data which
103862306a36Sopenharmony_ci	 * we are passed in exec_op. Calculate the data length from it.
103962306a36Sopenharmony_ci	 */
104062306a36Sopenharmony_ci	if (this->bch)
104162306a36Sopenharmony_ci		return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size);
104262306a36Sopenharmony_ci	else
104362306a36Sopenharmony_ci		return raw_len;
104462306a36Sopenharmony_ci}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci/* Can we use the upper's buffer directly for DMA? */
104762306a36Sopenharmony_cistatic bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf,
104862306a36Sopenharmony_ci			     int raw_len, struct scatterlist *sgl,
104962306a36Sopenharmony_ci			     enum dma_data_direction dr)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	int ret;
105262306a36Sopenharmony_ci	int len = gpmi_raw_len_to_len(this, raw_len);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/* first try to map the upper buffer directly */
105562306a36Sopenharmony_ci	if (virt_addr_valid(buf) && !object_is_on_stack(buf)) {
105662306a36Sopenharmony_ci		sg_init_one(sgl, buf, len);
105762306a36Sopenharmony_ci		ret = dma_map_sg(this->dev, sgl, 1, dr);
105862306a36Sopenharmony_ci		if (ret == 0)
105962306a36Sopenharmony_ci			goto map_fail;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		return true;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cimap_fail:
106562306a36Sopenharmony_ci	/* We have to use our own DMA buffer. */
106662306a36Sopenharmony_ci	sg_init_one(sgl, this->data_buffer_dma, len);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (dr == DMA_TO_DEVICE && buf != this->data_buffer_dma)
106962306a36Sopenharmony_ci		memcpy(this->data_buffer_dma, buf, len);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	dma_map_sg(this->dev, sgl, 1, dr);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	return false;
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci/* add our owner bbt descriptor */
107762306a36Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff };
107862306a36Sopenharmony_cistatic struct nand_bbt_descr gpmi_bbt_descr = {
107962306a36Sopenharmony_ci	.options	= 0,
108062306a36Sopenharmony_ci	.offs		= 0,
108162306a36Sopenharmony_ci	.len		= 1,
108262306a36Sopenharmony_ci	.pattern	= scan_ff_pattern
108362306a36Sopenharmony_ci};
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci/*
108662306a36Sopenharmony_ci * We may change the layout if we can get the ECC info from the datasheet,
108762306a36Sopenharmony_ci * else we will use all the (page + OOB).
108862306a36Sopenharmony_ci */
108962306a36Sopenharmony_cistatic int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section,
109062306a36Sopenharmony_ci			      struct mtd_oob_region *oobregion)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
109362306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
109462306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	if (section)
109762306a36Sopenharmony_ci		return -ERANGE;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	oobregion->offset = 0;
110062306a36Sopenharmony_ci	oobregion->length = geo->page_size - mtd->writesize;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	return 0;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic int gpmi_ooblayout_free(struct mtd_info *mtd, int section,
110662306a36Sopenharmony_ci			       struct mtd_oob_region *oobregion)
110762306a36Sopenharmony_ci{
110862306a36Sopenharmony_ci	struct nand_chip *chip = mtd_to_nand(mtd);
110962306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
111062306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (section)
111362306a36Sopenharmony_ci		return -ERANGE;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* The available oob size we have. */
111662306a36Sopenharmony_ci	if (geo->page_size < mtd->writesize + mtd->oobsize) {
111762306a36Sopenharmony_ci		oobregion->offset = geo->page_size - mtd->writesize;
111862306a36Sopenharmony_ci		oobregion->length = mtd->oobsize - oobregion->offset;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	return 0;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cistatic const char * const gpmi_clks_for_mx2x[] = {
112562306a36Sopenharmony_ci	"gpmi_io",
112662306a36Sopenharmony_ci};
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops gpmi_ooblayout_ops = {
112962306a36Sopenharmony_ci	.ecc = gpmi_ooblayout_ecc,
113062306a36Sopenharmony_ci	.free = gpmi_ooblayout_free,
113162306a36Sopenharmony_ci};
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx23 = {
113462306a36Sopenharmony_ci	.type = IS_MX23,
113562306a36Sopenharmony_ci	.bch_max_ecc_strength = 20,
113662306a36Sopenharmony_ci	.max_chain_delay = 16000,
113762306a36Sopenharmony_ci	.clks = gpmi_clks_for_mx2x,
113862306a36Sopenharmony_ci	.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
113962306a36Sopenharmony_ci};
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx28 = {
114262306a36Sopenharmony_ci	.type = IS_MX28,
114362306a36Sopenharmony_ci	.bch_max_ecc_strength = 20,
114462306a36Sopenharmony_ci	.max_chain_delay = 16000,
114562306a36Sopenharmony_ci	.clks = gpmi_clks_for_mx2x,
114662306a36Sopenharmony_ci	.clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x),
114762306a36Sopenharmony_ci};
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic const char * const gpmi_clks_for_mx6[] = {
115062306a36Sopenharmony_ci	"gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch",
115162306a36Sopenharmony_ci};
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx6q = {
115462306a36Sopenharmony_ci	.type = IS_MX6Q,
115562306a36Sopenharmony_ci	.bch_max_ecc_strength = 40,
115662306a36Sopenharmony_ci	.max_chain_delay = 12000,
115762306a36Sopenharmony_ci	.clks = gpmi_clks_for_mx6,
115862306a36Sopenharmony_ci	.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
115962306a36Sopenharmony_ci};
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx6sx = {
116262306a36Sopenharmony_ci	.type = IS_MX6SX,
116362306a36Sopenharmony_ci	.bch_max_ecc_strength = 62,
116462306a36Sopenharmony_ci	.max_chain_delay = 12000,
116562306a36Sopenharmony_ci	.clks = gpmi_clks_for_mx6,
116662306a36Sopenharmony_ci	.clks_count = ARRAY_SIZE(gpmi_clks_for_mx6),
116762306a36Sopenharmony_ci};
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_cistatic const char * const gpmi_clks_for_mx7d[] = {
117062306a36Sopenharmony_ci	"gpmi_io", "gpmi_bch_apb",
117162306a36Sopenharmony_ci};
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx7d = {
117462306a36Sopenharmony_ci	.type = IS_MX7D,
117562306a36Sopenharmony_ci	.bch_max_ecc_strength = 62,
117662306a36Sopenharmony_ci	.max_chain_delay = 12000,
117762306a36Sopenharmony_ci	.clks = gpmi_clks_for_mx7d,
117862306a36Sopenharmony_ci	.clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d),
117962306a36Sopenharmony_ci};
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistatic int acquire_register_block(struct gpmi_nand_data *this,
118262306a36Sopenharmony_ci				  const char *res_name)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct platform_device *pdev = this->pdev;
118562306a36Sopenharmony_ci	struct resources *res = &this->resources;
118662306a36Sopenharmony_ci	void __iomem *p;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	p = devm_platform_ioremap_resource_byname(pdev, res_name);
118962306a36Sopenharmony_ci	if (IS_ERR(p))
119062306a36Sopenharmony_ci		return PTR_ERR(p);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME))
119362306a36Sopenharmony_ci		res->gpmi_regs = p;
119462306a36Sopenharmony_ci	else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME))
119562306a36Sopenharmony_ci		res->bch_regs = p;
119662306a36Sopenharmony_ci	else
119762306a36Sopenharmony_ci		dev_err(this->dev, "unknown resource name : %s\n", res_name);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return 0;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	struct platform_device *pdev = this->pdev;
120562306a36Sopenharmony_ci	const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
120662306a36Sopenharmony_ci	int err;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	err = platform_get_irq_byname(pdev, res_name);
120962306a36Sopenharmony_ci	if (err < 0)
121062306a36Sopenharmony_ci		return err;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	err = devm_request_irq(this->dev, err, irq_h, 0, res_name, this);
121362306a36Sopenharmony_ci	if (err)
121462306a36Sopenharmony_ci		dev_err(this->dev, "error requesting BCH IRQ\n");
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return err;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic void release_dma_channels(struct gpmi_nand_data *this)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	unsigned int i;
122262306a36Sopenharmony_ci	for (i = 0; i < DMA_CHANS; i++)
122362306a36Sopenharmony_ci		if (this->dma_chans[i]) {
122462306a36Sopenharmony_ci			dma_release_channel(this->dma_chans[i]);
122562306a36Sopenharmony_ci			this->dma_chans[i] = NULL;
122662306a36Sopenharmony_ci		}
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic int acquire_dma_channels(struct gpmi_nand_data *this)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	struct platform_device *pdev = this->pdev;
123262306a36Sopenharmony_ci	struct dma_chan *dma_chan;
123362306a36Sopenharmony_ci	int ret = 0;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* request dma channel */
123662306a36Sopenharmony_ci	dma_chan = dma_request_chan(&pdev->dev, "rx-tx");
123762306a36Sopenharmony_ci	if (IS_ERR(dma_chan)) {
123862306a36Sopenharmony_ci		ret = dev_err_probe(this->dev, PTR_ERR(dma_chan),
123962306a36Sopenharmony_ci				    "DMA channel request failed\n");
124062306a36Sopenharmony_ci		release_dma_channels(this);
124162306a36Sopenharmony_ci	} else {
124262306a36Sopenharmony_ci		this->dma_chans[0] = dma_chan;
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return ret;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic int gpmi_get_clks(struct gpmi_nand_data *this)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct resources *r = &this->resources;
125162306a36Sopenharmony_ci	struct clk *clk;
125262306a36Sopenharmony_ci	int err, i;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	for (i = 0; i < this->devdata->clks_count; i++) {
125562306a36Sopenharmony_ci		clk = devm_clk_get(this->dev, this->devdata->clks[i]);
125662306a36Sopenharmony_ci		if (IS_ERR(clk)) {
125762306a36Sopenharmony_ci			err = PTR_ERR(clk);
125862306a36Sopenharmony_ci			goto err_clock;
125962306a36Sopenharmony_ci		}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci		r->clock[i] = clk;
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	return 0;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cierr_clock:
126762306a36Sopenharmony_ci	dev_dbg(this->dev, "failed in finding the clocks.\n");
126862306a36Sopenharmony_ci	return err;
126962306a36Sopenharmony_ci}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_cistatic int acquire_resources(struct gpmi_nand_data *this)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	int ret;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME);
127662306a36Sopenharmony_ci	if (ret)
127762306a36Sopenharmony_ci		goto exit_regs;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME);
128062306a36Sopenharmony_ci	if (ret)
128162306a36Sopenharmony_ci		goto exit_regs;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	ret = acquire_bch_irq(this, bch_irq);
128462306a36Sopenharmony_ci	if (ret)
128562306a36Sopenharmony_ci		goto exit_regs;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	ret = acquire_dma_channels(this);
128862306a36Sopenharmony_ci	if (ret)
128962306a36Sopenharmony_ci		goto exit_regs;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	ret = gpmi_get_clks(this);
129262306a36Sopenharmony_ci	if (ret)
129362306a36Sopenharmony_ci		goto exit_clock;
129462306a36Sopenharmony_ci	return 0;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ciexit_clock:
129762306a36Sopenharmony_ci	release_dma_channels(this);
129862306a36Sopenharmony_ciexit_regs:
129962306a36Sopenharmony_ci	return ret;
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic void release_resources(struct gpmi_nand_data *this)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	release_dma_channels(this);
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_cistatic void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
130862306a36Sopenharmony_ci{
130962306a36Sopenharmony_ci	struct device *dev = this->dev;
131062306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	if (this->auxiliary_virt && virt_addr_valid(this->auxiliary_virt))
131362306a36Sopenharmony_ci		dma_free_coherent(dev, geo->auxiliary_size,
131462306a36Sopenharmony_ci					this->auxiliary_virt,
131562306a36Sopenharmony_ci					this->auxiliary_phys);
131662306a36Sopenharmony_ci	kfree(this->data_buffer_dma);
131762306a36Sopenharmony_ci	kfree(this->raw_buffer);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	this->data_buffer_dma	= NULL;
132062306a36Sopenharmony_ci	this->raw_buffer	= NULL;
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci/* Allocate the DMA buffers */
132462306a36Sopenharmony_cistatic int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
132762306a36Sopenharmony_ci	struct device *dev = this->dev;
132862306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&this->nand);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/*
133162306a36Sopenharmony_ci	 * [2] Allocate a read/write data buffer.
133262306a36Sopenharmony_ci	 *     The gpmi_alloc_dma_buffer can be called twice.
133362306a36Sopenharmony_ci	 *     We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer
133462306a36Sopenharmony_ci	 *     is called before the NAND identification; and we allocate a
133562306a36Sopenharmony_ci	 *     buffer of the real NAND page size when the gpmi_alloc_dma_buffer
133662306a36Sopenharmony_ci	 *     is called after.
133762306a36Sopenharmony_ci	 */
133862306a36Sopenharmony_ci	this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE,
133962306a36Sopenharmony_ci					GFP_DMA | GFP_KERNEL);
134062306a36Sopenharmony_ci	if (this->data_buffer_dma == NULL)
134162306a36Sopenharmony_ci		goto error_alloc;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	this->auxiliary_virt = dma_alloc_coherent(dev, geo->auxiliary_size,
134462306a36Sopenharmony_ci					&this->auxiliary_phys, GFP_DMA);
134562306a36Sopenharmony_ci	if (!this->auxiliary_virt)
134662306a36Sopenharmony_ci		goto error_alloc;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	this->raw_buffer = kzalloc((mtd->writesize ?: PAGE_SIZE) + mtd->oobsize, GFP_KERNEL);
134962306a36Sopenharmony_ci	if (!this->raw_buffer)
135062306a36Sopenharmony_ci		goto error_alloc;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	return 0;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cierror_alloc:
135562306a36Sopenharmony_ci	gpmi_free_dma_buffer(this);
135662306a36Sopenharmony_ci	return -ENOMEM;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci/*
136062306a36Sopenharmony_ci * Handles block mark swapping.
136162306a36Sopenharmony_ci * It can be called in swapping the block mark, or swapping it back,
136262306a36Sopenharmony_ci * because the operations are the same.
136362306a36Sopenharmony_ci */
136462306a36Sopenharmony_cistatic void block_mark_swapping(struct gpmi_nand_data *this,
136562306a36Sopenharmony_ci				void *payload, void *auxiliary)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct bch_geometry *nfc_geo = &this->bch_geometry;
136862306a36Sopenharmony_ci	unsigned char *p;
136962306a36Sopenharmony_ci	unsigned char *a;
137062306a36Sopenharmony_ci	unsigned int  bit;
137162306a36Sopenharmony_ci	unsigned char mask;
137262306a36Sopenharmony_ci	unsigned char from_data;
137362306a36Sopenharmony_ci	unsigned char from_oob;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	if (!this->swap_block_mark)
137662306a36Sopenharmony_ci		return;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	/*
137962306a36Sopenharmony_ci	 * If control arrives here, we're swapping. Make some convenience
138062306a36Sopenharmony_ci	 * variables.
138162306a36Sopenharmony_ci	 */
138262306a36Sopenharmony_ci	bit = nfc_geo->block_mark_bit_offset;
138362306a36Sopenharmony_ci	p   = payload + nfc_geo->block_mark_byte_offset;
138462306a36Sopenharmony_ci	a   = auxiliary;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	/*
138762306a36Sopenharmony_ci	 * Get the byte from the data area that overlays the block mark. Since
138862306a36Sopenharmony_ci	 * the ECC engine applies its own view to the bits in the page, the
138962306a36Sopenharmony_ci	 * physical block mark won't (in general) appear on a byte boundary in
139062306a36Sopenharmony_ci	 * the data.
139162306a36Sopenharmony_ci	 */
139262306a36Sopenharmony_ci	from_data = (p[0] >> bit) | (p[1] << (8 - bit));
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	/* Get the byte from the OOB. */
139562306a36Sopenharmony_ci	from_oob = a[0];
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* Swap them. */
139862306a36Sopenharmony_ci	a[0] = from_data;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	mask = (0x1 << bit) - 1;
140162306a36Sopenharmony_ci	p[0] = (p[0] & mask) | (from_oob << bit);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	mask = ~0 << bit;
140462306a36Sopenharmony_ci	p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistatic int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first,
140862306a36Sopenharmony_ci			       int last, int meta)
140962306a36Sopenharmony_ci{
141062306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
141162306a36Sopenharmony_ci	struct bch_geometry *nfc_geo = &this->bch_geometry;
141262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
141362306a36Sopenharmony_ci	int i;
141462306a36Sopenharmony_ci	unsigned char *status;
141562306a36Sopenharmony_ci	unsigned int max_bitflips = 0;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	/* Loop over status bytes, accumulating ECC status. */
141862306a36Sopenharmony_ci	status = this->auxiliary_virt + ALIGN(meta, 4);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	for (i = first; i < last; i++, status++) {
142162306a36Sopenharmony_ci		if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
142262306a36Sopenharmony_ci			continue;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		if (*status == STATUS_UNCORRECTABLE) {
142562306a36Sopenharmony_ci			int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
142662306a36Sopenharmony_ci			u8 *eccbuf = this->raw_buffer;
142762306a36Sopenharmony_ci			int offset, bitoffset;
142862306a36Sopenharmony_ci			int eccbytes;
142962306a36Sopenharmony_ci			int flips;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci			/* Read ECC bytes into our internal raw_buffer */
143262306a36Sopenharmony_ci			offset = nfc_geo->metadata_size * 8;
143362306a36Sopenharmony_ci			offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1);
143462306a36Sopenharmony_ci			offset -= eccbits;
143562306a36Sopenharmony_ci			bitoffset = offset % 8;
143662306a36Sopenharmony_ci			eccbytes = DIV_ROUND_UP(offset + eccbits, 8);
143762306a36Sopenharmony_ci			offset /= 8;
143862306a36Sopenharmony_ci			eccbytes -= offset;
143962306a36Sopenharmony_ci			nand_change_read_column_op(chip, offset, eccbuf,
144062306a36Sopenharmony_ci						   eccbytes, false);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci			/*
144362306a36Sopenharmony_ci			 * ECC data are not byte aligned and we may have
144462306a36Sopenharmony_ci			 * in-band data in the first and last byte of
144562306a36Sopenharmony_ci			 * eccbuf. Set non-eccbits to one so that
144662306a36Sopenharmony_ci			 * nand_check_erased_ecc_chunk() does not count them
144762306a36Sopenharmony_ci			 * as bitflips.
144862306a36Sopenharmony_ci			 */
144962306a36Sopenharmony_ci			if (bitoffset)
145062306a36Sopenharmony_ci				eccbuf[0] |= GENMASK(bitoffset - 1, 0);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci			bitoffset = (bitoffset + eccbits) % 8;
145362306a36Sopenharmony_ci			if (bitoffset)
145462306a36Sopenharmony_ci				eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci			/*
145762306a36Sopenharmony_ci			 * The ECC hardware has an uncorrectable ECC status
145862306a36Sopenharmony_ci			 * code in case we have bitflips in an erased page. As
145962306a36Sopenharmony_ci			 * nothing was written into this subpage the ECC is
146062306a36Sopenharmony_ci			 * obviously wrong and we can not trust it. We assume
146162306a36Sopenharmony_ci			 * at this point that we are reading an erased page and
146262306a36Sopenharmony_ci			 * try to correct the bitflips in buffer up to
146362306a36Sopenharmony_ci			 * ecc_strength bitflips. If this is a page with random
146462306a36Sopenharmony_ci			 * data, we exceed this number of bitflips and have a
146562306a36Sopenharmony_ci			 * ECC failure. Otherwise we use the corrected buffer.
146662306a36Sopenharmony_ci			 */
146762306a36Sopenharmony_ci			if (i == 0) {
146862306a36Sopenharmony_ci				/* The first block includes metadata */
146962306a36Sopenharmony_ci				flips = nand_check_erased_ecc_chunk(
147062306a36Sopenharmony_ci						buf + i * nfc_geo->eccn_chunk_size,
147162306a36Sopenharmony_ci						nfc_geo->eccn_chunk_size,
147262306a36Sopenharmony_ci						eccbuf, eccbytes,
147362306a36Sopenharmony_ci						this->auxiliary_virt,
147462306a36Sopenharmony_ci						nfc_geo->metadata_size,
147562306a36Sopenharmony_ci						nfc_geo->ecc_strength);
147662306a36Sopenharmony_ci			} else {
147762306a36Sopenharmony_ci				flips = nand_check_erased_ecc_chunk(
147862306a36Sopenharmony_ci						buf + i * nfc_geo->eccn_chunk_size,
147962306a36Sopenharmony_ci						nfc_geo->eccn_chunk_size,
148062306a36Sopenharmony_ci						eccbuf, eccbytes,
148162306a36Sopenharmony_ci						NULL, 0,
148262306a36Sopenharmony_ci						nfc_geo->ecc_strength);
148362306a36Sopenharmony_ci			}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci			if (flips > 0) {
148662306a36Sopenharmony_ci				max_bitflips = max_t(unsigned int, max_bitflips,
148762306a36Sopenharmony_ci						     flips);
148862306a36Sopenharmony_ci				mtd->ecc_stats.corrected += flips;
148962306a36Sopenharmony_ci				continue;
149062306a36Sopenharmony_ci			}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
149362306a36Sopenharmony_ci			continue;
149462306a36Sopenharmony_ci		}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci		mtd->ecc_stats.corrected += *status;
149762306a36Sopenharmony_ci		max_bitflips = max_t(unsigned int, max_bitflips, *status);
149862306a36Sopenharmony_ci	}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	return max_bitflips;
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic void gpmi_bch_layout_std(struct gpmi_nand_data *this)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
150662306a36Sopenharmony_ci	unsigned int ecc_strength = geo->ecc_strength >> 1;
150762306a36Sopenharmony_ci	unsigned int gf_len = geo->gf_len;
150862306a36Sopenharmony_ci	unsigned int block0_size = geo->ecc0_chunk_size;
150962306a36Sopenharmony_ci	unsigned int blockn_size = geo->eccn_chunk_size;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	this->bch_flashlayout0 =
151262306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) |
151362306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) |
151462306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
151562306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) |
151662306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	this->bch_flashlayout1 =
151962306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) |
152062306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
152162306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) |
152262306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this);
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf,
152662306a36Sopenharmony_ci			      int oob_required, int page)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
152962306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
153062306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
153162306a36Sopenharmony_ci	unsigned int max_bitflips;
153262306a36Sopenharmony_ci	int ret;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	gpmi_bch_layout_std(this);
153562306a36Sopenharmony_ci	this->bch = true;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, 0, buf, geo->page_size);
153862306a36Sopenharmony_ci	if (ret)
153962306a36Sopenharmony_ci		return ret;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	max_bitflips = gpmi_count_bitflips(chip, buf, 0,
154262306a36Sopenharmony_ci					   geo->ecc_chunk_count,
154362306a36Sopenharmony_ci					   geo->auxiliary_status_offset);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	/* handle the block mark swapping */
154662306a36Sopenharmony_ci	block_mark_swapping(this, buf, this->auxiliary_virt);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (oob_required) {
154962306a36Sopenharmony_ci		/*
155062306a36Sopenharmony_ci		 * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob()
155162306a36Sopenharmony_ci		 * for details about our policy for delivering the OOB.
155262306a36Sopenharmony_ci		 *
155362306a36Sopenharmony_ci		 * We fill the caller's buffer with set bits, and then copy the
155462306a36Sopenharmony_ci		 * block mark to th caller's buffer. Note that, if block mark
155562306a36Sopenharmony_ci		 * swapping was necessary, it has already been done, so we can
155662306a36Sopenharmony_ci		 * rely on the first byte of the auxiliary buffer to contain
155762306a36Sopenharmony_ci		 * the block mark.
155862306a36Sopenharmony_ci		 */
155962306a36Sopenharmony_ci		memset(chip->oob_poi, ~0, mtd->oobsize);
156062306a36Sopenharmony_ci		chip->oob_poi[0] = ((uint8_t *)this->auxiliary_virt)[0];
156162306a36Sopenharmony_ci	}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	return max_bitflips;
156462306a36Sopenharmony_ci}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci/* Fake a virtual small page for the subpage read */
156762306a36Sopenharmony_cistatic int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs,
156862306a36Sopenharmony_ci				 uint32_t len, uint8_t *buf, int page)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
157162306a36Sopenharmony_ci	struct bch_geometry *geo = &this->bch_geometry;
157262306a36Sopenharmony_ci	int size = chip->ecc.size; /* ECC chunk size */
157362306a36Sopenharmony_ci	int meta, n, page_size;
157462306a36Sopenharmony_ci	unsigned int max_bitflips;
157562306a36Sopenharmony_ci	unsigned int ecc_strength;
157662306a36Sopenharmony_ci	int first, last, marker_pos;
157762306a36Sopenharmony_ci	int ecc_parity_size;
157862306a36Sopenharmony_ci	int col = 0;
157962306a36Sopenharmony_ci	int ret;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	/* The size of ECC parity */
158262306a36Sopenharmony_ci	ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	/* Align it with the chunk size */
158562306a36Sopenharmony_ci	first = offs / size;
158662306a36Sopenharmony_ci	last = (offs + len - 1) / size;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	if (this->swap_block_mark) {
158962306a36Sopenharmony_ci		/*
159062306a36Sopenharmony_ci		 * Find the chunk which contains the Block Marker.
159162306a36Sopenharmony_ci		 * If this chunk is in the range of [first, last],
159262306a36Sopenharmony_ci		 * we have to read out the whole page.
159362306a36Sopenharmony_ci		 * Why? since we had swapped the data at the position of Block
159462306a36Sopenharmony_ci		 * Marker to the metadata which is bound with the chunk 0.
159562306a36Sopenharmony_ci		 */
159662306a36Sopenharmony_ci		marker_pos = geo->block_mark_byte_offset / size;
159762306a36Sopenharmony_ci		if (last >= marker_pos && first <= marker_pos) {
159862306a36Sopenharmony_ci			dev_dbg(this->dev,
159962306a36Sopenharmony_ci				"page:%d, first:%d, last:%d, marker at:%d\n",
160062306a36Sopenharmony_ci				page, first, last, marker_pos);
160162306a36Sopenharmony_ci			return gpmi_ecc_read_page(chip, buf, 0, page);
160262306a36Sopenharmony_ci		}
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	/*
160662306a36Sopenharmony_ci	 * if there is an ECC dedicate for meta:
160762306a36Sopenharmony_ci	 * - need to add an extra ECC size when calculating col and page_size,
160862306a36Sopenharmony_ci	 *   if the meta size is NOT zero.
160962306a36Sopenharmony_ci	 * - ecc0_chunk size need to set to the same size as other chunks,
161062306a36Sopenharmony_ci	 *   if the meta size is zero.
161162306a36Sopenharmony_ci	 */
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	meta = geo->metadata_size;
161462306a36Sopenharmony_ci	if (first) {
161562306a36Sopenharmony_ci		if (geo->ecc_for_meta)
161662306a36Sopenharmony_ci			col = meta + ecc_parity_size
161762306a36Sopenharmony_ci				+ (size + ecc_parity_size) * first;
161862306a36Sopenharmony_ci		else
161962306a36Sopenharmony_ci			col = meta + (size + ecc_parity_size) * first;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci		meta = 0;
162262306a36Sopenharmony_ci		buf = buf + first * size;
162362306a36Sopenharmony_ci	}
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
162662306a36Sopenharmony_ci	n = last - first + 1;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	if (geo->ecc_for_meta && meta)
162962306a36Sopenharmony_ci		page_size = meta + ecc_parity_size
163062306a36Sopenharmony_ci			    + (size + ecc_parity_size) * n;
163162306a36Sopenharmony_ci	else
163262306a36Sopenharmony_ci		page_size = meta + (size + ecc_parity_size) * n;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	ecc_strength = geo->ecc_strength >> 1;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(
163762306a36Sopenharmony_ci		(geo->ecc_for_meta ? n : n - 1)) |
163862306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) |
163962306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) |
164062306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) |
164162306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ?
164262306a36Sopenharmony_ci		0 : geo->ecc0_chunk_size), this);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) |
164562306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) |
164662306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) |
164762306a36Sopenharmony_ci		BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this);
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	this->bch = true;
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, col, buf, page_size);
165262306a36Sopenharmony_ci	if (ret)
165362306a36Sopenharmony_ci		return ret;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
165662306a36Sopenharmony_ci		page, offs, len, col, first, n, page_size);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	max_bitflips = gpmi_count_bitflips(chip, buf, first, last, meta);
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	return max_bitflips;
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
166462306a36Sopenharmony_ci			       int oob_required, int page)
166562306a36Sopenharmony_ci{
166662306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
166762306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
166862306a36Sopenharmony_ci	struct bch_geometry *nfc_geo = &this->bch_geometry;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	dev_dbg(this->dev, "ecc write page.\n");
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	gpmi_bch_layout_std(this);
167362306a36Sopenharmony_ci	this->bch = true;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	memcpy(this->auxiliary_virt, chip->oob_poi, nfc_geo->auxiliary_size);
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	if (this->swap_block_mark) {
167862306a36Sopenharmony_ci		/*
167962306a36Sopenharmony_ci		 * When doing bad block marker swapping we must always copy the
168062306a36Sopenharmony_ci		 * input buffer as we can't modify the const buffer.
168162306a36Sopenharmony_ci		 */
168262306a36Sopenharmony_ci		memcpy(this->data_buffer_dma, buf, mtd->writesize);
168362306a36Sopenharmony_ci		buf = this->data_buffer_dma;
168462306a36Sopenharmony_ci		block_mark_swapping(this, this->data_buffer_dma,
168562306a36Sopenharmony_ci				    this->auxiliary_virt);
168662306a36Sopenharmony_ci	}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	return nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size);
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci/*
169262306a36Sopenharmony_ci * There are several places in this driver where we have to handle the OOB and
169362306a36Sopenharmony_ci * block marks. This is the function where things are the most complicated, so
169462306a36Sopenharmony_ci * this is where we try to explain it all. All the other places refer back to
169562306a36Sopenharmony_ci * here.
169662306a36Sopenharmony_ci *
169762306a36Sopenharmony_ci * These are the rules, in order of decreasing importance:
169862306a36Sopenharmony_ci *
169962306a36Sopenharmony_ci * 1) Nothing the caller does can be allowed to imperil the block mark.
170062306a36Sopenharmony_ci *
170162306a36Sopenharmony_ci * 2) In read operations, the first byte of the OOB we return must reflect the
170262306a36Sopenharmony_ci *    true state of the block mark, no matter where that block mark appears in
170362306a36Sopenharmony_ci *    the physical page.
170462306a36Sopenharmony_ci *
170562306a36Sopenharmony_ci * 3) ECC-based read operations return an OOB full of set bits (since we never
170662306a36Sopenharmony_ci *    allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads
170762306a36Sopenharmony_ci *    return).
170862306a36Sopenharmony_ci *
170962306a36Sopenharmony_ci * 4) "Raw" read operations return a direct view of the physical bytes in the
171062306a36Sopenharmony_ci *    page, using the conventional definition of which bytes are data and which
171162306a36Sopenharmony_ci *    are OOB. This gives the caller a way to see the actual, physical bytes
171262306a36Sopenharmony_ci *    in the page, without the distortions applied by our ECC engine.
171362306a36Sopenharmony_ci *
171462306a36Sopenharmony_ci *
171562306a36Sopenharmony_ci * What we do for this specific read operation depends on two questions:
171662306a36Sopenharmony_ci *
171762306a36Sopenharmony_ci * 1) Are we doing a "raw" read, or an ECC-based read?
171862306a36Sopenharmony_ci *
171962306a36Sopenharmony_ci * 2) Are we using block mark swapping or transcription?
172062306a36Sopenharmony_ci *
172162306a36Sopenharmony_ci * There are four cases, illustrated by the following Karnaugh map:
172262306a36Sopenharmony_ci *
172362306a36Sopenharmony_ci *                    |           Raw           |         ECC-based       |
172462306a36Sopenharmony_ci *       -------------+-------------------------+-------------------------+
172562306a36Sopenharmony_ci *                    | Read the conventional   |                         |
172662306a36Sopenharmony_ci *                    | OOB at the end of the   |                         |
172762306a36Sopenharmony_ci *       Swapping     | page and return it. It  |                         |
172862306a36Sopenharmony_ci *                    | contains exactly what   |                         |
172962306a36Sopenharmony_ci *                    | we want.                | Read the block mark and |
173062306a36Sopenharmony_ci *       -------------+-------------------------+ return it in a buffer   |
173162306a36Sopenharmony_ci *                    | Read the conventional   | full of set bits.       |
173262306a36Sopenharmony_ci *                    | OOB at the end of the   |                         |
173362306a36Sopenharmony_ci *                    | page and also the block |                         |
173462306a36Sopenharmony_ci *       Transcribing | mark in the metadata.   |                         |
173562306a36Sopenharmony_ci *                    | Copy the block mark     |                         |
173662306a36Sopenharmony_ci *                    | into the first byte of  |                         |
173762306a36Sopenharmony_ci *                    | the OOB.                |                         |
173862306a36Sopenharmony_ci *       -------------+-------------------------+-------------------------+
173962306a36Sopenharmony_ci *
174062306a36Sopenharmony_ci * Note that we break rule #4 in the Transcribing/Raw case because we're not
174162306a36Sopenharmony_ci * giving an accurate view of the actual, physical bytes in the page (we're
174262306a36Sopenharmony_ci * overwriting the block mark). That's OK because it's more important to follow
174362306a36Sopenharmony_ci * rule #2.
174462306a36Sopenharmony_ci *
174562306a36Sopenharmony_ci * It turns out that knowing whether we want an "ECC-based" or "raw" read is not
174662306a36Sopenharmony_ci * easy. When reading a page, for example, the NAND Flash MTD code calls our
174762306a36Sopenharmony_ci * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
174862306a36Sopenharmony_ci * ECC-based or raw view of the page is implicit in which function it calls
174962306a36Sopenharmony_ci * (there is a similar pair of ECC-based/raw functions for writing).
175062306a36Sopenharmony_ci */
175162306a36Sopenharmony_cistatic int gpmi_ecc_read_oob(struct nand_chip *chip, int page)
175262306a36Sopenharmony_ci{
175362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
175462306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
175562306a36Sopenharmony_ci	int ret;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	/* clear the OOB buffer */
175862306a36Sopenharmony_ci	memset(chip->oob_poi, ~0, mtd->oobsize);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	/* Read out the conventional OOB. */
176162306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, mtd->writesize, chip->oob_poi,
176262306a36Sopenharmony_ci				mtd->oobsize);
176362306a36Sopenharmony_ci	if (ret)
176462306a36Sopenharmony_ci		return ret;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	/*
176762306a36Sopenharmony_ci	 * Now, we want to make sure the block mark is correct. In the
176862306a36Sopenharmony_ci	 * non-transcribing case (!GPMI_IS_MX23()), we already have it.
176962306a36Sopenharmony_ci	 * Otherwise, we need to explicitly read it.
177062306a36Sopenharmony_ci	 */
177162306a36Sopenharmony_ci	if (GPMI_IS_MX23(this)) {
177262306a36Sopenharmony_ci		/* Read the block mark into the first byte of the OOB buffer. */
177362306a36Sopenharmony_ci		ret = nand_read_page_op(chip, page, 0, chip->oob_poi, 1);
177462306a36Sopenharmony_ci		if (ret)
177562306a36Sopenharmony_ci			return ret;
177662306a36Sopenharmony_ci	}
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	return 0;
177962306a36Sopenharmony_ci}
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_cistatic int gpmi_ecc_write_oob(struct nand_chip *chip, int page)
178262306a36Sopenharmony_ci{
178362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
178462306a36Sopenharmony_ci	struct mtd_oob_region of = { };
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* Do we have available oob area? */
178762306a36Sopenharmony_ci	mtd_ooblayout_free(mtd, 0, &of);
178862306a36Sopenharmony_ci	if (!of.length)
178962306a36Sopenharmony_ci		return -EPERM;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	if (!nand_is_slc(chip))
179262306a36Sopenharmony_ci		return -EPERM;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	return nand_prog_page_op(chip, page, mtd->writesize + of.offset,
179562306a36Sopenharmony_ci				 chip->oob_poi + of.offset, of.length);
179662306a36Sopenharmony_ci}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci/*
179962306a36Sopenharmony_ci * This function reads a NAND page without involving the ECC engine (no HW
180062306a36Sopenharmony_ci * ECC correction).
180162306a36Sopenharmony_ci * The tricky part in the GPMI/BCH controller is that it stores ECC bits
180262306a36Sopenharmony_ci * inline (interleaved with payload DATA), and do not align data chunk on
180362306a36Sopenharmony_ci * byte boundaries.
180462306a36Sopenharmony_ci * We thus need to take care moving the payload data and ECC bits stored in the
180562306a36Sopenharmony_ci * page into the provided buffers, which is why we're using nand_extract_bits().
180662306a36Sopenharmony_ci *
180762306a36Sopenharmony_ci * See set_geometry_by_ecc_info inline comments to have a full description
180862306a36Sopenharmony_ci * of the layout used by the GPMI controller.
180962306a36Sopenharmony_ci */
181062306a36Sopenharmony_cistatic int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
181162306a36Sopenharmony_ci				  int oob_required, int page)
181262306a36Sopenharmony_ci{
181362306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
181462306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
181562306a36Sopenharmony_ci	struct bch_geometry *nfc_geo = &this->bch_geometry;
181662306a36Sopenharmony_ci	int eccsize = nfc_geo->eccn_chunk_size;
181762306a36Sopenharmony_ci	int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
181862306a36Sopenharmony_ci	u8 *tmp_buf = this->raw_buffer;
181962306a36Sopenharmony_ci	size_t src_bit_off;
182062306a36Sopenharmony_ci	size_t oob_bit_off;
182162306a36Sopenharmony_ci	size_t oob_byte_off;
182262306a36Sopenharmony_ci	uint8_t *oob = chip->oob_poi;
182362306a36Sopenharmony_ci	int step;
182462306a36Sopenharmony_ci	int ret;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, 0, tmp_buf,
182762306a36Sopenharmony_ci				mtd->writesize + mtd->oobsize);
182862306a36Sopenharmony_ci	if (ret)
182962306a36Sopenharmony_ci		return ret;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	/*
183262306a36Sopenharmony_ci	 * If required, swap the bad block marker and the data stored in the
183362306a36Sopenharmony_ci	 * metadata section, so that we don't wrongly consider a block as bad.
183462306a36Sopenharmony_ci	 *
183562306a36Sopenharmony_ci	 * See the layout description for a detailed explanation on why this
183662306a36Sopenharmony_ci	 * is needed.
183762306a36Sopenharmony_ci	 */
183862306a36Sopenharmony_ci	if (this->swap_block_mark)
183962306a36Sopenharmony_ci		swap(tmp_buf[0], tmp_buf[mtd->writesize]);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	/*
184262306a36Sopenharmony_ci	 * Copy the metadata section into the oob buffer (this section is
184362306a36Sopenharmony_ci	 * guaranteed to be aligned on a byte boundary).
184462306a36Sopenharmony_ci	 */
184562306a36Sopenharmony_ci	if (oob_required)
184662306a36Sopenharmony_ci		memcpy(oob, tmp_buf, nfc_geo->metadata_size);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	oob_bit_off = nfc_geo->metadata_size * 8;
184962306a36Sopenharmony_ci	src_bit_off = oob_bit_off;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/* Extract interleaved payload data and ECC bits */
185262306a36Sopenharmony_ci	for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
185362306a36Sopenharmony_ci		if (buf)
185462306a36Sopenharmony_ci			nand_extract_bits(buf, step * eccsize * 8, tmp_buf,
185562306a36Sopenharmony_ci					  src_bit_off, eccsize * 8);
185662306a36Sopenharmony_ci		src_bit_off += eccsize * 8;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci		/* Align last ECC block to align a byte boundary */
185962306a36Sopenharmony_ci		if (step == nfc_geo->ecc_chunk_count - 1 &&
186062306a36Sopenharmony_ci		    (oob_bit_off + eccbits) % 8)
186162306a36Sopenharmony_ci			eccbits += 8 - ((oob_bit_off + eccbits) % 8);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci		if (oob_required)
186462306a36Sopenharmony_ci			nand_extract_bits(oob, oob_bit_off, tmp_buf,
186562306a36Sopenharmony_ci					  src_bit_off, eccbits);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci		src_bit_off += eccbits;
186862306a36Sopenharmony_ci		oob_bit_off += eccbits;
186962306a36Sopenharmony_ci	}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (oob_required) {
187262306a36Sopenharmony_ci		oob_byte_off = oob_bit_off / 8;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci		if (oob_byte_off < mtd->oobsize)
187562306a36Sopenharmony_ci			memcpy(oob + oob_byte_off,
187662306a36Sopenharmony_ci			       tmp_buf + mtd->writesize + oob_byte_off,
187762306a36Sopenharmony_ci			       mtd->oobsize - oob_byte_off);
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	return 0;
188162306a36Sopenharmony_ci}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci/*
188462306a36Sopenharmony_ci * This function writes a NAND page without involving the ECC engine (no HW
188562306a36Sopenharmony_ci * ECC generation).
188662306a36Sopenharmony_ci * The tricky part in the GPMI/BCH controller is that it stores ECC bits
188762306a36Sopenharmony_ci * inline (interleaved with payload DATA), and do not align data chunk on
188862306a36Sopenharmony_ci * byte boundaries.
188962306a36Sopenharmony_ci * We thus need to take care moving the OOB area at the right place in the
189062306a36Sopenharmony_ci * final page, which is why we're using nand_extract_bits().
189162306a36Sopenharmony_ci *
189262306a36Sopenharmony_ci * See set_geometry_by_ecc_info inline comments to have a full description
189362306a36Sopenharmony_ci * of the layout used by the GPMI controller.
189462306a36Sopenharmony_ci */
189562306a36Sopenharmony_cistatic int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
189662306a36Sopenharmony_ci				   int oob_required, int page)
189762306a36Sopenharmony_ci{
189862306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
189962306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
190062306a36Sopenharmony_ci	struct bch_geometry *nfc_geo = &this->bch_geometry;
190162306a36Sopenharmony_ci	int eccsize = nfc_geo->eccn_chunk_size;
190262306a36Sopenharmony_ci	int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
190362306a36Sopenharmony_ci	u8 *tmp_buf = this->raw_buffer;
190462306a36Sopenharmony_ci	uint8_t *oob = chip->oob_poi;
190562306a36Sopenharmony_ci	size_t dst_bit_off;
190662306a36Sopenharmony_ci	size_t oob_bit_off;
190762306a36Sopenharmony_ci	size_t oob_byte_off;
190862306a36Sopenharmony_ci	int step;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	/*
191162306a36Sopenharmony_ci	 * Initialize all bits to 1 in case we don't have a buffer for the
191262306a36Sopenharmony_ci	 * payload or oob data in order to leave unspecified bits of data
191362306a36Sopenharmony_ci	 * to their initial state.
191462306a36Sopenharmony_ci	 */
191562306a36Sopenharmony_ci	if (!buf || !oob_required)
191662306a36Sopenharmony_ci		memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize);
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	/*
191962306a36Sopenharmony_ci	 * First copy the metadata section (stored in oob buffer) at the
192062306a36Sopenharmony_ci	 * beginning of the page, as imposed by the GPMI layout.
192162306a36Sopenharmony_ci	 */
192262306a36Sopenharmony_ci	memcpy(tmp_buf, oob, nfc_geo->metadata_size);
192362306a36Sopenharmony_ci	oob_bit_off = nfc_geo->metadata_size * 8;
192462306a36Sopenharmony_ci	dst_bit_off = oob_bit_off;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	/* Interleave payload data and ECC bits */
192762306a36Sopenharmony_ci	for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
192862306a36Sopenharmony_ci		if (buf)
192962306a36Sopenharmony_ci			nand_extract_bits(tmp_buf, dst_bit_off, buf,
193062306a36Sopenharmony_ci					  step * eccsize * 8, eccsize * 8);
193162306a36Sopenharmony_ci		dst_bit_off += eccsize * 8;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci		/* Align last ECC block to align a byte boundary */
193462306a36Sopenharmony_ci		if (step == nfc_geo->ecc_chunk_count - 1 &&
193562306a36Sopenharmony_ci		    (oob_bit_off + eccbits) % 8)
193662306a36Sopenharmony_ci			eccbits += 8 - ((oob_bit_off + eccbits) % 8);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci		if (oob_required)
193962306a36Sopenharmony_ci			nand_extract_bits(tmp_buf, dst_bit_off, oob,
194062306a36Sopenharmony_ci					  oob_bit_off, eccbits);
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci		dst_bit_off += eccbits;
194362306a36Sopenharmony_ci		oob_bit_off += eccbits;
194462306a36Sopenharmony_ci	}
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	oob_byte_off = oob_bit_off / 8;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	if (oob_required && oob_byte_off < mtd->oobsize)
194962306a36Sopenharmony_ci		memcpy(tmp_buf + mtd->writesize + oob_byte_off,
195062306a36Sopenharmony_ci		       oob + oob_byte_off, mtd->oobsize - oob_byte_off);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	/*
195362306a36Sopenharmony_ci	 * If required, swap the bad block marker and the first byte of the
195462306a36Sopenharmony_ci	 * metadata section, so that we don't modify the bad block marker.
195562306a36Sopenharmony_ci	 *
195662306a36Sopenharmony_ci	 * See the layout description for a detailed explanation on why this
195762306a36Sopenharmony_ci	 * is needed.
195862306a36Sopenharmony_ci	 */
195962306a36Sopenharmony_ci	if (this->swap_block_mark)
196062306a36Sopenharmony_ci		swap(tmp_buf[0], tmp_buf[mtd->writesize]);
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	return nand_prog_page_op(chip, page, 0, tmp_buf,
196362306a36Sopenharmony_ci				 mtd->writesize + mtd->oobsize);
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cistatic int gpmi_ecc_read_oob_raw(struct nand_chip *chip, int page)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	return gpmi_ecc_read_page_raw(chip, NULL, 1, page);
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic int gpmi_ecc_write_oob_raw(struct nand_chip *chip, int page)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	return gpmi_ecc_write_page_raw(chip, NULL, 1, page);
197462306a36Sopenharmony_ci}
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_cistatic int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs)
197762306a36Sopenharmony_ci{
197862306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
197962306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
198062306a36Sopenharmony_ci	int ret = 0;
198162306a36Sopenharmony_ci	uint8_t *block_mark;
198262306a36Sopenharmony_ci	int column, page, chipnr;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	chipnr = (int)(ofs >> chip->chip_shift);
198562306a36Sopenharmony_ci	nand_select_target(chip, chipnr);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	column = !GPMI_IS_MX23(this) ? mtd->writesize : 0;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	/* Write the block mark. */
199062306a36Sopenharmony_ci	block_mark = this->data_buffer_dma;
199162306a36Sopenharmony_ci	block_mark[0] = 0; /* bad block marker */
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	/* Shift to get page */
199462306a36Sopenharmony_ci	page = (int)(ofs >> chip->page_shift);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	ret = nand_prog_page_op(chip, page, column, block_mark, 1);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	nand_deselect_target(chip);
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	return ret;
200162306a36Sopenharmony_ci}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic int nand_boot_set_geometry(struct gpmi_nand_data *this)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	struct boot_rom_geometry *geometry = &this->rom_geometry;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	/*
200862306a36Sopenharmony_ci	 * Set the boot block stride size.
200962306a36Sopenharmony_ci	 *
201062306a36Sopenharmony_ci	 * In principle, we should be reading this from the OTP bits, since
201162306a36Sopenharmony_ci	 * that's where the ROM is going to get it. In fact, we don't have any
201262306a36Sopenharmony_ci	 * way to read the OTP bits, so we go with the default and hope for the
201362306a36Sopenharmony_ci	 * best.
201462306a36Sopenharmony_ci	 */
201562306a36Sopenharmony_ci	geometry->stride_size_in_pages = 64;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	/*
201862306a36Sopenharmony_ci	 * Set the search area stride exponent.
201962306a36Sopenharmony_ci	 *
202062306a36Sopenharmony_ci	 * In principle, we should be reading this from the OTP bits, since
202162306a36Sopenharmony_ci	 * that's where the ROM is going to get it. In fact, we don't have any
202262306a36Sopenharmony_ci	 * way to read the OTP bits, so we go with the default and hope for the
202362306a36Sopenharmony_ci	 * best.
202462306a36Sopenharmony_ci	 */
202562306a36Sopenharmony_ci	geometry->search_area_stride_exponent = 2;
202662306a36Sopenharmony_ci	return 0;
202762306a36Sopenharmony_ci}
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_cistatic const char  *fingerprint = "STMP";
203062306a36Sopenharmony_cistatic int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
203162306a36Sopenharmony_ci{
203262306a36Sopenharmony_ci	struct boot_rom_geometry *rom_geo = &this->rom_geometry;
203362306a36Sopenharmony_ci	struct device *dev = this->dev;
203462306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
203562306a36Sopenharmony_ci	unsigned int search_area_size_in_strides;
203662306a36Sopenharmony_ci	unsigned int stride;
203762306a36Sopenharmony_ci	unsigned int page;
203862306a36Sopenharmony_ci	u8 *buffer = nand_get_data_buf(chip);
203962306a36Sopenharmony_ci	int found_an_ncb_fingerprint = false;
204062306a36Sopenharmony_ci	int ret;
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	/* Compute the number of strides in a search area. */
204362306a36Sopenharmony_ci	search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	nand_select_target(chip, 0);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	/*
204862306a36Sopenharmony_ci	 * Loop through the first search area, looking for the NCB fingerprint.
204962306a36Sopenharmony_ci	 */
205062306a36Sopenharmony_ci	dev_dbg(dev, "Scanning for an NCB fingerprint...\n");
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	for (stride = 0; stride < search_area_size_in_strides; stride++) {
205362306a36Sopenharmony_ci		/* Compute the page addresses. */
205462306a36Sopenharmony_ci		page = stride * rom_geo->stride_size_in_pages;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci		dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page);
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci		/*
205962306a36Sopenharmony_ci		 * Read the NCB fingerprint. The fingerprint is four bytes long
206062306a36Sopenharmony_ci		 * and starts in the 12th byte of the page.
206162306a36Sopenharmony_ci		 */
206262306a36Sopenharmony_ci		ret = nand_read_page_op(chip, page, 12, buffer,
206362306a36Sopenharmony_ci					strlen(fingerprint));
206462306a36Sopenharmony_ci		if (ret)
206562306a36Sopenharmony_ci			continue;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci		/* Look for the fingerprint. */
206862306a36Sopenharmony_ci		if (!memcmp(buffer, fingerprint, strlen(fingerprint))) {
206962306a36Sopenharmony_ci			found_an_ncb_fingerprint = true;
207062306a36Sopenharmony_ci			break;
207162306a36Sopenharmony_ci		}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	nand_deselect_target(chip);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	if (found_an_ncb_fingerprint)
207862306a36Sopenharmony_ci		dev_dbg(dev, "\tFound a fingerprint\n");
207962306a36Sopenharmony_ci	else
208062306a36Sopenharmony_ci		dev_dbg(dev, "\tNo fingerprint found\n");
208162306a36Sopenharmony_ci	return found_an_ncb_fingerprint;
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci/* Writes a transcription stamp. */
208562306a36Sopenharmony_cistatic int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	struct device *dev = this->dev;
208862306a36Sopenharmony_ci	struct boot_rom_geometry *rom_geo = &this->rom_geometry;
208962306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
209062306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
209162306a36Sopenharmony_ci	unsigned int block_size_in_pages;
209262306a36Sopenharmony_ci	unsigned int search_area_size_in_strides;
209362306a36Sopenharmony_ci	unsigned int search_area_size_in_pages;
209462306a36Sopenharmony_ci	unsigned int search_area_size_in_blocks;
209562306a36Sopenharmony_ci	unsigned int block;
209662306a36Sopenharmony_ci	unsigned int stride;
209762306a36Sopenharmony_ci	unsigned int page;
209862306a36Sopenharmony_ci	u8 *buffer = nand_get_data_buf(chip);
209962306a36Sopenharmony_ci	int status;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	/* Compute the search area geometry. */
210262306a36Sopenharmony_ci	block_size_in_pages = mtd->erasesize / mtd->writesize;
210362306a36Sopenharmony_ci	search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
210462306a36Sopenharmony_ci	search_area_size_in_pages = search_area_size_in_strides *
210562306a36Sopenharmony_ci					rom_geo->stride_size_in_pages;
210662306a36Sopenharmony_ci	search_area_size_in_blocks =
210762306a36Sopenharmony_ci		  (search_area_size_in_pages + (block_size_in_pages - 1)) /
210862306a36Sopenharmony_ci				    block_size_in_pages;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	dev_dbg(dev, "Search Area Geometry :\n");
211162306a36Sopenharmony_ci	dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks);
211262306a36Sopenharmony_ci	dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides);
211362306a36Sopenharmony_ci	dev_dbg(dev, "\tin Pages  : %u\n", search_area_size_in_pages);
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	nand_select_target(chip, 0);
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	/* Loop over blocks in the first search area, erasing them. */
211862306a36Sopenharmony_ci	dev_dbg(dev, "Erasing the search area...\n");
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	for (block = 0; block < search_area_size_in_blocks; block++) {
212162306a36Sopenharmony_ci		/* Erase this block. */
212262306a36Sopenharmony_ci		dev_dbg(dev, "\tErasing block 0x%x\n", block);
212362306a36Sopenharmony_ci		status = nand_erase_op(chip, block);
212462306a36Sopenharmony_ci		if (status)
212562306a36Sopenharmony_ci			dev_err(dev, "[%s] Erase failed.\n", __func__);
212662306a36Sopenharmony_ci	}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	/* Write the NCB fingerprint into the page buffer. */
212962306a36Sopenharmony_ci	memset(buffer, ~0, mtd->writesize);
213062306a36Sopenharmony_ci	memcpy(buffer + 12, fingerprint, strlen(fingerprint));
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	/* Loop through the first search area, writing NCB fingerprints. */
213362306a36Sopenharmony_ci	dev_dbg(dev, "Writing NCB fingerprints...\n");
213462306a36Sopenharmony_ci	for (stride = 0; stride < search_area_size_in_strides; stride++) {
213562306a36Sopenharmony_ci		/* Compute the page addresses. */
213662306a36Sopenharmony_ci		page = stride * rom_geo->stride_size_in_pages;
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci		/* Write the first page of the current stride. */
213962306a36Sopenharmony_ci		dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page);
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci		status = chip->ecc.write_page_raw(chip, buffer, 0, page);
214262306a36Sopenharmony_ci		if (status)
214362306a36Sopenharmony_ci			dev_err(dev, "[%s] Write failed.\n", __func__);
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	nand_deselect_target(chip);
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	return 0;
214962306a36Sopenharmony_ci}
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_cistatic int mx23_boot_init(struct gpmi_nand_data  *this)
215262306a36Sopenharmony_ci{
215362306a36Sopenharmony_ci	struct device *dev = this->dev;
215462306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
215562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
215662306a36Sopenharmony_ci	unsigned int block_count;
215762306a36Sopenharmony_ci	unsigned int block;
215862306a36Sopenharmony_ci	int     chipnr;
215962306a36Sopenharmony_ci	int     page;
216062306a36Sopenharmony_ci	loff_t  byte;
216162306a36Sopenharmony_ci	uint8_t block_mark;
216262306a36Sopenharmony_ci	int     ret = 0;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	/*
216562306a36Sopenharmony_ci	 * If control arrives here, we can't use block mark swapping, which
216662306a36Sopenharmony_ci	 * means we're forced to use transcription. First, scan for the
216762306a36Sopenharmony_ci	 * transcription stamp. If we find it, then we don't have to do
216862306a36Sopenharmony_ci	 * anything -- the block marks are already transcribed.
216962306a36Sopenharmony_ci	 */
217062306a36Sopenharmony_ci	if (mx23_check_transcription_stamp(this))
217162306a36Sopenharmony_ci		return 0;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	/*
217462306a36Sopenharmony_ci	 * If control arrives here, we couldn't find a transcription stamp, so
217562306a36Sopenharmony_ci	 * so we presume the block marks are in the conventional location.
217662306a36Sopenharmony_ci	 */
217762306a36Sopenharmony_ci	dev_dbg(dev, "Transcribing bad block marks...\n");
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	/* Compute the number of blocks in the entire medium. */
218062306a36Sopenharmony_ci	block_count = nanddev_eraseblocks_per_target(&chip->base);
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	/*
218362306a36Sopenharmony_ci	 * Loop over all the blocks in the medium, transcribing block marks as
218462306a36Sopenharmony_ci	 * we go.
218562306a36Sopenharmony_ci	 */
218662306a36Sopenharmony_ci	for (block = 0; block < block_count; block++) {
218762306a36Sopenharmony_ci		/*
218862306a36Sopenharmony_ci		 * Compute the chip, page and byte addresses for this block's
218962306a36Sopenharmony_ci		 * conventional mark.
219062306a36Sopenharmony_ci		 */
219162306a36Sopenharmony_ci		chipnr = block >> (chip->chip_shift - chip->phys_erase_shift);
219262306a36Sopenharmony_ci		page = block << (chip->phys_erase_shift - chip->page_shift);
219362306a36Sopenharmony_ci		byte = block <<  chip->phys_erase_shift;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci		/* Send the command to read the conventional block mark. */
219662306a36Sopenharmony_ci		nand_select_target(chip, chipnr);
219762306a36Sopenharmony_ci		ret = nand_read_page_op(chip, page, mtd->writesize, &block_mark,
219862306a36Sopenharmony_ci					1);
219962306a36Sopenharmony_ci		nand_deselect_target(chip);
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci		if (ret)
220262306a36Sopenharmony_ci			continue;
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci		/*
220562306a36Sopenharmony_ci		 * Check if the block is marked bad. If so, we need to mark it
220662306a36Sopenharmony_ci		 * again, but this time the result will be a mark in the
220762306a36Sopenharmony_ci		 * location where we transcribe block marks.
220862306a36Sopenharmony_ci		 */
220962306a36Sopenharmony_ci		if (block_mark != 0xff) {
221062306a36Sopenharmony_ci			dev_dbg(dev, "Transcribing mark in block %u\n", block);
221162306a36Sopenharmony_ci			ret = chip->legacy.block_markbad(chip, byte);
221262306a36Sopenharmony_ci			if (ret)
221362306a36Sopenharmony_ci				dev_err(dev,
221462306a36Sopenharmony_ci					"Failed to mark block bad with ret %d\n",
221562306a36Sopenharmony_ci					ret);
221662306a36Sopenharmony_ci		}
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	/* Write the stamp that indicates we've transcribed the block marks. */
222062306a36Sopenharmony_ci	mx23_write_transcription_stamp(this);
222162306a36Sopenharmony_ci	return 0;
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cistatic int nand_boot_init(struct gpmi_nand_data  *this)
222562306a36Sopenharmony_ci{
222662306a36Sopenharmony_ci	nand_boot_set_geometry(this);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	/* This is ROM arch-specific initilization before the BBT scanning. */
222962306a36Sopenharmony_ci	if (GPMI_IS_MX23(this))
223062306a36Sopenharmony_ci		return mx23_boot_init(this);
223162306a36Sopenharmony_ci	return 0;
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci
223462306a36Sopenharmony_cistatic int gpmi_set_geometry(struct gpmi_nand_data *this)
223562306a36Sopenharmony_ci{
223662306a36Sopenharmony_ci	int ret;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	/* Free the temporary DMA memory for reading ID. */
223962306a36Sopenharmony_ci	gpmi_free_dma_buffer(this);
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	/* Set up the NFC geometry which is used by BCH. */
224262306a36Sopenharmony_ci	ret = bch_set_geometry(this);
224362306a36Sopenharmony_ci	if (ret) {
224462306a36Sopenharmony_ci		dev_err(this->dev, "Error setting BCH geometry : %d\n", ret);
224562306a36Sopenharmony_ci		return ret;
224662306a36Sopenharmony_ci	}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	/* Alloc the new DMA buffers according to the pagesize and oobsize */
224962306a36Sopenharmony_ci	return gpmi_alloc_dma_buffer(this);
225062306a36Sopenharmony_ci}
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_cistatic int gpmi_init_last(struct gpmi_nand_data *this)
225362306a36Sopenharmony_ci{
225462306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
225562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
225662306a36Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &chip->ecc;
225762306a36Sopenharmony_ci	struct bch_geometry *bch_geo = &this->bch_geometry;
225862306a36Sopenharmony_ci	int ret;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	/* Set up the medium geometry */
226162306a36Sopenharmony_ci	ret = gpmi_set_geometry(this);
226262306a36Sopenharmony_ci	if (ret)
226362306a36Sopenharmony_ci		return ret;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	/* Init the nand_ecc_ctrl{} */
226662306a36Sopenharmony_ci	ecc->read_page	= gpmi_ecc_read_page;
226762306a36Sopenharmony_ci	ecc->write_page	= gpmi_ecc_write_page;
226862306a36Sopenharmony_ci	ecc->read_oob	= gpmi_ecc_read_oob;
226962306a36Sopenharmony_ci	ecc->write_oob	= gpmi_ecc_write_oob;
227062306a36Sopenharmony_ci	ecc->read_page_raw = gpmi_ecc_read_page_raw;
227162306a36Sopenharmony_ci	ecc->write_page_raw = gpmi_ecc_write_page_raw;
227262306a36Sopenharmony_ci	ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
227362306a36Sopenharmony_ci	ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
227462306a36Sopenharmony_ci	ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
227562306a36Sopenharmony_ci	ecc->size	= bch_geo->eccn_chunk_size;
227662306a36Sopenharmony_ci	ecc->strength	= bch_geo->ecc_strength;
227762306a36Sopenharmony_ci	mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops);
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	/*
228062306a36Sopenharmony_ci	 * We only enable the subpage read when:
228162306a36Sopenharmony_ci	 *  (1) the chip is imx6, and
228262306a36Sopenharmony_ci	 *  (2) the size of the ECC parity is byte aligned.
228362306a36Sopenharmony_ci	 */
228462306a36Sopenharmony_ci	if (GPMI_IS_MX6(this) &&
228562306a36Sopenharmony_ci		((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
228662306a36Sopenharmony_ci		ecc->read_subpage = gpmi_ecc_read_subpage;
228762306a36Sopenharmony_ci		chip->options |= NAND_SUBPAGE_READ;
228862306a36Sopenharmony_ci	}
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	return 0;
229162306a36Sopenharmony_ci}
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_cistatic int gpmi_nand_attach_chip(struct nand_chip *chip)
229462306a36Sopenharmony_ci{
229562306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
229662306a36Sopenharmony_ci	int ret;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	if (chip->bbt_options & NAND_BBT_USE_FLASH) {
229962306a36Sopenharmony_ci		chip->bbt_options |= NAND_BBT_NO_OOB;
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci		if (of_property_read_bool(this->dev->of_node,
230262306a36Sopenharmony_ci					  "fsl,no-blockmark-swap"))
230362306a36Sopenharmony_ci			this->swap_block_mark = false;
230462306a36Sopenharmony_ci	}
230562306a36Sopenharmony_ci	dev_dbg(this->dev, "Blockmark swapping %sabled\n",
230662306a36Sopenharmony_ci		this->swap_block_mark ? "en" : "dis");
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	ret = gpmi_init_last(this);
230962306a36Sopenharmony_ci	if (ret)
231062306a36Sopenharmony_ci		return ret;
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	chip->options |= NAND_SKIP_BBTSCAN;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	return 0;
231562306a36Sopenharmony_ci}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_cistatic struct gpmi_transfer *get_next_transfer(struct gpmi_nand_data *this)
231862306a36Sopenharmony_ci{
231962306a36Sopenharmony_ci	struct gpmi_transfer *transfer = &this->transfers[this->ntransfers];
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	this->ntransfers++;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	if (this->ntransfers == GPMI_MAX_TRANSFERS)
232462306a36Sopenharmony_ci		return NULL;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	return transfer;
232762306a36Sopenharmony_ci}
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_command(
233062306a36Sopenharmony_ci	struct gpmi_nand_data *this, u8 cmd, const u8 *addr, int naddr)
233162306a36Sopenharmony_ci{
233262306a36Sopenharmony_ci	struct dma_chan *channel = get_dma_chan(this);
233362306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
233462306a36Sopenharmony_ci	struct gpmi_transfer *transfer;
233562306a36Sopenharmony_ci	int chip = this->nand.cur_cs;
233662306a36Sopenharmony_ci	u32 pio[3];
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci	/* [1] send out the PIO words */
233962306a36Sopenharmony_ci	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
234062306a36Sopenharmony_ci		| BM_GPMI_CTRL0_WORD_LENGTH
234162306a36Sopenharmony_ci		| BF_GPMI_CTRL0_CS(chip, this)
234262306a36Sopenharmony_ci		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
234362306a36Sopenharmony_ci		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE)
234462306a36Sopenharmony_ci		| BM_GPMI_CTRL0_ADDRESS_INCREMENT
234562306a36Sopenharmony_ci		| BF_GPMI_CTRL0_XFER_COUNT(naddr + 1);
234662306a36Sopenharmony_ci	pio[1] = 0;
234762306a36Sopenharmony_ci	pio[2] = 0;
234862306a36Sopenharmony_ci	desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio),
234962306a36Sopenharmony_ci				      DMA_TRANS_NONE, 0);
235062306a36Sopenharmony_ci	if (!desc)
235162306a36Sopenharmony_ci		return NULL;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	transfer = get_next_transfer(this);
235462306a36Sopenharmony_ci	if (!transfer)
235562306a36Sopenharmony_ci		return NULL;
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	transfer->cmdbuf[0] = cmd;
235862306a36Sopenharmony_ci	if (naddr)
235962306a36Sopenharmony_ci		memcpy(&transfer->cmdbuf[1], addr, naddr);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	sg_init_one(&transfer->sgl, transfer->cmdbuf, naddr + 1);
236262306a36Sopenharmony_ci	dma_map_sg(this->dev, &transfer->sgl, 1, DMA_TO_DEVICE);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	transfer->direction = DMA_TO_DEVICE;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci	desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, DMA_MEM_TO_DEV,
236762306a36Sopenharmony_ci				       MXS_DMA_CTRL_WAIT4END);
236862306a36Sopenharmony_ci	return desc;
236962306a36Sopenharmony_ci}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_wait_ready(
237262306a36Sopenharmony_ci	struct gpmi_nand_data *this)
237362306a36Sopenharmony_ci{
237462306a36Sopenharmony_ci	struct dma_chan *channel = get_dma_chan(this);
237562306a36Sopenharmony_ci	u32 pio[2];
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	pio[0] =  BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY)
237862306a36Sopenharmony_ci		| BM_GPMI_CTRL0_WORD_LENGTH
237962306a36Sopenharmony_ci		| BF_GPMI_CTRL0_CS(this->nand.cur_cs, this)
238062306a36Sopenharmony_ci		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
238162306a36Sopenharmony_ci		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
238262306a36Sopenharmony_ci		| BF_GPMI_CTRL0_XFER_COUNT(0);
238362306a36Sopenharmony_ci	pio[1] = 0;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	return mxs_dmaengine_prep_pio(channel, pio, 2, DMA_TRANS_NONE,
238662306a36Sopenharmony_ci				MXS_DMA_CTRL_WAIT4END | MXS_DMA_CTRL_WAIT4RDY);
238762306a36Sopenharmony_ci}
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_data_read(
239062306a36Sopenharmony_ci	struct gpmi_nand_data *this, void *buf, int raw_len, bool *direct)
239162306a36Sopenharmony_ci{
239262306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
239362306a36Sopenharmony_ci	struct dma_chan *channel = get_dma_chan(this);
239462306a36Sopenharmony_ci	struct gpmi_transfer *transfer;
239562306a36Sopenharmony_ci	u32 pio[6] = {};
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	transfer = get_next_transfer(this);
239862306a36Sopenharmony_ci	if (!transfer)
239962306a36Sopenharmony_ci		return NULL;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	transfer->direction = DMA_FROM_DEVICE;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	*direct = prepare_data_dma(this, buf, raw_len, &transfer->sgl,
240462306a36Sopenharmony_ci				   DMA_FROM_DEVICE);
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	pio[0] =  BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ)
240762306a36Sopenharmony_ci		| BM_GPMI_CTRL0_WORD_LENGTH
240862306a36Sopenharmony_ci		| BF_GPMI_CTRL0_CS(this->nand.cur_cs, this)
240962306a36Sopenharmony_ci		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
241062306a36Sopenharmony_ci		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
241162306a36Sopenharmony_ci		| BF_GPMI_CTRL0_XFER_COUNT(raw_len);
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	if (this->bch) {
241462306a36Sopenharmony_ci		pio[2] =  BM_GPMI_ECCCTRL_ENABLE_ECC
241562306a36Sopenharmony_ci			| BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE)
241662306a36Sopenharmony_ci			| BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE
241762306a36Sopenharmony_ci				| BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY);
241862306a36Sopenharmony_ci		pio[3] = raw_len;
241962306a36Sopenharmony_ci		pio[4] = transfer->sgl.dma_address;
242062306a36Sopenharmony_ci		pio[5] = this->auxiliary_phys;
242162306a36Sopenharmony_ci	}
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio),
242462306a36Sopenharmony_ci				      DMA_TRANS_NONE, 0);
242562306a36Sopenharmony_ci	if (!desc)
242662306a36Sopenharmony_ci		return NULL;
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	if (!this->bch)
242962306a36Sopenharmony_ci		desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1,
243062306a36Sopenharmony_ci					     DMA_DEV_TO_MEM,
243162306a36Sopenharmony_ci					     MXS_DMA_CTRL_WAIT4END);
243262306a36Sopenharmony_ci
243362306a36Sopenharmony_ci	return desc;
243462306a36Sopenharmony_ci}
243562306a36Sopenharmony_ci
243662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_data_write(
243762306a36Sopenharmony_ci	struct gpmi_nand_data *this, const void *buf, int raw_len)
243862306a36Sopenharmony_ci{
243962306a36Sopenharmony_ci	struct dma_chan *channel = get_dma_chan(this);
244062306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
244162306a36Sopenharmony_ci	struct gpmi_transfer *transfer;
244262306a36Sopenharmony_ci	u32 pio[6] = {};
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	transfer = get_next_transfer(this);
244562306a36Sopenharmony_ci	if (!transfer)
244662306a36Sopenharmony_ci		return NULL;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	transfer->direction = DMA_TO_DEVICE;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	prepare_data_dma(this, buf, raw_len, &transfer->sgl, DMA_TO_DEVICE);
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE)
245362306a36Sopenharmony_ci		| BM_GPMI_CTRL0_WORD_LENGTH
245462306a36Sopenharmony_ci		| BF_GPMI_CTRL0_CS(this->nand.cur_cs, this)
245562306a36Sopenharmony_ci		| BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this)
245662306a36Sopenharmony_ci		| BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA)
245762306a36Sopenharmony_ci		| BF_GPMI_CTRL0_XFER_COUNT(raw_len);
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	if (this->bch) {
246062306a36Sopenharmony_ci		pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC
246162306a36Sopenharmony_ci			| BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE)
246262306a36Sopenharmony_ci			| BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
246362306a36Sopenharmony_ci					BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY);
246462306a36Sopenharmony_ci		pio[3] = raw_len;
246562306a36Sopenharmony_ci		pio[4] = transfer->sgl.dma_address;
246662306a36Sopenharmony_ci		pio[5] = this->auxiliary_phys;
246762306a36Sopenharmony_ci	}
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio),
247062306a36Sopenharmony_ci				      DMA_TRANS_NONE,
247162306a36Sopenharmony_ci				      (this->bch ? MXS_DMA_CTRL_WAIT4END : 0));
247262306a36Sopenharmony_ci	if (!desc)
247362306a36Sopenharmony_ci		return NULL;
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci	if (!this->bch)
247662306a36Sopenharmony_ci		desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1,
247762306a36Sopenharmony_ci					       DMA_MEM_TO_DEV,
247862306a36Sopenharmony_ci					       MXS_DMA_CTRL_WAIT4END);
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	return desc;
248162306a36Sopenharmony_ci}
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_cistatic int gpmi_nfc_exec_op(struct nand_chip *chip,
248462306a36Sopenharmony_ci			     const struct nand_operation *op,
248562306a36Sopenharmony_ci			     bool check_only)
248662306a36Sopenharmony_ci{
248762306a36Sopenharmony_ci	const struct nand_op_instr *instr;
248862306a36Sopenharmony_ci	struct gpmi_nand_data *this = nand_get_controller_data(chip);
248962306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc = NULL;
249062306a36Sopenharmony_ci	int i, ret, buf_len = 0, nbufs = 0;
249162306a36Sopenharmony_ci	u8 cmd = 0;
249262306a36Sopenharmony_ci	void *buf_read = NULL;
249362306a36Sopenharmony_ci	const void *buf_write = NULL;
249462306a36Sopenharmony_ci	bool direct = false;
249562306a36Sopenharmony_ci	struct completion *dma_completion, *bch_completion;
249662306a36Sopenharmony_ci	unsigned long to;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	if (check_only)
249962306a36Sopenharmony_ci		return 0;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	this->ntransfers = 0;
250262306a36Sopenharmony_ci	for (i = 0; i < GPMI_MAX_TRANSFERS; i++)
250362306a36Sopenharmony_ci		this->transfers[i].direction = DMA_NONE;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(this->dev);
250662306a36Sopenharmony_ci	if (ret < 0)
250762306a36Sopenharmony_ci		return ret;
250862306a36Sopenharmony_ci
250962306a36Sopenharmony_ci	/*
251062306a36Sopenharmony_ci	 * This driver currently supports only one NAND chip. Plus, dies share
251162306a36Sopenharmony_ci	 * the same configuration. So once timings have been applied on the
251262306a36Sopenharmony_ci	 * controller side, they will not change anymore. When the time will
251362306a36Sopenharmony_ci	 * come, the check on must_apply_timings will have to be dropped.
251462306a36Sopenharmony_ci	 */
251562306a36Sopenharmony_ci	if (this->hw.must_apply_timings) {
251662306a36Sopenharmony_ci		this->hw.must_apply_timings = false;
251762306a36Sopenharmony_ci		ret = gpmi_nfc_apply_timings(this);
251862306a36Sopenharmony_ci		if (ret)
251962306a36Sopenharmony_ci			goto out_pm;
252062306a36Sopenharmony_ci	}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs);
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	for (i = 0; i < op->ninstrs; i++) {
252562306a36Sopenharmony_ci		instr = &op->instrs[i];
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci		nand_op_trace("  ", instr);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci		switch (instr->type) {
253062306a36Sopenharmony_ci		case NAND_OP_WAITRDY_INSTR:
253162306a36Sopenharmony_ci			desc = gpmi_chain_wait_ready(this);
253262306a36Sopenharmony_ci			break;
253362306a36Sopenharmony_ci		case NAND_OP_CMD_INSTR:
253462306a36Sopenharmony_ci			cmd = instr->ctx.cmd.opcode;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci			/*
253762306a36Sopenharmony_ci			 * When this command has an address cycle chain it
253862306a36Sopenharmony_ci			 * together with the address cycle
253962306a36Sopenharmony_ci			 */
254062306a36Sopenharmony_ci			if (i + 1 != op->ninstrs &&
254162306a36Sopenharmony_ci			    op->instrs[i + 1].type == NAND_OP_ADDR_INSTR)
254262306a36Sopenharmony_ci				continue;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci			desc = gpmi_chain_command(this, cmd, NULL, 0);
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci			break;
254762306a36Sopenharmony_ci		case NAND_OP_ADDR_INSTR:
254862306a36Sopenharmony_ci			desc = gpmi_chain_command(this, cmd, instr->ctx.addr.addrs,
254962306a36Sopenharmony_ci						  instr->ctx.addr.naddrs);
255062306a36Sopenharmony_ci			break;
255162306a36Sopenharmony_ci		case NAND_OP_DATA_OUT_INSTR:
255262306a36Sopenharmony_ci			buf_write = instr->ctx.data.buf.out;
255362306a36Sopenharmony_ci			buf_len = instr->ctx.data.len;
255462306a36Sopenharmony_ci			nbufs++;
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci			desc = gpmi_chain_data_write(this, buf_write, buf_len);
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci			break;
255962306a36Sopenharmony_ci		case NAND_OP_DATA_IN_INSTR:
256062306a36Sopenharmony_ci			if (!instr->ctx.data.len)
256162306a36Sopenharmony_ci				break;
256262306a36Sopenharmony_ci			buf_read = instr->ctx.data.buf.in;
256362306a36Sopenharmony_ci			buf_len = instr->ctx.data.len;
256462306a36Sopenharmony_ci			nbufs++;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci			desc = gpmi_chain_data_read(this, buf_read, buf_len,
256762306a36Sopenharmony_ci						   &direct);
256862306a36Sopenharmony_ci			break;
256962306a36Sopenharmony_ci		}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci		if (!desc) {
257262306a36Sopenharmony_ci			ret = -ENXIO;
257362306a36Sopenharmony_ci			goto unmap;
257462306a36Sopenharmony_ci		}
257562306a36Sopenharmony_ci	}
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	dev_dbg(this->dev, "%s setup done\n", __func__);
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci	if (nbufs > 1) {
258062306a36Sopenharmony_ci		dev_err(this->dev, "Multiple data instructions not supported\n");
258162306a36Sopenharmony_ci		ret = -EINVAL;
258262306a36Sopenharmony_ci		goto unmap;
258362306a36Sopenharmony_ci	}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	if (this->bch) {
258662306a36Sopenharmony_ci		writel(this->bch_flashlayout0,
258762306a36Sopenharmony_ci		       this->resources.bch_regs + HW_BCH_FLASH0LAYOUT0);
258862306a36Sopenharmony_ci		writel(this->bch_flashlayout1,
258962306a36Sopenharmony_ci		       this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1);
259062306a36Sopenharmony_ci	}
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	desc->callback = dma_irq_callback;
259362306a36Sopenharmony_ci	desc->callback_param = this;
259462306a36Sopenharmony_ci	dma_completion = &this->dma_done;
259562306a36Sopenharmony_ci	bch_completion = NULL;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	init_completion(dma_completion);
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	if (this->bch && buf_read) {
260062306a36Sopenharmony_ci		writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
260162306a36Sopenharmony_ci		       this->resources.bch_regs + HW_BCH_CTRL_SET);
260262306a36Sopenharmony_ci		bch_completion = &this->bch_done;
260362306a36Sopenharmony_ci		init_completion(bch_completion);
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	dmaengine_submit(desc);
260762306a36Sopenharmony_ci	dma_async_issue_pending(get_dma_chan(this));
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci	to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000));
261062306a36Sopenharmony_ci	if (!to) {
261162306a36Sopenharmony_ci		dev_err(this->dev, "DMA timeout, last DMA\n");
261262306a36Sopenharmony_ci		gpmi_dump_info(this);
261362306a36Sopenharmony_ci		ret = -ETIMEDOUT;
261462306a36Sopenharmony_ci		goto unmap;
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	if (this->bch && buf_read) {
261862306a36Sopenharmony_ci		to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000));
261962306a36Sopenharmony_ci		if (!to) {
262062306a36Sopenharmony_ci			dev_err(this->dev, "BCH timeout, last DMA\n");
262162306a36Sopenharmony_ci			gpmi_dump_info(this);
262262306a36Sopenharmony_ci			ret = -ETIMEDOUT;
262362306a36Sopenharmony_ci			goto unmap;
262462306a36Sopenharmony_ci		}
262562306a36Sopenharmony_ci	}
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	writel(BM_BCH_CTRL_COMPLETE_IRQ_EN,
262862306a36Sopenharmony_ci	       this->resources.bch_regs + HW_BCH_CTRL_CLR);
262962306a36Sopenharmony_ci	gpmi_clear_bch(this);
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci	ret = 0;
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ciunmap:
263462306a36Sopenharmony_ci	for (i = 0; i < this->ntransfers; i++) {
263562306a36Sopenharmony_ci		struct gpmi_transfer *transfer = &this->transfers[i];
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci		if (transfer->direction != DMA_NONE)
263862306a36Sopenharmony_ci			dma_unmap_sg(this->dev, &transfer->sgl, 1,
263962306a36Sopenharmony_ci				     transfer->direction);
264062306a36Sopenharmony_ci	}
264162306a36Sopenharmony_ci
264262306a36Sopenharmony_ci	if (!ret && buf_read && !direct)
264362306a36Sopenharmony_ci		memcpy(buf_read, this->data_buffer_dma,
264462306a36Sopenharmony_ci		       gpmi_raw_len_to_len(this, buf_len));
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	this->bch = false;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ciout_pm:
264962306a36Sopenharmony_ci	pm_runtime_mark_last_busy(this->dev);
265062306a36Sopenharmony_ci	pm_runtime_put_autosuspend(this->dev);
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	return ret;
265362306a36Sopenharmony_ci}
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_cistatic const struct nand_controller_ops gpmi_nand_controller_ops = {
265662306a36Sopenharmony_ci	.attach_chip = gpmi_nand_attach_chip,
265762306a36Sopenharmony_ci	.setup_interface = gpmi_setup_interface,
265862306a36Sopenharmony_ci	.exec_op = gpmi_nfc_exec_op,
265962306a36Sopenharmony_ci};
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_cistatic int gpmi_nand_init(struct gpmi_nand_data *this)
266262306a36Sopenharmony_ci{
266362306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
266462306a36Sopenharmony_ci	struct mtd_info  *mtd = nand_to_mtd(chip);
266562306a36Sopenharmony_ci	int ret;
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	/* init the MTD data structures */
266862306a36Sopenharmony_ci	mtd->name		= "gpmi-nand";
266962306a36Sopenharmony_ci	mtd->dev.parent		= this->dev;
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	/* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
267262306a36Sopenharmony_ci	nand_set_controller_data(chip, this);
267362306a36Sopenharmony_ci	nand_set_flash_node(chip, this->pdev->dev.of_node);
267462306a36Sopenharmony_ci	chip->legacy.block_markbad = gpmi_block_markbad;
267562306a36Sopenharmony_ci	chip->badblock_pattern	= &gpmi_bbt_descr;
267662306a36Sopenharmony_ci	chip->options		|= NAND_NO_SUBPAGE_WRITE;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	/* Set up swap_block_mark, must be set before the gpmi_set_geometry() */
267962306a36Sopenharmony_ci	this->swap_block_mark = !GPMI_IS_MX23(this);
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	/*
268262306a36Sopenharmony_ci	 * Allocate a temporary DMA buffer for reading ID in the
268362306a36Sopenharmony_ci	 * nand_scan_ident().
268462306a36Sopenharmony_ci	 */
268562306a36Sopenharmony_ci	this->bch_geometry.payload_size = 1024;
268662306a36Sopenharmony_ci	this->bch_geometry.auxiliary_size = 128;
268762306a36Sopenharmony_ci	ret = gpmi_alloc_dma_buffer(this);
268862306a36Sopenharmony_ci	if (ret)
268962306a36Sopenharmony_ci		return ret;
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	nand_controller_init(&this->base);
269262306a36Sopenharmony_ci	this->base.ops = &gpmi_nand_controller_ops;
269362306a36Sopenharmony_ci	chip->controller = &this->base;
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1);
269662306a36Sopenharmony_ci	if (ret)
269762306a36Sopenharmony_ci		goto err_out;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	ret = nand_boot_init(this);
270062306a36Sopenharmony_ci	if (ret)
270162306a36Sopenharmony_ci		goto err_nand_cleanup;
270262306a36Sopenharmony_ci	ret = nand_create_bbt(chip);
270362306a36Sopenharmony_ci	if (ret)
270462306a36Sopenharmony_ci		goto err_nand_cleanup;
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci	ret = mtd_device_register(mtd, NULL, 0);
270762306a36Sopenharmony_ci	if (ret)
270862306a36Sopenharmony_ci		goto err_nand_cleanup;
270962306a36Sopenharmony_ci	return 0;
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_cierr_nand_cleanup:
271262306a36Sopenharmony_ci	nand_cleanup(chip);
271362306a36Sopenharmony_cierr_out:
271462306a36Sopenharmony_ci	gpmi_free_dma_buffer(this);
271562306a36Sopenharmony_ci	return ret;
271662306a36Sopenharmony_ci}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_cistatic const struct of_device_id gpmi_nand_id_table[] = {
271962306a36Sopenharmony_ci	{ .compatible = "fsl,imx23-gpmi-nand", .data = &gpmi_devdata_imx23, },
272062306a36Sopenharmony_ci	{ .compatible = "fsl,imx28-gpmi-nand", .data = &gpmi_devdata_imx28, },
272162306a36Sopenharmony_ci	{ .compatible = "fsl,imx6q-gpmi-nand", .data = &gpmi_devdata_imx6q, },
272262306a36Sopenharmony_ci	{ .compatible = "fsl,imx6sx-gpmi-nand", .data = &gpmi_devdata_imx6sx, },
272362306a36Sopenharmony_ci	{ .compatible = "fsl,imx7d-gpmi-nand", .data = &gpmi_devdata_imx7d,},
272462306a36Sopenharmony_ci	{}
272562306a36Sopenharmony_ci};
272662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpmi_nand_id_table);
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_cistatic int gpmi_nand_probe(struct platform_device *pdev)
272962306a36Sopenharmony_ci{
273062306a36Sopenharmony_ci	struct gpmi_nand_data *this;
273162306a36Sopenharmony_ci	int ret;
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci	this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL);
273462306a36Sopenharmony_ci	if (!this)
273562306a36Sopenharmony_ci		return -ENOMEM;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	this->devdata = of_device_get_match_data(&pdev->dev);
273862306a36Sopenharmony_ci	platform_set_drvdata(pdev, this);
273962306a36Sopenharmony_ci	this->pdev  = pdev;
274062306a36Sopenharmony_ci	this->dev   = &pdev->dev;
274162306a36Sopenharmony_ci
274262306a36Sopenharmony_ci	ret = acquire_resources(this);
274362306a36Sopenharmony_ci	if (ret)
274462306a36Sopenharmony_ci		goto exit_acquire_resources;
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	ret = __gpmi_enable_clk(this, true);
274762306a36Sopenharmony_ci	if (ret)
274862306a36Sopenharmony_ci		goto exit_acquire_resources;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
275162306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
275262306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
275362306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
275462306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	ret = gpmi_init(this);
275762306a36Sopenharmony_ci	if (ret)
275862306a36Sopenharmony_ci		goto exit_nfc_init;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	ret = gpmi_nand_init(this);
276162306a36Sopenharmony_ci	if (ret)
276262306a36Sopenharmony_ci		goto exit_nfc_init;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
276562306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	dev_info(this->dev, "driver registered.\n");
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	return 0;
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_ciexit_nfc_init:
277262306a36Sopenharmony_ci	pm_runtime_put(&pdev->dev);
277362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
277462306a36Sopenharmony_ci	release_resources(this);
277562306a36Sopenharmony_ciexit_acquire_resources:
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	return ret;
277862306a36Sopenharmony_ci}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_cistatic void gpmi_nand_remove(struct platform_device *pdev)
278162306a36Sopenharmony_ci{
278262306a36Sopenharmony_ci	struct gpmi_nand_data *this = platform_get_drvdata(pdev);
278362306a36Sopenharmony_ci	struct nand_chip *chip = &this->nand;
278462306a36Sopenharmony_ci	int ret;
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci	pm_runtime_put_sync(&pdev->dev);
278762306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
278862306a36Sopenharmony_ci
278962306a36Sopenharmony_ci	ret = mtd_device_unregister(nand_to_mtd(chip));
279062306a36Sopenharmony_ci	WARN_ON(ret);
279162306a36Sopenharmony_ci	nand_cleanup(chip);
279262306a36Sopenharmony_ci	gpmi_free_dma_buffer(this);
279362306a36Sopenharmony_ci	release_resources(this);
279462306a36Sopenharmony_ci}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
279762306a36Sopenharmony_cistatic int gpmi_pm_suspend(struct device *dev)
279862306a36Sopenharmony_ci{
279962306a36Sopenharmony_ci	struct gpmi_nand_data *this = dev_get_drvdata(dev);
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	release_dma_channels(this);
280262306a36Sopenharmony_ci	return 0;
280362306a36Sopenharmony_ci}
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_cistatic int gpmi_pm_resume(struct device *dev)
280662306a36Sopenharmony_ci{
280762306a36Sopenharmony_ci	struct gpmi_nand_data *this = dev_get_drvdata(dev);
280862306a36Sopenharmony_ci	int ret;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	ret = acquire_dma_channels(this);
281162306a36Sopenharmony_ci	if (ret < 0)
281262306a36Sopenharmony_ci		return ret;
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	/* re-init the GPMI registers */
281562306a36Sopenharmony_ci	ret = gpmi_init(this);
281662306a36Sopenharmony_ci	if (ret) {
281762306a36Sopenharmony_ci		dev_err(this->dev, "Error setting GPMI : %d\n", ret);
281862306a36Sopenharmony_ci		return ret;
281962306a36Sopenharmony_ci	}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci	/* Set flag to get timing setup restored for next exec_op */
282262306a36Sopenharmony_ci	if (this->hw.clk_rate)
282362306a36Sopenharmony_ci		this->hw.must_apply_timings = true;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	/* re-init the BCH registers */
282662306a36Sopenharmony_ci	ret = bch_set_geometry(this);
282762306a36Sopenharmony_ci	if (ret) {
282862306a36Sopenharmony_ci		dev_err(this->dev, "Error setting BCH : %d\n", ret);
282962306a36Sopenharmony_ci		return ret;
283062306a36Sopenharmony_ci	}
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	return 0;
283362306a36Sopenharmony_ci}
283462306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_cistatic int __maybe_unused gpmi_runtime_suspend(struct device *dev)
283762306a36Sopenharmony_ci{
283862306a36Sopenharmony_ci	struct gpmi_nand_data *this = dev_get_drvdata(dev);
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	return __gpmi_enable_clk(this, false);
284162306a36Sopenharmony_ci}
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_cistatic int __maybe_unused gpmi_runtime_resume(struct device *dev)
284462306a36Sopenharmony_ci{
284562306a36Sopenharmony_ci	struct gpmi_nand_data *this = dev_get_drvdata(dev);
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	return __gpmi_enable_clk(this, true);
284862306a36Sopenharmony_ci}
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_cistatic const struct dev_pm_ops gpmi_pm_ops = {
285162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume)
285262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(gpmi_runtime_suspend, gpmi_runtime_resume, NULL)
285362306a36Sopenharmony_ci};
285462306a36Sopenharmony_ci
285562306a36Sopenharmony_cistatic struct platform_driver gpmi_nand_driver = {
285662306a36Sopenharmony_ci	.driver = {
285762306a36Sopenharmony_ci		.name = "gpmi-nand",
285862306a36Sopenharmony_ci		.pm = &gpmi_pm_ops,
285962306a36Sopenharmony_ci		.of_match_table = gpmi_nand_id_table,
286062306a36Sopenharmony_ci	},
286162306a36Sopenharmony_ci	.probe   = gpmi_nand_probe,
286262306a36Sopenharmony_ci	.remove_new = gpmi_nand_remove,
286362306a36Sopenharmony_ci};
286462306a36Sopenharmony_cimodule_platform_driver(gpmi_nand_driver);
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc.");
286762306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver");
286862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2869