162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
462306a36Sopenharmony_ci * Copyright © 2004 Micron Technology Inc.
562306a36Sopenharmony_ci * Copyright © 2004 David Brownell
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci#include <linux/dmaengine.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/jiffies.h>
1662306a36Sopenharmony_ci#include <linux/sched.h>
1762306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1862306a36Sopenharmony_ci#include <linux/mtd/nand-ecc-sw-bch.h>
1962306a36Sopenharmony_ci#include <linux/mtd/rawnand.h>
2062306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
2162306a36Sopenharmony_ci#include <linux/omap-dma.h>
2262306a36Sopenharmony_ci#include <linux/iopoll.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/of.h>
2562306a36Sopenharmony_ci#include <linux/of_platform.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <linux/platform_data/elm.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/omap-gpmc.h>
3062306a36Sopenharmony_ci#include <linux/platform_data/mtd-nand-omap2.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define	DRIVER_NAME	"omap2-nand"
3362306a36Sopenharmony_ci#define	OMAP_NAND_TIMEOUT_MS	5000
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define NAND_Ecc_P1e		(1 << 0)
3662306a36Sopenharmony_ci#define NAND_Ecc_P2e		(1 << 1)
3762306a36Sopenharmony_ci#define NAND_Ecc_P4e		(1 << 2)
3862306a36Sopenharmony_ci#define NAND_Ecc_P8e		(1 << 3)
3962306a36Sopenharmony_ci#define NAND_Ecc_P16e		(1 << 4)
4062306a36Sopenharmony_ci#define NAND_Ecc_P32e		(1 << 5)
4162306a36Sopenharmony_ci#define NAND_Ecc_P64e		(1 << 6)
4262306a36Sopenharmony_ci#define NAND_Ecc_P128e		(1 << 7)
4362306a36Sopenharmony_ci#define NAND_Ecc_P256e		(1 << 8)
4462306a36Sopenharmony_ci#define NAND_Ecc_P512e		(1 << 9)
4562306a36Sopenharmony_ci#define NAND_Ecc_P1024e		(1 << 10)
4662306a36Sopenharmony_ci#define NAND_Ecc_P2048e		(1 << 11)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define NAND_Ecc_P1o		(1 << 16)
4962306a36Sopenharmony_ci#define NAND_Ecc_P2o		(1 << 17)
5062306a36Sopenharmony_ci#define NAND_Ecc_P4o		(1 << 18)
5162306a36Sopenharmony_ci#define NAND_Ecc_P8o		(1 << 19)
5262306a36Sopenharmony_ci#define NAND_Ecc_P16o		(1 << 20)
5362306a36Sopenharmony_ci#define NAND_Ecc_P32o		(1 << 21)
5462306a36Sopenharmony_ci#define NAND_Ecc_P64o		(1 << 22)
5562306a36Sopenharmony_ci#define NAND_Ecc_P128o		(1 << 23)
5662306a36Sopenharmony_ci#define NAND_Ecc_P256o		(1 << 24)
5762306a36Sopenharmony_ci#define NAND_Ecc_P512o		(1 << 25)
5862306a36Sopenharmony_ci#define NAND_Ecc_P1024o		(1 << 26)
5962306a36Sopenharmony_ci#define NAND_Ecc_P2048o		(1 << 27)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define TF(value)	(value ? 1 : 0)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define P2048e(a)	(TF(a & NAND_Ecc_P2048e)	<< 0)
6462306a36Sopenharmony_ci#define P2048o(a)	(TF(a & NAND_Ecc_P2048o)	<< 1)
6562306a36Sopenharmony_ci#define P1e(a)		(TF(a & NAND_Ecc_P1e)		<< 2)
6662306a36Sopenharmony_ci#define P1o(a)		(TF(a & NAND_Ecc_P1o)		<< 3)
6762306a36Sopenharmony_ci#define P2e(a)		(TF(a & NAND_Ecc_P2e)		<< 4)
6862306a36Sopenharmony_ci#define P2o(a)		(TF(a & NAND_Ecc_P2o)		<< 5)
6962306a36Sopenharmony_ci#define P4e(a)		(TF(a & NAND_Ecc_P4e)		<< 6)
7062306a36Sopenharmony_ci#define P4o(a)		(TF(a & NAND_Ecc_P4o)		<< 7)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define P8e(a)		(TF(a & NAND_Ecc_P8e)		<< 0)
7362306a36Sopenharmony_ci#define P8o(a)		(TF(a & NAND_Ecc_P8o)		<< 1)
7462306a36Sopenharmony_ci#define P16e(a)		(TF(a & NAND_Ecc_P16e)		<< 2)
7562306a36Sopenharmony_ci#define P16o(a)		(TF(a & NAND_Ecc_P16o)		<< 3)
7662306a36Sopenharmony_ci#define P32e(a)		(TF(a & NAND_Ecc_P32e)		<< 4)
7762306a36Sopenharmony_ci#define P32o(a)		(TF(a & NAND_Ecc_P32o)		<< 5)
7862306a36Sopenharmony_ci#define P64e(a)		(TF(a & NAND_Ecc_P64e)		<< 6)
7962306a36Sopenharmony_ci#define P64o(a)		(TF(a & NAND_Ecc_P64o)		<< 7)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define P128e(a)	(TF(a & NAND_Ecc_P128e)		<< 0)
8262306a36Sopenharmony_ci#define P128o(a)	(TF(a & NAND_Ecc_P128o)		<< 1)
8362306a36Sopenharmony_ci#define P256e(a)	(TF(a & NAND_Ecc_P256e)		<< 2)
8462306a36Sopenharmony_ci#define P256o(a)	(TF(a & NAND_Ecc_P256o)		<< 3)
8562306a36Sopenharmony_ci#define P512e(a)	(TF(a & NAND_Ecc_P512e)		<< 4)
8662306a36Sopenharmony_ci#define P512o(a)	(TF(a & NAND_Ecc_P512o)		<< 5)
8762306a36Sopenharmony_ci#define P1024e(a)	(TF(a & NAND_Ecc_P1024e)	<< 6)
8862306a36Sopenharmony_ci#define P1024o(a)	(TF(a & NAND_Ecc_P1024o)	<< 7)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define P8e_s(a)	(TF(a & NAND_Ecc_P8e)		<< 0)
9162306a36Sopenharmony_ci#define P8o_s(a)	(TF(a & NAND_Ecc_P8o)		<< 1)
9262306a36Sopenharmony_ci#define P16e_s(a)	(TF(a & NAND_Ecc_P16e)		<< 2)
9362306a36Sopenharmony_ci#define P16o_s(a)	(TF(a & NAND_Ecc_P16o)		<< 3)
9462306a36Sopenharmony_ci#define P1e_s(a)	(TF(a & NAND_Ecc_P1e)		<< 4)
9562306a36Sopenharmony_ci#define P1o_s(a)	(TF(a & NAND_Ecc_P1o)		<< 5)
9662306a36Sopenharmony_ci#define P2e_s(a)	(TF(a & NAND_Ecc_P2e)		<< 6)
9762306a36Sopenharmony_ci#define P2o_s(a)	(TF(a & NAND_Ecc_P2o)		<< 7)
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define P4e_s(a)	(TF(a & NAND_Ecc_P4e)		<< 0)
10062306a36Sopenharmony_ci#define P4o_s(a)	(TF(a & NAND_Ecc_P4o)		<< 1)
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define	PREFETCH_CONFIG1_CS_SHIFT	24
10362306a36Sopenharmony_ci#define	ECC_CONFIG_CS_SHIFT		1
10462306a36Sopenharmony_ci#define	CS_MASK				0x7
10562306a36Sopenharmony_ci#define	ENABLE_PREFETCH			(0x1 << 7)
10662306a36Sopenharmony_ci#define	DMA_MPU_MODE_SHIFT		2
10762306a36Sopenharmony_ci#define	ECCSIZE0_SHIFT			12
10862306a36Sopenharmony_ci#define	ECCSIZE1_SHIFT			22
10962306a36Sopenharmony_ci#define	ECC1RESULTSIZE			0x1
11062306a36Sopenharmony_ci#define	ECCCLEAR			0x100
11162306a36Sopenharmony_ci#define	ECC1				0x1
11262306a36Sopenharmony_ci#define	PREFETCH_FIFOTHRESHOLD_MAX	0x40
11362306a36Sopenharmony_ci#define	PREFETCH_FIFOTHRESHOLD(val)	((val) << 8)
11462306a36Sopenharmony_ci#define	PREFETCH_STATUS_COUNT(val)	(val & 0x00003fff)
11562306a36Sopenharmony_ci#define	PREFETCH_STATUS_FIFO_CNT(val)	((val >> 24) & 0x7F)
11662306a36Sopenharmony_ci#define	STATUS_BUFF_EMPTY		0x00000001
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define SECTOR_BYTES		512
11962306a36Sopenharmony_ci/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
12062306a36Sopenharmony_ci#define BCH4_BIT_PAD		4
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* GPMC ecc engine settings for read */
12362306a36Sopenharmony_ci#define BCH_WRAPMODE_1		1	/* BCH wrap mode 1 */
12462306a36Sopenharmony_ci#define BCH8R_ECC_SIZE0		0x1a	/* ecc_size0 = 26 */
12562306a36Sopenharmony_ci#define BCH8R_ECC_SIZE1		0x2	/* ecc_size1 = 2 */
12662306a36Sopenharmony_ci#define BCH4R_ECC_SIZE0		0xd	/* ecc_size0 = 13 */
12762306a36Sopenharmony_ci#define BCH4R_ECC_SIZE1		0x3	/* ecc_size1 = 3 */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* GPMC ecc engine settings for write */
13062306a36Sopenharmony_ci#define BCH_WRAPMODE_6		6	/* BCH wrap mode 6 */
13162306a36Sopenharmony_ci#define BCH_ECC_SIZE0		0x0	/* ecc_size0 = 0, no oob protection */
13262306a36Sopenharmony_ci#define BCH_ECC_SIZE1		0x20	/* ecc_size1 = 32 */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#define BBM_LEN			2
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
13762306a36Sopenharmony_ci				0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
13862306a36Sopenharmony_ci				0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93,
13962306a36Sopenharmony_ci				0x07, 0x0e};
14062306a36Sopenharmony_cistatic u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
14162306a36Sopenharmony_ci	0xac, 0x6b, 0xff, 0x99, 0x7b};
14262306a36Sopenharmony_cistatic u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistruct omap_nand_info {
14562306a36Sopenharmony_ci	struct nand_chip		nand;
14662306a36Sopenharmony_ci	struct platform_device		*pdev;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	int				gpmc_cs;
14962306a36Sopenharmony_ci	bool				dev_ready;
15062306a36Sopenharmony_ci	enum nand_io			xfer_type;
15162306a36Sopenharmony_ci	enum omap_ecc			ecc_opt;
15262306a36Sopenharmony_ci	struct device_node		*elm_of_node;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	unsigned long			phys_base;
15562306a36Sopenharmony_ci	struct completion		comp;
15662306a36Sopenharmony_ci	struct dma_chan			*dma;
15762306a36Sopenharmony_ci	int				gpmc_irq_fifo;
15862306a36Sopenharmony_ci	int				gpmc_irq_count;
15962306a36Sopenharmony_ci	enum {
16062306a36Sopenharmony_ci		OMAP_NAND_IO_READ = 0,	/* read */
16162306a36Sopenharmony_ci		OMAP_NAND_IO_WRITE,	/* write */
16262306a36Sopenharmony_ci	} iomode;
16362306a36Sopenharmony_ci	u_char				*buf;
16462306a36Sopenharmony_ci	int					buf_len;
16562306a36Sopenharmony_ci	/* Interface to GPMC */
16662306a36Sopenharmony_ci	void __iomem			*fifo;
16762306a36Sopenharmony_ci	struct gpmc_nand_regs		reg;
16862306a36Sopenharmony_ci	struct gpmc_nand_ops		*ops;
16962306a36Sopenharmony_ci	bool				flash_bbt;
17062306a36Sopenharmony_ci	/* fields specific for BCHx_HW ECC scheme */
17162306a36Sopenharmony_ci	struct device			*elm_dev;
17262306a36Sopenharmony_ci	/* NAND ready gpio */
17362306a36Sopenharmony_ci	struct gpio_desc		*ready_gpiod;
17462306a36Sopenharmony_ci	unsigned int			neccpg;
17562306a36Sopenharmony_ci	unsigned int			nsteps_per_eccpg;
17662306a36Sopenharmony_ci	unsigned int			eccpg_size;
17762306a36Sopenharmony_ci	unsigned int			eccpg_bytes;
17862306a36Sopenharmony_ci	void (*data_in)(struct nand_chip *chip, void *buf,
17962306a36Sopenharmony_ci			unsigned int len, bool force_8bit);
18062306a36Sopenharmony_ci	void (*data_out)(struct nand_chip *chip,
18162306a36Sopenharmony_ci			 const void *buf, unsigned int len,
18262306a36Sopenharmony_ci			 bool force_8bit);
18362306a36Sopenharmony_ci};
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic void omap_nand_data_in(struct nand_chip *chip, void *buf,
19162306a36Sopenharmony_ci			      unsigned int len, bool force_8bit);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void omap_nand_data_out(struct nand_chip *chip,
19462306a36Sopenharmony_ci			       const void *buf, unsigned int len,
19562306a36Sopenharmony_ci			       bool force_8bit);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * omap_prefetch_enable - configures and starts prefetch transfer
19962306a36Sopenharmony_ci * @cs: cs (chip select) number
20062306a36Sopenharmony_ci * @fifo_th: fifo threshold to be used for read/ write
20162306a36Sopenharmony_ci * @dma_mode: dma mode enable (1) or disable (0)
20262306a36Sopenharmony_ci * @u32_count: number of bytes to be transferred
20362306a36Sopenharmony_ci * @is_write: prefetch read(0) or write post(1) mode
20462306a36Sopenharmony_ci * @info: NAND device structure containing platform data
20562306a36Sopenharmony_ci */
20662306a36Sopenharmony_cistatic int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
20762306a36Sopenharmony_ci	unsigned int u32_count, int is_write, struct omap_nand_info *info)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	u32 val;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX)
21262306a36Sopenharmony_ci		return -1;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (readl(info->reg.gpmc_prefetch_control))
21562306a36Sopenharmony_ci		return -EBUSY;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Set the amount of bytes to be prefetched */
21862306a36Sopenharmony_ci	writel(u32_count, info->reg.gpmc_prefetch_config2);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Set dma/mpu mode, the prefetch read / post write and
22162306a36Sopenharmony_ci	 * enable the engine. Set which cs is has requested for.
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) |
22462306a36Sopenharmony_ci		PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH |
22562306a36Sopenharmony_ci		(dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1));
22662306a36Sopenharmony_ci	writel(val, info->reg.gpmc_prefetch_config1);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/*  Start the prefetch engine */
22962306a36Sopenharmony_ci	writel(0x1, info->reg.gpmc_prefetch_control);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return 0;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/*
23562306a36Sopenharmony_ci * omap_prefetch_reset - disables and stops the prefetch engine
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_cistatic int omap_prefetch_reset(int cs, struct omap_nand_info *info)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	u32 config1;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* check if the same module/cs is trying to reset */
24262306a36Sopenharmony_ci	config1 = readl(info->reg.gpmc_prefetch_config1);
24362306a36Sopenharmony_ci	if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs)
24462306a36Sopenharmony_ci		return -EINVAL;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Stop the PFPW engine */
24762306a36Sopenharmony_ci	writel(0x0, info->reg.gpmc_prefetch_control);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* Reset/disable the PFPW engine */
25062306a36Sopenharmony_ci	writel(0x0, info->reg.gpmc_prefetch_config1);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci/**
25662306a36Sopenharmony_ci * omap_nand_data_in_pref - NAND data in using prefetch engine
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cistatic void omap_nand_data_in_pref(struct nand_chip *chip, void *buf,
25962306a36Sopenharmony_ci				   unsigned int len, bool force_8bit)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
26262306a36Sopenharmony_ci	uint32_t r_count = 0;
26362306a36Sopenharmony_ci	int ret = 0;
26462306a36Sopenharmony_ci	u32 *p = (u32 *)buf;
26562306a36Sopenharmony_ci	unsigned int pref_len;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (force_8bit) {
26862306a36Sopenharmony_ci		omap_nand_data_in(chip, buf, len, force_8bit);
26962306a36Sopenharmony_ci		return;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* read 32-bit words using prefetch and remaining bytes normally */
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* configure and start prefetch transfer */
27562306a36Sopenharmony_ci	pref_len = len - (len & 3);
27662306a36Sopenharmony_ci	ret = omap_prefetch_enable(info->gpmc_cs,
27762306a36Sopenharmony_ci			PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info);
27862306a36Sopenharmony_ci	if (ret) {
27962306a36Sopenharmony_ci		/* prefetch engine is busy, use CPU copy method */
28062306a36Sopenharmony_ci		omap_nand_data_in(chip, buf, len, false);
28162306a36Sopenharmony_ci	} else {
28262306a36Sopenharmony_ci		do {
28362306a36Sopenharmony_ci			r_count = readl(info->reg.gpmc_prefetch_status);
28462306a36Sopenharmony_ci			r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
28562306a36Sopenharmony_ci			r_count = r_count >> 2;
28662306a36Sopenharmony_ci			ioread32_rep(info->fifo, p, r_count);
28762306a36Sopenharmony_ci			p += r_count;
28862306a36Sopenharmony_ci			pref_len -= r_count << 2;
28962306a36Sopenharmony_ci		} while (pref_len);
29062306a36Sopenharmony_ci		/* disable and stop the Prefetch engine */
29162306a36Sopenharmony_ci		omap_prefetch_reset(info->gpmc_cs, info);
29262306a36Sopenharmony_ci		/* fetch any remaining bytes */
29362306a36Sopenharmony_ci		if (len & 3)
29462306a36Sopenharmony_ci			omap_nand_data_in(chip, p, len & 3, false);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci/**
29962306a36Sopenharmony_ci * omap_nand_data_out_pref - NAND data out using Write Posting engine
30062306a36Sopenharmony_ci */
30162306a36Sopenharmony_cistatic void omap_nand_data_out_pref(struct nand_chip *chip,
30262306a36Sopenharmony_ci				    const void *buf, unsigned int len,
30362306a36Sopenharmony_ci				    bool force_8bit)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
30662306a36Sopenharmony_ci	uint32_t w_count = 0;
30762306a36Sopenharmony_ci	int i = 0, ret = 0;
30862306a36Sopenharmony_ci	u16 *p = (u16 *)buf;
30962306a36Sopenharmony_ci	unsigned long tim, limit;
31062306a36Sopenharmony_ci	u32 val;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (force_8bit) {
31362306a36Sopenharmony_ci		omap_nand_data_out(chip, buf, len, force_8bit);
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* take care of subpage writes */
31862306a36Sopenharmony_ci	if (len % 2 != 0) {
31962306a36Sopenharmony_ci		writeb(*(u8 *)buf, info->fifo);
32062306a36Sopenharmony_ci		p = (u16 *)(buf + 1);
32162306a36Sopenharmony_ci		len--;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/*  configure and start prefetch transfer */
32562306a36Sopenharmony_ci	ret = omap_prefetch_enable(info->gpmc_cs,
32662306a36Sopenharmony_ci			PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info);
32762306a36Sopenharmony_ci	if (ret) {
32862306a36Sopenharmony_ci		/* write posting engine is busy, use CPU copy method */
32962306a36Sopenharmony_ci		omap_nand_data_out(chip, buf, len, false);
33062306a36Sopenharmony_ci	} else {
33162306a36Sopenharmony_ci		while (len) {
33262306a36Sopenharmony_ci			w_count = readl(info->reg.gpmc_prefetch_status);
33362306a36Sopenharmony_ci			w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
33462306a36Sopenharmony_ci			w_count = w_count >> 1;
33562306a36Sopenharmony_ci			for (i = 0; (i < w_count) && len; i++, len -= 2)
33662306a36Sopenharmony_ci				iowrite16(*p++, info->fifo);
33762306a36Sopenharmony_ci		}
33862306a36Sopenharmony_ci		/* wait for data to flushed-out before reset the prefetch */
33962306a36Sopenharmony_ci		tim = 0;
34062306a36Sopenharmony_ci		limit = (loops_per_jiffy *
34162306a36Sopenharmony_ci					msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
34262306a36Sopenharmony_ci		do {
34362306a36Sopenharmony_ci			cpu_relax();
34462306a36Sopenharmony_ci			val = readl(info->reg.gpmc_prefetch_status);
34562306a36Sopenharmony_ci			val = PREFETCH_STATUS_COUNT(val);
34662306a36Sopenharmony_ci		} while (val && (tim++ < limit));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		/* disable and stop the PFPW engine */
34962306a36Sopenharmony_ci		omap_prefetch_reset(info->gpmc_cs, info);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci/*
35462306a36Sopenharmony_ci * omap_nand_dma_callback: callback on the completion of dma transfer
35562306a36Sopenharmony_ci * @data: pointer to completion data structure
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic void omap_nand_dma_callback(void *data)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	complete((struct completion *) data);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*
36362306a36Sopenharmony_ci * omap_nand_dma_transfer: configure and start dma transfer
36462306a36Sopenharmony_ci * @chip: nand chip structure
36562306a36Sopenharmony_ci * @addr: virtual address in RAM of source/destination
36662306a36Sopenharmony_ci * @len: number of data bytes to be transferred
36762306a36Sopenharmony_ci * @is_write: flag for read/write operation
36862306a36Sopenharmony_ci */
36962306a36Sopenharmony_cistatic inline int omap_nand_dma_transfer(struct nand_chip *chip,
37062306a36Sopenharmony_ci					 const void *addr, unsigned int len,
37162306a36Sopenharmony_ci					 int is_write)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
37462306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
37562306a36Sopenharmony_ci	enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
37662306a36Sopenharmony_ci							DMA_FROM_DEVICE;
37762306a36Sopenharmony_ci	struct scatterlist sg;
37862306a36Sopenharmony_ci	unsigned long tim, limit;
37962306a36Sopenharmony_ci	unsigned n;
38062306a36Sopenharmony_ci	int ret;
38162306a36Sopenharmony_ci	u32 val;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (!virt_addr_valid(addr))
38462306a36Sopenharmony_ci		goto out_copy;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	sg_init_one(&sg, addr, len);
38762306a36Sopenharmony_ci	n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
38862306a36Sopenharmony_ci	if (n == 0) {
38962306a36Sopenharmony_ci		dev_err(&info->pdev->dev,
39062306a36Sopenharmony_ci			"Couldn't DMA map a %d byte buffer\n", len);
39162306a36Sopenharmony_ci		goto out_copy;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
39562306a36Sopenharmony_ci		is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
39662306a36Sopenharmony_ci		DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
39762306a36Sopenharmony_ci	if (!tx)
39862306a36Sopenharmony_ci		goto out_copy_unmap;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	tx->callback = omap_nand_dma_callback;
40162306a36Sopenharmony_ci	tx->callback_param = &info->comp;
40262306a36Sopenharmony_ci	dmaengine_submit(tx);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	init_completion(&info->comp);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	/* setup and start DMA using dma_addr */
40762306a36Sopenharmony_ci	dma_async_issue_pending(info->dma);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/*  configure and start prefetch transfer */
41062306a36Sopenharmony_ci	ret = omap_prefetch_enable(info->gpmc_cs,
41162306a36Sopenharmony_ci		PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info);
41262306a36Sopenharmony_ci	if (ret)
41362306a36Sopenharmony_ci		/* PFPW engine is busy, use cpu copy method */
41462306a36Sopenharmony_ci		goto out_copy_unmap;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	wait_for_completion(&info->comp);
41762306a36Sopenharmony_ci	tim = 0;
41862306a36Sopenharmony_ci	limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	do {
42162306a36Sopenharmony_ci		cpu_relax();
42262306a36Sopenharmony_ci		val = readl(info->reg.gpmc_prefetch_status);
42362306a36Sopenharmony_ci		val = PREFETCH_STATUS_COUNT(val);
42462306a36Sopenharmony_ci	} while (val && (tim++ < limit));
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* disable and stop the PFPW engine */
42762306a36Sopenharmony_ci	omap_prefetch_reset(info->gpmc_cs, info);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciout_copy_unmap:
43362306a36Sopenharmony_ci	dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
43462306a36Sopenharmony_ciout_copy:
43562306a36Sopenharmony_ci	is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false)
43662306a36Sopenharmony_ci		      : omap_nand_data_out(chip, addr, len, false);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/**
44262306a36Sopenharmony_ci * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch
44362306a36Sopenharmony_ci */
44462306a36Sopenharmony_cistatic void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf,
44562306a36Sopenharmony_ci				       unsigned int len, bool force_8bit)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (force_8bit) {
45062306a36Sopenharmony_ci		omap_nand_data_in(chip, buf, len, force_8bit);
45162306a36Sopenharmony_ci		return;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (len <= mtd->oobsize)
45562306a36Sopenharmony_ci		omap_nand_data_in_pref(chip, buf, len, false);
45662306a36Sopenharmony_ci	else
45762306a36Sopenharmony_ci		/* start transfer in DMA mode */
45862306a36Sopenharmony_ci		omap_nand_dma_transfer(chip, buf, len, 0x0);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting
46362306a36Sopenharmony_ci */
46462306a36Sopenharmony_cistatic void omap_nand_data_out_dma_pref(struct nand_chip *chip,
46562306a36Sopenharmony_ci					const void *buf, unsigned int len,
46662306a36Sopenharmony_ci					bool force_8bit)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (force_8bit) {
47162306a36Sopenharmony_ci		omap_nand_data_out(chip, buf, len, force_8bit);
47262306a36Sopenharmony_ci		return;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (len <= mtd->oobsize)
47662306a36Sopenharmony_ci		omap_nand_data_out_pref(chip, buf, len, false);
47762306a36Sopenharmony_ci	else
47862306a36Sopenharmony_ci		/* start transfer in DMA mode */
47962306a36Sopenharmony_ci		omap_nand_dma_transfer(chip, buf, len, 0x1);
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/*
48362306a36Sopenharmony_ci * omap_nand_irq - GPMC irq handler
48462306a36Sopenharmony_ci * @this_irq: gpmc irq number
48562306a36Sopenharmony_ci * @dev: omap_nand_info structure pointer is passed here
48662306a36Sopenharmony_ci */
48762306a36Sopenharmony_cistatic irqreturn_t omap_nand_irq(int this_irq, void *dev)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct omap_nand_info *info = (struct omap_nand_info *) dev;
49062306a36Sopenharmony_ci	u32 bytes;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	bytes = readl(info->reg.gpmc_prefetch_status);
49362306a36Sopenharmony_ci	bytes = PREFETCH_STATUS_FIFO_CNT(bytes);
49462306a36Sopenharmony_ci	bytes = bytes  & 0xFFFC; /* io in multiple of 4 bytes */
49562306a36Sopenharmony_ci	if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */
49662306a36Sopenharmony_ci		if (this_irq == info->gpmc_irq_count)
49762306a36Sopenharmony_ci			goto done;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		if (info->buf_len && (info->buf_len < bytes))
50062306a36Sopenharmony_ci			bytes = info->buf_len;
50162306a36Sopenharmony_ci		else if (!info->buf_len)
50262306a36Sopenharmony_ci			bytes = 0;
50362306a36Sopenharmony_ci		iowrite32_rep(info->fifo, (u32 *)info->buf,
50462306a36Sopenharmony_ci			      bytes >> 2);
50562306a36Sopenharmony_ci		info->buf = info->buf + bytes;
50662306a36Sopenharmony_ci		info->buf_len -= bytes;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	} else {
50962306a36Sopenharmony_ci		ioread32_rep(info->fifo, (u32 *)info->buf,
51062306a36Sopenharmony_ci			     bytes >> 2);
51162306a36Sopenharmony_ci		info->buf = info->buf + bytes;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		if (this_irq == info->gpmc_irq_count)
51462306a36Sopenharmony_ci			goto done;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return IRQ_HANDLED;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cidone:
52062306a36Sopenharmony_ci	complete(&info->comp);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	disable_irq_nosync(info->gpmc_irq_fifo);
52362306a36Sopenharmony_ci	disable_irq_nosync(info->gpmc_irq_count);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return IRQ_HANDLED;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/*
52962306a36Sopenharmony_ci * omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_cistatic void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf,
53262306a36Sopenharmony_ci				       unsigned int len, bool force_8bit)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
53562306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&info->nand);
53662306a36Sopenharmony_ci	int ret = 0;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	if (len <= mtd->oobsize || force_8bit) {
53962306a36Sopenharmony_ci		omap_nand_data_in(chip, buf, len, force_8bit);
54062306a36Sopenharmony_ci		return;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	info->iomode = OMAP_NAND_IO_READ;
54462306a36Sopenharmony_ci	info->buf = buf;
54562306a36Sopenharmony_ci	init_completion(&info->comp);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/*  configure and start prefetch transfer */
54862306a36Sopenharmony_ci	ret = omap_prefetch_enable(info->gpmc_cs,
54962306a36Sopenharmony_ci			PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info);
55062306a36Sopenharmony_ci	if (ret) {
55162306a36Sopenharmony_ci		/* PFPW engine is busy, use cpu copy method */
55262306a36Sopenharmony_ci		omap_nand_data_in(chip, buf, len, false);
55362306a36Sopenharmony_ci		return;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	info->buf_len = len;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	enable_irq(info->gpmc_irq_count);
55962306a36Sopenharmony_ci	enable_irq(info->gpmc_irq_fifo);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* waiting for read to complete */
56262306a36Sopenharmony_ci	wait_for_completion(&info->comp);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* disable and stop the PFPW engine */
56562306a36Sopenharmony_ci	omap_prefetch_reset(info->gpmc_cs, info);
56662306a36Sopenharmony_ci	return;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/*
57062306a36Sopenharmony_ci * omap_nand_data_out_irq_pref - NAND out using write posting and IRQ
57162306a36Sopenharmony_ci */
57262306a36Sopenharmony_cistatic void omap_nand_data_out_irq_pref(struct nand_chip *chip,
57362306a36Sopenharmony_ci					const void *buf, unsigned int len,
57462306a36Sopenharmony_ci					bool force_8bit)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
57762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(&info->nand);
57862306a36Sopenharmony_ci	int ret = 0;
57962306a36Sopenharmony_ci	unsigned long tim, limit;
58062306a36Sopenharmony_ci	u32 val;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (len <= mtd->oobsize || force_8bit) {
58362306a36Sopenharmony_ci		omap_nand_data_out(chip, buf, len, force_8bit);
58462306a36Sopenharmony_ci		return;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	info->iomode = OMAP_NAND_IO_WRITE;
58862306a36Sopenharmony_ci	info->buf = (u_char *) buf;
58962306a36Sopenharmony_ci	init_completion(&info->comp);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* configure and start prefetch transfer : size=24 */
59262306a36Sopenharmony_ci	ret = omap_prefetch_enable(info->gpmc_cs,
59362306a36Sopenharmony_ci		(PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info);
59462306a36Sopenharmony_ci	if (ret) {
59562306a36Sopenharmony_ci		/* PFPW engine is busy, use cpu copy method */
59662306a36Sopenharmony_ci		omap_nand_data_out(chip, buf, len, false);
59762306a36Sopenharmony_ci		return;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	info->buf_len = len;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	enable_irq(info->gpmc_irq_count);
60362306a36Sopenharmony_ci	enable_irq(info->gpmc_irq_fifo);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	/* waiting for write to complete */
60662306a36Sopenharmony_ci	wait_for_completion(&info->comp);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* wait for data to flushed-out before reset the prefetch */
60962306a36Sopenharmony_ci	tim = 0;
61062306a36Sopenharmony_ci	limit = (loops_per_jiffy *  msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
61162306a36Sopenharmony_ci	do {
61262306a36Sopenharmony_ci		val = readl(info->reg.gpmc_prefetch_status);
61362306a36Sopenharmony_ci		val = PREFETCH_STATUS_COUNT(val);
61462306a36Sopenharmony_ci		cpu_relax();
61562306a36Sopenharmony_ci	} while (val && (tim++ < limit));
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	/* disable and stop the PFPW engine */
61862306a36Sopenharmony_ci	omap_prefetch_reset(info->gpmc_cs, info);
61962306a36Sopenharmony_ci	return;
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci/**
62362306a36Sopenharmony_ci * gen_true_ecc - This function will generate true ECC value
62462306a36Sopenharmony_ci * @ecc_buf: buffer to store ecc code
62562306a36Sopenharmony_ci *
62662306a36Sopenharmony_ci * This generated true ECC value can be used when correcting
62762306a36Sopenharmony_ci * data read from NAND flash memory core
62862306a36Sopenharmony_ci */
62962306a36Sopenharmony_cistatic void gen_true_ecc(u8 *ecc_buf)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) |
63262306a36Sopenharmony_ci		((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) |
63562306a36Sopenharmony_ci			P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp));
63662306a36Sopenharmony_ci	ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) |
63762306a36Sopenharmony_ci			P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp));
63862306a36Sopenharmony_ci	ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) |
63962306a36Sopenharmony_ci			P1e(tmp) | P2048o(tmp) | P2048e(tmp));
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci/**
64362306a36Sopenharmony_ci * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
64462306a36Sopenharmony_ci * @ecc_data1:  ecc code from nand spare area
64562306a36Sopenharmony_ci * @ecc_data2:  ecc code from hardware register obtained from hardware ecc
64662306a36Sopenharmony_ci * @page_data:  page data
64762306a36Sopenharmony_ci *
64862306a36Sopenharmony_ci * This function compares two ECC's and indicates if there is an error.
64962306a36Sopenharmony_ci * If the error can be corrected it will be corrected to the buffer.
65062306a36Sopenharmony_ci * If there is no error, %0 is returned. If there is an error but it
65162306a36Sopenharmony_ci * was corrected, %1 is returned. Otherwise, %-1 is returned.
65262306a36Sopenharmony_ci */
65362306a36Sopenharmony_cistatic int omap_compare_ecc(u8 *ecc_data1,	/* read from NAND memory */
65462306a36Sopenharmony_ci			    u8 *ecc_data2,	/* read from register */
65562306a36Sopenharmony_ci			    u8 *page_data)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	uint	i;
65862306a36Sopenharmony_ci	u8	tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];
65962306a36Sopenharmony_ci	u8	comp0_bit[8], comp1_bit[8], comp2_bit[8];
66062306a36Sopenharmony_ci	u8	ecc_bit[24];
66162306a36Sopenharmony_ci	u8	ecc_sum = 0;
66262306a36Sopenharmony_ci	u8	find_bit = 0;
66362306a36Sopenharmony_ci	uint	find_byte = 0;
66462306a36Sopenharmony_ci	int	isEccFF;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	gen_true_ecc(ecc_data1);
66962306a36Sopenharmony_ci	gen_true_ecc(ecc_data2);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	for (i = 0; i <= 2; i++) {
67262306a36Sopenharmony_ci		*(ecc_data1 + i) = ~(*(ecc_data1 + i));
67362306a36Sopenharmony_ci		*(ecc_data2 + i) = ~(*(ecc_data2 + i));
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
67762306a36Sopenharmony_ci		tmp0_bit[i]     = *ecc_data1 % 2;
67862306a36Sopenharmony_ci		*ecc_data1	= *ecc_data1 / 2;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
68262306a36Sopenharmony_ci		tmp1_bit[i]	 = *(ecc_data1 + 1) % 2;
68362306a36Sopenharmony_ci		*(ecc_data1 + 1) = *(ecc_data1 + 1) / 2;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
68762306a36Sopenharmony_ci		tmp2_bit[i]	 = *(ecc_data1 + 2) % 2;
68862306a36Sopenharmony_ci		*(ecc_data1 + 2) = *(ecc_data1 + 2) / 2;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
69262306a36Sopenharmony_ci		comp0_bit[i]     = *ecc_data2 % 2;
69362306a36Sopenharmony_ci		*ecc_data2       = *ecc_data2 / 2;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
69762306a36Sopenharmony_ci		comp1_bit[i]     = *(ecc_data2 + 1) % 2;
69862306a36Sopenharmony_ci		*(ecc_data2 + 1) = *(ecc_data2 + 1) / 2;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
70262306a36Sopenharmony_ci		comp2_bit[i]     = *(ecc_data2 + 2) % 2;
70362306a36Sopenharmony_ci		*(ecc_data2 + 2) = *(ecc_data2 + 2) / 2;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
70762306a36Sopenharmony_ci		ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2];
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
71062306a36Sopenharmony_ci		ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i];
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
71362306a36Sopenharmony_ci		ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i];
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0];
71662306a36Sopenharmony_ci	ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1];
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	for (i = 0; i < 24; i++)
71962306a36Sopenharmony_ci		ecc_sum += ecc_bit[i];
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	switch (ecc_sum) {
72262306a36Sopenharmony_ci	case 0:
72362306a36Sopenharmony_ci		/* Not reached because this function is not called if
72462306a36Sopenharmony_ci		 *  ECC values are equal
72562306a36Sopenharmony_ci		 */
72662306a36Sopenharmony_ci		return 0;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	case 1:
72962306a36Sopenharmony_ci		/* Uncorrectable error */
73062306a36Sopenharmony_ci		pr_debug("ECC UNCORRECTED_ERROR 1\n");
73162306a36Sopenharmony_ci		return -EBADMSG;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	case 11:
73462306a36Sopenharmony_ci		/* UN-Correctable error */
73562306a36Sopenharmony_ci		pr_debug("ECC UNCORRECTED_ERROR B\n");
73662306a36Sopenharmony_ci		return -EBADMSG;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	case 12:
73962306a36Sopenharmony_ci		/* Correctable error */
74062306a36Sopenharmony_ci		find_byte = (ecc_bit[23] << 8) +
74162306a36Sopenharmony_ci			    (ecc_bit[21] << 7) +
74262306a36Sopenharmony_ci			    (ecc_bit[19] << 6) +
74362306a36Sopenharmony_ci			    (ecc_bit[17] << 5) +
74462306a36Sopenharmony_ci			    (ecc_bit[15] << 4) +
74562306a36Sopenharmony_ci			    (ecc_bit[13] << 3) +
74662306a36Sopenharmony_ci			    (ecc_bit[11] << 2) +
74762306a36Sopenharmony_ci			    (ecc_bit[9]  << 1) +
74862306a36Sopenharmony_ci			    ecc_bit[7];
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci		find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1];
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci		pr_debug("Correcting single bit ECC error at offset: "
75362306a36Sopenharmony_ci				"%d, bit: %d\n", find_byte, find_bit);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		page_data[find_byte] ^= (1 << find_bit);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		return 1;
75862306a36Sopenharmony_ci	default:
75962306a36Sopenharmony_ci		if (isEccFF) {
76062306a36Sopenharmony_ci			if (ecc_data2[0] == 0 &&
76162306a36Sopenharmony_ci			    ecc_data2[1] == 0 &&
76262306a36Sopenharmony_ci			    ecc_data2[2] == 0)
76362306a36Sopenharmony_ci				return 0;
76462306a36Sopenharmony_ci		}
76562306a36Sopenharmony_ci		pr_debug("UNCORRECTED_ERROR default\n");
76662306a36Sopenharmony_ci		return -EBADMSG;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci/**
77162306a36Sopenharmony_ci * omap_correct_data - Compares the ECC read with HW generated ECC
77262306a36Sopenharmony_ci * @chip: NAND chip object
77362306a36Sopenharmony_ci * @dat: page data
77462306a36Sopenharmony_ci * @read_ecc: ecc read from nand flash
77562306a36Sopenharmony_ci * @calc_ecc: ecc read from HW ECC registers
77662306a36Sopenharmony_ci *
77762306a36Sopenharmony_ci * Compares the ecc read from nand spare area with ECC registers values
77862306a36Sopenharmony_ci * and if ECC's mismatched, it will call 'omap_compare_ecc' for error
77962306a36Sopenharmony_ci * detection and correction. If there are no errors, %0 is returned. If
78062306a36Sopenharmony_ci * there were errors and all of the errors were corrected, the number of
78162306a36Sopenharmony_ci * corrected errors is returned. If uncorrectable errors exist, %-1 is
78262306a36Sopenharmony_ci * returned.
78362306a36Sopenharmony_ci */
78462306a36Sopenharmony_cistatic int omap_correct_data(struct nand_chip *chip, u_char *dat,
78562306a36Sopenharmony_ci			     u_char *read_ecc, u_char *calc_ecc)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
78862306a36Sopenharmony_ci	int blockCnt = 0, i = 0, ret = 0;
78962306a36Sopenharmony_ci	int stat = 0;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* Ex NAND_ECC_HW12_2048 */
79262306a36Sopenharmony_ci	if (info->nand.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST &&
79362306a36Sopenharmony_ci	    info->nand.ecc.size == 2048)
79462306a36Sopenharmony_ci		blockCnt = 4;
79562306a36Sopenharmony_ci	else
79662306a36Sopenharmony_ci		blockCnt = 1;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	for (i = 0; i < blockCnt; i++) {
79962306a36Sopenharmony_ci		if (memcmp(read_ecc, calc_ecc, 3) != 0) {
80062306a36Sopenharmony_ci			ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
80162306a36Sopenharmony_ci			if (ret < 0)
80262306a36Sopenharmony_ci				return ret;
80362306a36Sopenharmony_ci			/* keep track of the number of corrected errors */
80462306a36Sopenharmony_ci			stat += ret;
80562306a36Sopenharmony_ci		}
80662306a36Sopenharmony_ci		read_ecc += 3;
80762306a36Sopenharmony_ci		calc_ecc += 3;
80862306a36Sopenharmony_ci		dat      += 512;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci	return stat;
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci/**
81462306a36Sopenharmony_ci * omap_calculate_ecc - Generate non-inverted ECC bytes.
81562306a36Sopenharmony_ci * @chip: NAND chip object
81662306a36Sopenharmony_ci * @dat: The pointer to data on which ecc is computed
81762306a36Sopenharmony_ci * @ecc_code: The ecc_code buffer
81862306a36Sopenharmony_ci *
81962306a36Sopenharmony_ci * Using noninverted ECC can be considered ugly since writing a blank
82062306a36Sopenharmony_ci * page ie. padding will clear the ECC bytes. This is no problem as long
82162306a36Sopenharmony_ci * nobody is trying to write data on the seemingly unused page. Reading
82262306a36Sopenharmony_ci * an erased page will produce an ECC mismatch between generated and read
82362306a36Sopenharmony_ci * ECC bytes that has to be dealt with separately.
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_cistatic int omap_calculate_ecc(struct nand_chip *chip, const u_char *dat,
82662306a36Sopenharmony_ci			      u_char *ecc_code)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
82962306a36Sopenharmony_ci	u32 val;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	val = readl(info->reg.gpmc_ecc_config);
83262306a36Sopenharmony_ci	if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs)
83362306a36Sopenharmony_ci		return -EINVAL;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	/* read ecc result */
83662306a36Sopenharmony_ci	val = readl(info->reg.gpmc_ecc1_result);
83762306a36Sopenharmony_ci	*ecc_code++ = val;          /* P128e, ..., P1e */
83862306a36Sopenharmony_ci	*ecc_code++ = val >> 16;    /* P128o, ..., P1o */
83962306a36Sopenharmony_ci	/* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
84062306a36Sopenharmony_ci	*ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	return 0;
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci/**
84662306a36Sopenharmony_ci * omap_enable_hwecc - This function enables the hardware ecc functionality
84762306a36Sopenharmony_ci * @chip: NAND chip object
84862306a36Sopenharmony_ci * @mode: Read/Write mode
84962306a36Sopenharmony_ci */
85062306a36Sopenharmony_cistatic void omap_enable_hwecc(struct nand_chip *chip, int mode)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
85362306a36Sopenharmony_ci	unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
85462306a36Sopenharmony_ci	u32 val;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	/* clear ecc and enable bits */
85762306a36Sopenharmony_ci	val = ECCCLEAR | ECC1;
85862306a36Sopenharmony_ci	writel(val, info->reg.gpmc_ecc_control);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* program ecc and result sizes */
86162306a36Sopenharmony_ci	val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) |
86262306a36Sopenharmony_ci			 ECC1RESULTSIZE);
86362306a36Sopenharmony_ci	writel(val, info->reg.gpmc_ecc_size_config);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	switch (mode) {
86662306a36Sopenharmony_ci	case NAND_ECC_READ:
86762306a36Sopenharmony_ci	case NAND_ECC_WRITE:
86862306a36Sopenharmony_ci		writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
86962306a36Sopenharmony_ci		break;
87062306a36Sopenharmony_ci	case NAND_ECC_READSYN:
87162306a36Sopenharmony_ci		writel(ECCCLEAR, info->reg.gpmc_ecc_control);
87262306a36Sopenharmony_ci		break;
87362306a36Sopenharmony_ci	default:
87462306a36Sopenharmony_ci		dev_info(&info->pdev->dev,
87562306a36Sopenharmony_ci			"error: unrecognized Mode[%d]!\n", mode);
87662306a36Sopenharmony_ci		break;
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	/* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
88062306a36Sopenharmony_ci	val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
88162306a36Sopenharmony_ci	writel(val, info->reg.gpmc_ecc_config);
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci/**
88562306a36Sopenharmony_ci * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
88662306a36Sopenharmony_ci * @chip: NAND chip object
88762306a36Sopenharmony_ci * @mode: Read/Write mode
88862306a36Sopenharmony_ci *
88962306a36Sopenharmony_ci * When using BCH with SW correction (i.e. no ELM), sector size is set
89062306a36Sopenharmony_ci * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode
89162306a36Sopenharmony_ci * for both reading and writing with:
89262306a36Sopenharmony_ci * eccsize0 = 0  (no additional protected byte in spare area)
89362306a36Sopenharmony_ci * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
89462306a36Sopenharmony_ci */
89562306a36Sopenharmony_cistatic void __maybe_unused omap_enable_hwecc_bch(struct nand_chip *chip,
89662306a36Sopenharmony_ci						 int mode)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	unsigned int bch_type;
89962306a36Sopenharmony_ci	unsigned int dev_width, nsectors;
90062306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
90162306a36Sopenharmony_ci	enum omap_ecc ecc_opt = info->ecc_opt;
90262306a36Sopenharmony_ci	u32 val, wr_mode;
90362306a36Sopenharmony_ci	unsigned int ecc_size1, ecc_size0;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	/* GPMC configurations for calculating ECC */
90662306a36Sopenharmony_ci	switch (ecc_opt) {
90762306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
90862306a36Sopenharmony_ci		bch_type = 0;
90962306a36Sopenharmony_ci		nsectors = 1;
91062306a36Sopenharmony_ci		wr_mode	  = BCH_WRAPMODE_6;
91162306a36Sopenharmony_ci		ecc_size0 = BCH_ECC_SIZE0;
91262306a36Sopenharmony_ci		ecc_size1 = BCH_ECC_SIZE1;
91362306a36Sopenharmony_ci		break;
91462306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW:
91562306a36Sopenharmony_ci		bch_type = 0;
91662306a36Sopenharmony_ci		nsectors = chip->ecc.steps;
91762306a36Sopenharmony_ci		if (mode == NAND_ECC_READ) {
91862306a36Sopenharmony_ci			wr_mode	  = BCH_WRAPMODE_1;
91962306a36Sopenharmony_ci			ecc_size0 = BCH4R_ECC_SIZE0;
92062306a36Sopenharmony_ci			ecc_size1 = BCH4R_ECC_SIZE1;
92162306a36Sopenharmony_ci		} else {
92262306a36Sopenharmony_ci			wr_mode   = BCH_WRAPMODE_6;
92362306a36Sopenharmony_ci			ecc_size0 = BCH_ECC_SIZE0;
92462306a36Sopenharmony_ci			ecc_size1 = BCH_ECC_SIZE1;
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci		break;
92762306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
92862306a36Sopenharmony_ci		bch_type = 1;
92962306a36Sopenharmony_ci		nsectors = 1;
93062306a36Sopenharmony_ci		wr_mode	  = BCH_WRAPMODE_6;
93162306a36Sopenharmony_ci		ecc_size0 = BCH_ECC_SIZE0;
93262306a36Sopenharmony_ci		ecc_size1 = BCH_ECC_SIZE1;
93362306a36Sopenharmony_ci		break;
93462306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW:
93562306a36Sopenharmony_ci		bch_type = 1;
93662306a36Sopenharmony_ci		nsectors = chip->ecc.steps;
93762306a36Sopenharmony_ci		if (mode == NAND_ECC_READ) {
93862306a36Sopenharmony_ci			wr_mode	  = BCH_WRAPMODE_1;
93962306a36Sopenharmony_ci			ecc_size0 = BCH8R_ECC_SIZE0;
94062306a36Sopenharmony_ci			ecc_size1 = BCH8R_ECC_SIZE1;
94162306a36Sopenharmony_ci		} else {
94262306a36Sopenharmony_ci			wr_mode   = BCH_WRAPMODE_6;
94362306a36Sopenharmony_ci			ecc_size0 = BCH_ECC_SIZE0;
94462306a36Sopenharmony_ci			ecc_size1 = BCH_ECC_SIZE1;
94562306a36Sopenharmony_ci		}
94662306a36Sopenharmony_ci		break;
94762306a36Sopenharmony_ci	case OMAP_ECC_BCH16_CODE_HW:
94862306a36Sopenharmony_ci		bch_type = 0x2;
94962306a36Sopenharmony_ci		nsectors = chip->ecc.steps;
95062306a36Sopenharmony_ci		if (mode == NAND_ECC_READ) {
95162306a36Sopenharmony_ci			wr_mode	  = 0x01;
95262306a36Sopenharmony_ci			ecc_size0 = 52; /* ECC bits in nibbles per sector */
95362306a36Sopenharmony_ci			ecc_size1 = 0;  /* non-ECC bits in nibbles per sector */
95462306a36Sopenharmony_ci		} else {
95562306a36Sopenharmony_ci			wr_mode	  = 0x01;
95662306a36Sopenharmony_ci			ecc_size0 = 0;  /* extra bits in nibbles per sector */
95762306a36Sopenharmony_ci			ecc_size1 = 52; /* OOB bits in nibbles per sector */
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci		break;
96062306a36Sopenharmony_ci	default:
96162306a36Sopenharmony_ci		return;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	writel(ECC1, info->reg.gpmc_ecc_control);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	/* Configure ecc size for BCH */
96762306a36Sopenharmony_ci	val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT);
96862306a36Sopenharmony_ci	writel(val, info->reg.gpmc_ecc_size_config);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* BCH configuration */
97362306a36Sopenharmony_ci	val = ((1                        << 16) | /* enable BCH */
97462306a36Sopenharmony_ci	       (bch_type		 << 12) | /* BCH4/BCH8/BCH16 */
97562306a36Sopenharmony_ci	       (wr_mode                  <<  8) | /* wrap mode */
97662306a36Sopenharmony_ci	       (dev_width                <<  7) | /* bus width */
97762306a36Sopenharmony_ci	       (((nsectors-1) & 0x7)     <<  4) | /* number of sectors */
97862306a36Sopenharmony_ci	       (info->gpmc_cs            <<  1) | /* ECC CS */
97962306a36Sopenharmony_ci	       (0x1));                            /* enable ECC */
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	writel(val, info->reg.gpmc_ecc_config);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Clear ecc and enable bits */
98462306a36Sopenharmony_ci	writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic u8  bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
98862306a36Sopenharmony_cistatic u8  bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
98962306a36Sopenharmony_ci				0x97, 0x79, 0xe5, 0x24, 0xb5};
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci/**
99262306a36Sopenharmony_ci * _omap_calculate_ecc_bch - Generate ECC bytes for one sector
99362306a36Sopenharmony_ci * @mtd:	MTD device structure
99462306a36Sopenharmony_ci * @dat:	The pointer to data on which ecc is computed
99562306a36Sopenharmony_ci * @ecc_calc:	The ecc_code buffer
99662306a36Sopenharmony_ci * @i:		The sector number (for a multi sector page)
99762306a36Sopenharmony_ci *
99862306a36Sopenharmony_ci * Support calculating of BCH4/8/16 ECC vectors for one sector
99962306a36Sopenharmony_ci * within a page. Sector number is in @i.
100062306a36Sopenharmony_ci */
100162306a36Sopenharmony_cistatic int _omap_calculate_ecc_bch(struct mtd_info *mtd,
100262306a36Sopenharmony_ci				   const u_char *dat, u_char *ecc_calc, int i)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
100562306a36Sopenharmony_ci	int eccbytes	= info->nand.ecc.bytes;
100662306a36Sopenharmony_ci	struct gpmc_nand_regs	*gpmc_regs = &info->reg;
100762306a36Sopenharmony_ci	u8 *ecc_code;
100862306a36Sopenharmony_ci	unsigned long bch_val1, bch_val2, bch_val3, bch_val4;
100962306a36Sopenharmony_ci	u32 val;
101062306a36Sopenharmony_ci	int j;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ecc_code = ecc_calc;
101362306a36Sopenharmony_ci	switch (info->ecc_opt) {
101462306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
101562306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW:
101662306a36Sopenharmony_ci		bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
101762306a36Sopenharmony_ci		bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
101862306a36Sopenharmony_ci		bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
101962306a36Sopenharmony_ci		bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
102062306a36Sopenharmony_ci		*ecc_code++ = (bch_val4 & 0xFF);
102162306a36Sopenharmony_ci		*ecc_code++ = ((bch_val3 >> 24) & 0xFF);
102262306a36Sopenharmony_ci		*ecc_code++ = ((bch_val3 >> 16) & 0xFF);
102362306a36Sopenharmony_ci		*ecc_code++ = ((bch_val3 >> 8) & 0xFF);
102462306a36Sopenharmony_ci		*ecc_code++ = (bch_val3 & 0xFF);
102562306a36Sopenharmony_ci		*ecc_code++ = ((bch_val2 >> 24) & 0xFF);
102662306a36Sopenharmony_ci		*ecc_code++ = ((bch_val2 >> 16) & 0xFF);
102762306a36Sopenharmony_ci		*ecc_code++ = ((bch_val2 >> 8) & 0xFF);
102862306a36Sopenharmony_ci		*ecc_code++ = (bch_val2 & 0xFF);
102962306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 >> 24) & 0xFF);
103062306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 >> 16) & 0xFF);
103162306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 >> 8) & 0xFF);
103262306a36Sopenharmony_ci		*ecc_code++ = (bch_val1 & 0xFF);
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
103562306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW:
103662306a36Sopenharmony_ci		bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
103762306a36Sopenharmony_ci		bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
103862306a36Sopenharmony_ci		*ecc_code++ = ((bch_val2 >> 12) & 0xFF);
103962306a36Sopenharmony_ci		*ecc_code++ = ((bch_val2 >> 4) & 0xFF);
104062306a36Sopenharmony_ci		*ecc_code++ = ((bch_val2 & 0xF) << 4) |
104162306a36Sopenharmony_ci			((bch_val1 >> 28) & 0xF);
104262306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 >> 20) & 0xFF);
104362306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 >> 12) & 0xFF);
104462306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 >> 4) & 0xFF);
104562306a36Sopenharmony_ci		*ecc_code++ = ((bch_val1 & 0xF) << 4);
104662306a36Sopenharmony_ci		break;
104762306a36Sopenharmony_ci	case OMAP_ECC_BCH16_CODE_HW:
104862306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result6[i]);
104962306a36Sopenharmony_ci		ecc_code[0]  = ((val >>  8) & 0xFF);
105062306a36Sopenharmony_ci		ecc_code[1]  = ((val >>  0) & 0xFF);
105162306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result5[i]);
105262306a36Sopenharmony_ci		ecc_code[2]  = ((val >> 24) & 0xFF);
105362306a36Sopenharmony_ci		ecc_code[3]  = ((val >> 16) & 0xFF);
105462306a36Sopenharmony_ci		ecc_code[4]  = ((val >>  8) & 0xFF);
105562306a36Sopenharmony_ci		ecc_code[5]  = ((val >>  0) & 0xFF);
105662306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result4[i]);
105762306a36Sopenharmony_ci		ecc_code[6]  = ((val >> 24) & 0xFF);
105862306a36Sopenharmony_ci		ecc_code[7]  = ((val >> 16) & 0xFF);
105962306a36Sopenharmony_ci		ecc_code[8]  = ((val >>  8) & 0xFF);
106062306a36Sopenharmony_ci		ecc_code[9]  = ((val >>  0) & 0xFF);
106162306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result3[i]);
106262306a36Sopenharmony_ci		ecc_code[10] = ((val >> 24) & 0xFF);
106362306a36Sopenharmony_ci		ecc_code[11] = ((val >> 16) & 0xFF);
106462306a36Sopenharmony_ci		ecc_code[12] = ((val >>  8) & 0xFF);
106562306a36Sopenharmony_ci		ecc_code[13] = ((val >>  0) & 0xFF);
106662306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result2[i]);
106762306a36Sopenharmony_ci		ecc_code[14] = ((val >> 24) & 0xFF);
106862306a36Sopenharmony_ci		ecc_code[15] = ((val >> 16) & 0xFF);
106962306a36Sopenharmony_ci		ecc_code[16] = ((val >>  8) & 0xFF);
107062306a36Sopenharmony_ci		ecc_code[17] = ((val >>  0) & 0xFF);
107162306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result1[i]);
107262306a36Sopenharmony_ci		ecc_code[18] = ((val >> 24) & 0xFF);
107362306a36Sopenharmony_ci		ecc_code[19] = ((val >> 16) & 0xFF);
107462306a36Sopenharmony_ci		ecc_code[20] = ((val >>  8) & 0xFF);
107562306a36Sopenharmony_ci		ecc_code[21] = ((val >>  0) & 0xFF);
107662306a36Sopenharmony_ci		val = readl(gpmc_regs->gpmc_bch_result0[i]);
107762306a36Sopenharmony_ci		ecc_code[22] = ((val >> 24) & 0xFF);
107862306a36Sopenharmony_ci		ecc_code[23] = ((val >> 16) & 0xFF);
107962306a36Sopenharmony_ci		ecc_code[24] = ((val >>  8) & 0xFF);
108062306a36Sopenharmony_ci		ecc_code[25] = ((val >>  0) & 0xFF);
108162306a36Sopenharmony_ci		break;
108262306a36Sopenharmony_ci	default:
108362306a36Sopenharmony_ci		return -EINVAL;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* ECC scheme specific syndrome customizations */
108762306a36Sopenharmony_ci	switch (info->ecc_opt) {
108862306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
108962306a36Sopenharmony_ci		/* Add constant polynomial to remainder, so that
109062306a36Sopenharmony_ci		 * ECC of blank pages results in 0x0 on reading back
109162306a36Sopenharmony_ci		 */
109262306a36Sopenharmony_ci		for (j = 0; j < eccbytes; j++)
109362306a36Sopenharmony_ci			ecc_calc[j] ^= bch4_polynomial[j];
109462306a36Sopenharmony_ci		break;
109562306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW:
109662306a36Sopenharmony_ci		/* Set  8th ECC byte as 0x0 for ROM compatibility */
109762306a36Sopenharmony_ci		ecc_calc[eccbytes - 1] = 0x0;
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
110062306a36Sopenharmony_ci		/* Add constant polynomial to remainder, so that
110162306a36Sopenharmony_ci		 * ECC of blank pages results in 0x0 on reading back
110262306a36Sopenharmony_ci		 */
110362306a36Sopenharmony_ci		for (j = 0; j < eccbytes; j++)
110462306a36Sopenharmony_ci			ecc_calc[j] ^= bch8_polynomial[j];
110562306a36Sopenharmony_ci		break;
110662306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW:
110762306a36Sopenharmony_ci		/* Set 14th ECC byte as 0x0 for ROM compatibility */
110862306a36Sopenharmony_ci		ecc_calc[eccbytes - 1] = 0x0;
110962306a36Sopenharmony_ci		break;
111062306a36Sopenharmony_ci	case OMAP_ECC_BCH16_CODE_HW:
111162306a36Sopenharmony_ci		break;
111262306a36Sopenharmony_ci	default:
111362306a36Sopenharmony_ci		return -EINVAL;
111462306a36Sopenharmony_ci	}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	return 0;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci/**
112062306a36Sopenharmony_ci * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction
112162306a36Sopenharmony_ci * @chip:	NAND chip object
112262306a36Sopenharmony_ci * @dat:	The pointer to data on which ecc is computed
112362306a36Sopenharmony_ci * @ecc_calc:	Buffer storing the calculated ECC bytes
112462306a36Sopenharmony_ci *
112562306a36Sopenharmony_ci * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used
112662306a36Sopenharmony_ci * when SW based correction is required as ECC is required for one sector
112762306a36Sopenharmony_ci * at a time.
112862306a36Sopenharmony_ci */
112962306a36Sopenharmony_cistatic int omap_calculate_ecc_bch_sw(struct nand_chip *chip,
113062306a36Sopenharmony_ci				     const u_char *dat, u_char *ecc_calc)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	return _omap_calculate_ecc_bch(nand_to_mtd(chip), dat, ecc_calc, 0);
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci/**
113662306a36Sopenharmony_ci * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors
113762306a36Sopenharmony_ci * @mtd:	MTD device structure
113862306a36Sopenharmony_ci * @dat:	The pointer to data on which ecc is computed
113962306a36Sopenharmony_ci * @ecc_calc:	Buffer storing the calculated ECC bytes
114062306a36Sopenharmony_ci *
114162306a36Sopenharmony_ci * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
114262306a36Sopenharmony_ci */
114362306a36Sopenharmony_cistatic int omap_calculate_ecc_bch_multi(struct mtd_info *mtd,
114462306a36Sopenharmony_ci					const u_char *dat, u_char *ecc_calc)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
114762306a36Sopenharmony_ci	int eccbytes = info->nand.ecc.bytes;
114862306a36Sopenharmony_ci	unsigned long nsectors;
114962306a36Sopenharmony_ci	int i, ret;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
115262306a36Sopenharmony_ci	for (i = 0; i < nsectors; i++) {
115362306a36Sopenharmony_ci		ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i);
115462306a36Sopenharmony_ci		if (ret)
115562306a36Sopenharmony_ci			return ret;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		ecc_calc += eccbytes;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return 0;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/**
116462306a36Sopenharmony_ci * erased_sector_bitflips - count bit flips
116562306a36Sopenharmony_ci * @data:	data sector buffer
116662306a36Sopenharmony_ci * @oob:	oob buffer
116762306a36Sopenharmony_ci * @info:	omap_nand_info
116862306a36Sopenharmony_ci *
116962306a36Sopenharmony_ci * Check the bit flips in erased page falls below correctable level.
117062306a36Sopenharmony_ci * If falls below, report the page as erased with correctable bit
117162306a36Sopenharmony_ci * flip, else report as uncorrectable page.
117262306a36Sopenharmony_ci */
117362306a36Sopenharmony_cistatic int erased_sector_bitflips(u_char *data, u_char *oob,
117462306a36Sopenharmony_ci		struct omap_nand_info *info)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	int flip_bits = 0, i;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	for (i = 0; i < info->nand.ecc.size; i++) {
117962306a36Sopenharmony_ci		flip_bits += hweight8(~data[i]);
118062306a36Sopenharmony_ci		if (flip_bits > info->nand.ecc.strength)
118162306a36Sopenharmony_ci			return 0;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	for (i = 0; i < info->nand.ecc.bytes - 1; i++) {
118562306a36Sopenharmony_ci		flip_bits += hweight8(~oob[i]);
118662306a36Sopenharmony_ci		if (flip_bits > info->nand.ecc.strength)
118762306a36Sopenharmony_ci			return 0;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/*
119162306a36Sopenharmony_ci	 * Bit flips falls in correctable level.
119262306a36Sopenharmony_ci	 * Fill data area with 0xFF
119362306a36Sopenharmony_ci	 */
119462306a36Sopenharmony_ci	if (flip_bits) {
119562306a36Sopenharmony_ci		memset(data, 0xFF, info->nand.ecc.size);
119662306a36Sopenharmony_ci		memset(oob, 0xFF, info->nand.ecc.bytes);
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	return flip_bits;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci/**
120362306a36Sopenharmony_ci * omap_elm_correct_data - corrects page data area in case error reported
120462306a36Sopenharmony_ci * @chip:	NAND chip object
120562306a36Sopenharmony_ci * @data:	page data
120662306a36Sopenharmony_ci * @read_ecc:	ecc read from nand flash
120762306a36Sopenharmony_ci * @calc_ecc:	ecc read from HW ECC registers
120862306a36Sopenharmony_ci *
120962306a36Sopenharmony_ci * Calculated ecc vector reported as zero in case of non-error pages.
121062306a36Sopenharmony_ci * In case of non-zero ecc vector, first filter out erased-pages, and
121162306a36Sopenharmony_ci * then process data via ELM to detect bit-flips.
121262306a36Sopenharmony_ci */
121362306a36Sopenharmony_cistatic int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
121462306a36Sopenharmony_ci				 u_char *read_ecc, u_char *calc_ecc)
121562306a36Sopenharmony_ci{
121662306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
121762306a36Sopenharmony_ci	struct nand_ecc_ctrl *ecc = &info->nand.ecc;
121862306a36Sopenharmony_ci	int eccsteps = info->nsteps_per_eccpg;
121962306a36Sopenharmony_ci	int i , j, stat = 0;
122062306a36Sopenharmony_ci	int eccflag, actual_eccbytes;
122162306a36Sopenharmony_ci	struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
122262306a36Sopenharmony_ci	u_char *ecc_vec = calc_ecc;
122362306a36Sopenharmony_ci	u_char *spare_ecc = read_ecc;
122462306a36Sopenharmony_ci	u_char *erased_ecc_vec;
122562306a36Sopenharmony_ci	u_char *buf;
122662306a36Sopenharmony_ci	int bitflip_count;
122762306a36Sopenharmony_ci	bool is_error_reported = false;
122862306a36Sopenharmony_ci	u32 bit_pos, byte_pos, error_max, pos;
122962306a36Sopenharmony_ci	int err;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	switch (info->ecc_opt) {
123262306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW:
123362306a36Sopenharmony_ci		/* omit  7th ECC byte reserved for ROM code compatibility */
123462306a36Sopenharmony_ci		actual_eccbytes = ecc->bytes - 1;
123562306a36Sopenharmony_ci		erased_ecc_vec = bch4_vector;
123662306a36Sopenharmony_ci		break;
123762306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW:
123862306a36Sopenharmony_ci		/* omit 14th ECC byte reserved for ROM code compatibility */
123962306a36Sopenharmony_ci		actual_eccbytes = ecc->bytes - 1;
124062306a36Sopenharmony_ci		erased_ecc_vec = bch8_vector;
124162306a36Sopenharmony_ci		break;
124262306a36Sopenharmony_ci	case OMAP_ECC_BCH16_CODE_HW:
124362306a36Sopenharmony_ci		actual_eccbytes = ecc->bytes;
124462306a36Sopenharmony_ci		erased_ecc_vec = bch16_vector;
124562306a36Sopenharmony_ci		break;
124662306a36Sopenharmony_ci	default:
124762306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "invalid driver configuration\n");
124862306a36Sopenharmony_ci		return -EINVAL;
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	/* Initialize elm error vector to zero */
125262306a36Sopenharmony_ci	memset(err_vec, 0, sizeof(err_vec));
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	for (i = 0; i < eccsteps ; i++) {
125562306a36Sopenharmony_ci		eccflag = 0;	/* initialize eccflag */
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		/*
125862306a36Sopenharmony_ci		 * Check any error reported,
125962306a36Sopenharmony_ci		 * In case of error, non zero ecc reported.
126062306a36Sopenharmony_ci		 */
126162306a36Sopenharmony_ci		for (j = 0; j < actual_eccbytes; j++) {
126262306a36Sopenharmony_ci			if (calc_ecc[j] != 0) {
126362306a36Sopenharmony_ci				eccflag = 1; /* non zero ecc, error present */
126462306a36Sopenharmony_ci				break;
126562306a36Sopenharmony_ci			}
126662306a36Sopenharmony_ci		}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci		if (eccflag == 1) {
126962306a36Sopenharmony_ci			if (memcmp(calc_ecc, erased_ecc_vec,
127062306a36Sopenharmony_ci						actual_eccbytes) == 0) {
127162306a36Sopenharmony_ci				/*
127262306a36Sopenharmony_ci				 * calc_ecc[] matches pattern for ECC(all 0xff)
127362306a36Sopenharmony_ci				 * so this is definitely an erased-page
127462306a36Sopenharmony_ci				 */
127562306a36Sopenharmony_ci			} else {
127662306a36Sopenharmony_ci				buf = &data[info->nand.ecc.size * i];
127762306a36Sopenharmony_ci				/*
127862306a36Sopenharmony_ci				 * count number of 0-bits in read_buf.
127962306a36Sopenharmony_ci				 * This check can be removed once a similar
128062306a36Sopenharmony_ci				 * check is introduced in generic NAND driver
128162306a36Sopenharmony_ci				 */
128262306a36Sopenharmony_ci				bitflip_count = erased_sector_bitflips(
128362306a36Sopenharmony_ci						buf, read_ecc, info);
128462306a36Sopenharmony_ci				if (bitflip_count) {
128562306a36Sopenharmony_ci					/*
128662306a36Sopenharmony_ci					 * number of 0-bits within ECC limits
128762306a36Sopenharmony_ci					 * So this may be an erased-page
128862306a36Sopenharmony_ci					 */
128962306a36Sopenharmony_ci					stat += bitflip_count;
129062306a36Sopenharmony_ci				} else {
129162306a36Sopenharmony_ci					/*
129262306a36Sopenharmony_ci					 * Too many 0-bits. It may be a
129362306a36Sopenharmony_ci					 * - programmed-page, OR
129462306a36Sopenharmony_ci					 * - erased-page with many bit-flips
129562306a36Sopenharmony_ci					 * So this page requires check by ELM
129662306a36Sopenharmony_ci					 */
129762306a36Sopenharmony_ci					err_vec[i].error_reported = true;
129862306a36Sopenharmony_ci					is_error_reported = true;
129962306a36Sopenharmony_ci				}
130062306a36Sopenharmony_ci			}
130162306a36Sopenharmony_ci		}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		/* Update the ecc vector */
130462306a36Sopenharmony_ci		calc_ecc += ecc->bytes;
130562306a36Sopenharmony_ci		read_ecc += ecc->bytes;
130662306a36Sopenharmony_ci	}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	/* Check if any error reported */
130962306a36Sopenharmony_ci	if (!is_error_reported)
131062306a36Sopenharmony_ci		return stat;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	/* Decode BCH error using ELM module */
131362306a36Sopenharmony_ci	elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	err = 0;
131662306a36Sopenharmony_ci	for (i = 0; i < eccsteps; i++) {
131762306a36Sopenharmony_ci		if (err_vec[i].error_uncorrectable) {
131862306a36Sopenharmony_ci			dev_err(&info->pdev->dev,
131962306a36Sopenharmony_ci				"uncorrectable bit-flips found\n");
132062306a36Sopenharmony_ci			err = -EBADMSG;
132162306a36Sopenharmony_ci		} else if (err_vec[i].error_reported) {
132262306a36Sopenharmony_ci			for (j = 0; j < err_vec[i].error_count; j++) {
132362306a36Sopenharmony_ci				switch (info->ecc_opt) {
132462306a36Sopenharmony_ci				case OMAP_ECC_BCH4_CODE_HW:
132562306a36Sopenharmony_ci					/* Add 4 bits to take care of padding */
132662306a36Sopenharmony_ci					pos = err_vec[i].error_loc[j] +
132762306a36Sopenharmony_ci						BCH4_BIT_PAD;
132862306a36Sopenharmony_ci					break;
132962306a36Sopenharmony_ci				case OMAP_ECC_BCH8_CODE_HW:
133062306a36Sopenharmony_ci				case OMAP_ECC_BCH16_CODE_HW:
133162306a36Sopenharmony_ci					pos = err_vec[i].error_loc[j];
133262306a36Sopenharmony_ci					break;
133362306a36Sopenharmony_ci				default:
133462306a36Sopenharmony_ci					return -EINVAL;
133562306a36Sopenharmony_ci				}
133662306a36Sopenharmony_ci				error_max = (ecc->size + actual_eccbytes) * 8;
133762306a36Sopenharmony_ci				/* Calculate bit position of error */
133862306a36Sopenharmony_ci				bit_pos = pos % 8;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci				/* Calculate byte position of error */
134162306a36Sopenharmony_ci				byte_pos = (error_max - pos - 1) / 8;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci				if (pos < error_max) {
134462306a36Sopenharmony_ci					if (byte_pos < 512) {
134562306a36Sopenharmony_ci						pr_debug("bitflip@dat[%d]=%x\n",
134662306a36Sopenharmony_ci						     byte_pos, data[byte_pos]);
134762306a36Sopenharmony_ci						data[byte_pos] ^= 1 << bit_pos;
134862306a36Sopenharmony_ci					} else {
134962306a36Sopenharmony_ci						pr_debug("bitflip@oob[%d]=%x\n",
135062306a36Sopenharmony_ci							(byte_pos - 512),
135162306a36Sopenharmony_ci						     spare_ecc[byte_pos - 512]);
135262306a36Sopenharmony_ci						spare_ecc[byte_pos - 512] ^=
135362306a36Sopenharmony_ci							1 << bit_pos;
135462306a36Sopenharmony_ci					}
135562306a36Sopenharmony_ci				} else {
135662306a36Sopenharmony_ci					dev_err(&info->pdev->dev,
135762306a36Sopenharmony_ci						"invalid bit-flip @ %d:%d\n",
135862306a36Sopenharmony_ci						byte_pos, bit_pos);
135962306a36Sopenharmony_ci					err = -EBADMSG;
136062306a36Sopenharmony_ci				}
136162306a36Sopenharmony_ci			}
136262306a36Sopenharmony_ci		}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci		/* Update number of correctable errors */
136562306a36Sopenharmony_ci		stat = max_t(unsigned int, stat, err_vec[i].error_count);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		/* Update page data with sector size */
136862306a36Sopenharmony_ci		data += ecc->size;
136962306a36Sopenharmony_ci		spare_ecc += ecc->bytes;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	return (err) ? err : stat;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci/**
137662306a36Sopenharmony_ci * omap_write_page_bch - BCH ecc based write page function for entire page
137762306a36Sopenharmony_ci * @chip:		nand chip info structure
137862306a36Sopenharmony_ci * @buf:		data buffer
137962306a36Sopenharmony_ci * @oob_required:	must write chip->oob_poi to OOB
138062306a36Sopenharmony_ci * @page:		page
138162306a36Sopenharmony_ci *
138262306a36Sopenharmony_ci * Custom write page method evolved to support multi sector writing in one shot
138362306a36Sopenharmony_ci */
138462306a36Sopenharmony_cistatic int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
138562306a36Sopenharmony_ci			       int oob_required, int page)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
138862306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
138962306a36Sopenharmony_ci	uint8_t *ecc_calc = chip->ecc.calc_buf;
139062306a36Sopenharmony_ci	unsigned int eccpg;
139162306a36Sopenharmony_ci	int ret;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
139462306a36Sopenharmony_ci	if (ret)
139562306a36Sopenharmony_ci		return ret;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
139862306a36Sopenharmony_ci		/* Enable GPMC ecc engine */
139962306a36Sopenharmony_ci		chip->ecc.hwctl(chip, NAND_ECC_WRITE);
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci		/* Write data */
140262306a36Sopenharmony_ci		info->data_out(chip, buf + (eccpg * info->eccpg_size),
140362306a36Sopenharmony_ci			       info->eccpg_size, false);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci		/* Update ecc vector from GPMC result registers */
140662306a36Sopenharmony_ci		ret = omap_calculate_ecc_bch_multi(mtd,
140762306a36Sopenharmony_ci						   buf + (eccpg * info->eccpg_size),
140862306a36Sopenharmony_ci						   ecc_calc);
140962306a36Sopenharmony_ci		if (ret)
141062306a36Sopenharmony_ci			return ret;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
141362306a36Sopenharmony_ci						 chip->oob_poi,
141462306a36Sopenharmony_ci						 eccpg * info->eccpg_bytes,
141562306a36Sopenharmony_ci						 info->eccpg_bytes);
141662306a36Sopenharmony_ci		if (ret)
141762306a36Sopenharmony_ci			return ret;
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/* Write ecc vector to OOB area */
142162306a36Sopenharmony_ci	info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	return nand_prog_page_end_op(chip);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci/**
142762306a36Sopenharmony_ci * omap_write_subpage_bch - BCH hardware ECC based subpage write
142862306a36Sopenharmony_ci * @chip:	nand chip info structure
142962306a36Sopenharmony_ci * @offset:	column address of subpage within the page
143062306a36Sopenharmony_ci * @data_len:	data length
143162306a36Sopenharmony_ci * @buf:	data buffer
143262306a36Sopenharmony_ci * @oob_required: must write chip->oob_poi to OOB
143362306a36Sopenharmony_ci * @page: page number to write
143462306a36Sopenharmony_ci *
143562306a36Sopenharmony_ci * OMAP optimized subpage write method.
143662306a36Sopenharmony_ci */
143762306a36Sopenharmony_cistatic int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
143862306a36Sopenharmony_ci				  u32 data_len, const u8 *buf,
143962306a36Sopenharmony_ci				  int oob_required, int page)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
144262306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
144362306a36Sopenharmony_ci	u8 *ecc_calc = chip->ecc.calc_buf;
144462306a36Sopenharmony_ci	int ecc_size      = chip->ecc.size;
144562306a36Sopenharmony_ci	int ecc_bytes     = chip->ecc.bytes;
144662306a36Sopenharmony_ci	u32 start_step = offset / ecc_size;
144762306a36Sopenharmony_ci	u32 end_step   = (offset + data_len - 1) / ecc_size;
144862306a36Sopenharmony_ci	unsigned int eccpg;
144962306a36Sopenharmony_ci	int step, ret = 0;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	/*
145262306a36Sopenharmony_ci	 * Write entire page at one go as it would be optimal
145362306a36Sopenharmony_ci	 * as ECC is calculated by hardware.
145462306a36Sopenharmony_ci	 * ECC is calculated for all subpages but we choose
145562306a36Sopenharmony_ci	 * only what we want.
145662306a36Sopenharmony_ci	 */
145762306a36Sopenharmony_ci	ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
145862306a36Sopenharmony_ci	if (ret)
145962306a36Sopenharmony_ci		return ret;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
146262306a36Sopenharmony_ci		/* Enable GPMC ECC engine */
146362306a36Sopenharmony_ci		chip->ecc.hwctl(chip, NAND_ECC_WRITE);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci		/* Write data */
146662306a36Sopenharmony_ci		info->data_out(chip, buf + (eccpg * info->eccpg_size),
146762306a36Sopenharmony_ci			       info->eccpg_size, false);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		for (step = 0; step < info->nsteps_per_eccpg; step++) {
147062306a36Sopenharmony_ci			unsigned int base_step = eccpg * info->nsteps_per_eccpg;
147162306a36Sopenharmony_ci			const u8 *bufoffs = buf + (eccpg * info->eccpg_size);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci			/* Mask ECC of un-touched subpages with 0xFFs */
147462306a36Sopenharmony_ci			if ((step + base_step) < start_step ||
147562306a36Sopenharmony_ci			    (step + base_step) > end_step)
147662306a36Sopenharmony_ci				memset(ecc_calc + (step * ecc_bytes), 0xff,
147762306a36Sopenharmony_ci				       ecc_bytes);
147862306a36Sopenharmony_ci			else
147962306a36Sopenharmony_ci				ret = _omap_calculate_ecc_bch(mtd,
148062306a36Sopenharmony_ci							      bufoffs + (step * ecc_size),
148162306a36Sopenharmony_ci							      ecc_calc + (step * ecc_bytes),
148262306a36Sopenharmony_ci							      step);
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci			if (ret)
148562306a36Sopenharmony_ci				return ret;
148662306a36Sopenharmony_ci		}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci		/*
148962306a36Sopenharmony_ci		 * Copy the calculated ECC for the whole page including the
149062306a36Sopenharmony_ci		 * masked values (0xFF) corresponding to unwritten subpages.
149162306a36Sopenharmony_ci		 */
149262306a36Sopenharmony_ci		ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
149362306a36Sopenharmony_ci						 eccpg * info->eccpg_bytes,
149462306a36Sopenharmony_ci						 info->eccpg_bytes);
149562306a36Sopenharmony_ci		if (ret)
149662306a36Sopenharmony_ci			return ret;
149762306a36Sopenharmony_ci	}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	/* write OOB buffer to NAND device */
150062306a36Sopenharmony_ci	info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	return nand_prog_page_end_op(chip);
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci/**
150662306a36Sopenharmony_ci * omap_read_page_bch - BCH ecc based page read function for entire page
150762306a36Sopenharmony_ci * @chip:		nand chip info structure
150862306a36Sopenharmony_ci * @buf:		buffer to store read data
150962306a36Sopenharmony_ci * @oob_required:	caller requires OOB data read to chip->oob_poi
151062306a36Sopenharmony_ci * @page:		page number to read
151162306a36Sopenharmony_ci *
151262306a36Sopenharmony_ci * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module
151362306a36Sopenharmony_ci * used for error correction.
151462306a36Sopenharmony_ci * Custom method evolved to support ELM error correction & multi sector
151562306a36Sopenharmony_ci * reading. On reading page data area is read along with OOB data with
151662306a36Sopenharmony_ci * ecc engine enabled. ecc vector updated after read of OOB data.
151762306a36Sopenharmony_ci * For non error pages ecc vector reported as zero.
151862306a36Sopenharmony_ci */
151962306a36Sopenharmony_cistatic int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
152062306a36Sopenharmony_ci			      int oob_required, int page)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
152362306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
152462306a36Sopenharmony_ci	uint8_t *ecc_calc = chip->ecc.calc_buf;
152562306a36Sopenharmony_ci	uint8_t *ecc_code = chip->ecc.code_buf;
152662306a36Sopenharmony_ci	unsigned int max_bitflips = 0, eccpg;
152762306a36Sopenharmony_ci	int stat, ret;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	ret = nand_read_page_op(chip, page, 0, NULL, 0);
153062306a36Sopenharmony_ci	if (ret)
153162306a36Sopenharmony_ci		return ret;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
153462306a36Sopenharmony_ci		/* Enable GPMC ecc engine */
153562306a36Sopenharmony_ci		chip->ecc.hwctl(chip, NAND_ECC_READ);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci		/* Read data */
153862306a36Sopenharmony_ci		ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size,
153962306a36Sopenharmony_ci						 buf + (eccpg * info->eccpg_size),
154062306a36Sopenharmony_ci						 info->eccpg_size, false);
154162306a36Sopenharmony_ci		if (ret)
154262306a36Sopenharmony_ci			return ret;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci		/* Read oob bytes */
154562306a36Sopenharmony_ci		ret = nand_change_read_column_op(chip,
154662306a36Sopenharmony_ci						 mtd->writesize + BBM_LEN +
154762306a36Sopenharmony_ci						 (eccpg * info->eccpg_bytes),
154862306a36Sopenharmony_ci						 chip->oob_poi + BBM_LEN +
154962306a36Sopenharmony_ci						 (eccpg * info->eccpg_bytes),
155062306a36Sopenharmony_ci						 info->eccpg_bytes, false);
155162306a36Sopenharmony_ci		if (ret)
155262306a36Sopenharmony_ci			return ret;
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci		/* Calculate ecc bytes */
155562306a36Sopenharmony_ci		ret = omap_calculate_ecc_bch_multi(mtd,
155662306a36Sopenharmony_ci						   buf + (eccpg * info->eccpg_size),
155762306a36Sopenharmony_ci						   ecc_calc);
155862306a36Sopenharmony_ci		if (ret)
155962306a36Sopenharmony_ci			return ret;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci		ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code,
156262306a36Sopenharmony_ci						 chip->oob_poi,
156362306a36Sopenharmony_ci						 eccpg * info->eccpg_bytes,
156462306a36Sopenharmony_ci						 info->eccpg_bytes);
156562306a36Sopenharmony_ci		if (ret)
156662306a36Sopenharmony_ci			return ret;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		stat = chip->ecc.correct(chip,
156962306a36Sopenharmony_ci					 buf + (eccpg * info->eccpg_size),
157062306a36Sopenharmony_ci					 ecc_code, ecc_calc);
157162306a36Sopenharmony_ci		if (stat < 0) {
157262306a36Sopenharmony_ci			mtd->ecc_stats.failed++;
157362306a36Sopenharmony_ci		} else {
157462306a36Sopenharmony_ci			mtd->ecc_stats.corrected += stat;
157562306a36Sopenharmony_ci			max_bitflips = max_t(unsigned int, max_bitflips, stat);
157662306a36Sopenharmony_ci		}
157762306a36Sopenharmony_ci	}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	return max_bitflips;
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci/**
158362306a36Sopenharmony_ci * is_elm_present - checks for presence of ELM module by scanning DT nodes
158462306a36Sopenharmony_ci * @info: NAND device structure containing platform data
158562306a36Sopenharmony_ci * @elm_node: ELM's DT node
158662306a36Sopenharmony_ci */
158762306a36Sopenharmony_cistatic bool is_elm_present(struct omap_nand_info *info,
158862306a36Sopenharmony_ci			   struct device_node *elm_node)
158962306a36Sopenharmony_ci{
159062306a36Sopenharmony_ci	struct platform_device *pdev;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	/* check whether elm-id is passed via DT */
159362306a36Sopenharmony_ci	if (!elm_node) {
159462306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "ELM devicetree node not found\n");
159562306a36Sopenharmony_ci		return false;
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci	pdev = of_find_device_by_node(elm_node);
159862306a36Sopenharmony_ci	/* check whether ELM device is registered */
159962306a36Sopenharmony_ci	if (!pdev) {
160062306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "ELM device not found\n");
160162306a36Sopenharmony_ci		return false;
160262306a36Sopenharmony_ci	}
160362306a36Sopenharmony_ci	/* ELM module available, now configure it */
160462306a36Sopenharmony_ci	info->elm_dev = &pdev->dev;
160562306a36Sopenharmony_ci	return true;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic bool omap2_nand_ecc_check(struct omap_nand_info *info)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	switch (info->ecc_opt) {
161362306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
161462306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
161562306a36Sopenharmony_ci		ecc_needs_omap_bch = false;
161662306a36Sopenharmony_ci		ecc_needs_bch = true;
161762306a36Sopenharmony_ci		ecc_needs_elm = false;
161862306a36Sopenharmony_ci		break;
161962306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW:
162062306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW:
162162306a36Sopenharmony_ci	case OMAP_ECC_BCH16_CODE_HW:
162262306a36Sopenharmony_ci		ecc_needs_omap_bch = true;
162362306a36Sopenharmony_ci		ecc_needs_bch = false;
162462306a36Sopenharmony_ci		ecc_needs_elm = true;
162562306a36Sopenharmony_ci		break;
162662306a36Sopenharmony_ci	default:
162762306a36Sopenharmony_ci		ecc_needs_omap_bch = false;
162862306a36Sopenharmony_ci		ecc_needs_bch = false;
162962306a36Sopenharmony_ci		ecc_needs_elm = false;
163062306a36Sopenharmony_ci		break;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) {
163462306a36Sopenharmony_ci		dev_err(&info->pdev->dev,
163562306a36Sopenharmony_ci			"CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n");
163662306a36Sopenharmony_ci		return false;
163762306a36Sopenharmony_ci	}
163862306a36Sopenharmony_ci	if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) {
163962306a36Sopenharmony_ci		dev_err(&info->pdev->dev,
164062306a36Sopenharmony_ci			"CONFIG_MTD_NAND_OMAP_BCH not enabled\n");
164162306a36Sopenharmony_ci		return false;
164262306a36Sopenharmony_ci	}
164362306a36Sopenharmony_ci	if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) {
164462306a36Sopenharmony_ci		dev_err(&info->pdev->dev, "ELM not available\n");
164562306a36Sopenharmony_ci		return false;
164662306a36Sopenharmony_ci	}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	return true;
164962306a36Sopenharmony_ci}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_cistatic const char * const nand_xfer_types[] = {
165262306a36Sopenharmony_ci	[NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled",
165362306a36Sopenharmony_ci	[NAND_OMAP_POLLED] = "polled",
165462306a36Sopenharmony_ci	[NAND_OMAP_PREFETCH_DMA] = "prefetch-dma",
165562306a36Sopenharmony_ci	[NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq",
165662306a36Sopenharmony_ci};
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_cistatic int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
165962306a36Sopenharmony_ci{
166062306a36Sopenharmony_ci	struct device_node *child = dev->of_node;
166162306a36Sopenharmony_ci	int i;
166262306a36Sopenharmony_ci	const char *s;
166362306a36Sopenharmony_ci	u32 cs;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if (of_property_read_u32(child, "reg", &cs) < 0) {
166662306a36Sopenharmony_ci		dev_err(dev, "reg not found in DT\n");
166762306a36Sopenharmony_ci		return -EINVAL;
166862306a36Sopenharmony_ci	}
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	info->gpmc_cs = cs;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/* detect availability of ELM module. Won't be present pre-OMAP4 */
167362306a36Sopenharmony_ci	info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0);
167462306a36Sopenharmony_ci	if (!info->elm_of_node) {
167562306a36Sopenharmony_ci		info->elm_of_node = of_parse_phandle(child, "elm_id", 0);
167662306a36Sopenharmony_ci		if (!info->elm_of_node)
167762306a36Sopenharmony_ci			dev_dbg(dev, "ti,elm-id not in DT\n");
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	/* select ecc-scheme for NAND */
168162306a36Sopenharmony_ci	if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) {
168262306a36Sopenharmony_ci		dev_err(dev, "ti,nand-ecc-opt not found\n");
168362306a36Sopenharmony_ci		return -EINVAL;
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (!strcmp(s, "sw")) {
168762306a36Sopenharmony_ci		info->ecc_opt = OMAP_ECC_HAM1_CODE_SW;
168862306a36Sopenharmony_ci	} else if (!strcmp(s, "ham1") ||
168962306a36Sopenharmony_ci		   !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) {
169062306a36Sopenharmony_ci		info->ecc_opt =	OMAP_ECC_HAM1_CODE_HW;
169162306a36Sopenharmony_ci	} else if (!strcmp(s, "bch4")) {
169262306a36Sopenharmony_ci		if (info->elm_of_node)
169362306a36Sopenharmony_ci			info->ecc_opt = OMAP_ECC_BCH4_CODE_HW;
169462306a36Sopenharmony_ci		else
169562306a36Sopenharmony_ci			info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW;
169662306a36Sopenharmony_ci	} else if (!strcmp(s, "bch8")) {
169762306a36Sopenharmony_ci		if (info->elm_of_node)
169862306a36Sopenharmony_ci			info->ecc_opt = OMAP_ECC_BCH8_CODE_HW;
169962306a36Sopenharmony_ci		else
170062306a36Sopenharmony_ci			info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
170162306a36Sopenharmony_ci	} else if (!strcmp(s, "bch16")) {
170262306a36Sopenharmony_ci		info->ecc_opt =	OMAP_ECC_BCH16_CODE_HW;
170362306a36Sopenharmony_ci	} else {
170462306a36Sopenharmony_ci		dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n");
170562306a36Sopenharmony_ci		return -EINVAL;
170662306a36Sopenharmony_ci	}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	/* select data transfer mode */
170962306a36Sopenharmony_ci	if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) {
171062306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) {
171162306a36Sopenharmony_ci			if (!strcasecmp(s, nand_xfer_types[i])) {
171262306a36Sopenharmony_ci				info->xfer_type = i;
171362306a36Sopenharmony_ci				return 0;
171462306a36Sopenharmony_ci			}
171562306a36Sopenharmony_ci		}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		dev_err(dev, "unrecognized value for ti,nand-xfer-type\n");
171862306a36Sopenharmony_ci		return -EINVAL;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	return 0;
172262306a36Sopenharmony_ci}
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_cistatic int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
172562306a36Sopenharmony_ci			      struct mtd_oob_region *oobregion)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
172862306a36Sopenharmony_ci	struct nand_chip *chip = &info->nand;
172962306a36Sopenharmony_ci	int off = BBM_LEN;
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
173262306a36Sopenharmony_ci	    !(chip->options & NAND_BUSWIDTH_16))
173362306a36Sopenharmony_ci		off = 1;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	if (section)
173662306a36Sopenharmony_ci		return -ERANGE;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	oobregion->offset = off;
173962306a36Sopenharmony_ci	oobregion->length = chip->ecc.total;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	return 0;
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic int omap_ooblayout_free(struct mtd_info *mtd, int section,
174562306a36Sopenharmony_ci			       struct mtd_oob_region *oobregion)
174662306a36Sopenharmony_ci{
174762306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
174862306a36Sopenharmony_ci	struct nand_chip *chip = &info->nand;
174962306a36Sopenharmony_ci	int off = BBM_LEN;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
175262306a36Sopenharmony_ci	    !(chip->options & NAND_BUSWIDTH_16))
175362306a36Sopenharmony_ci		off = 1;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (section)
175662306a36Sopenharmony_ci		return -ERANGE;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	off += chip->ecc.total;
175962306a36Sopenharmony_ci	if (off >= mtd->oobsize)
176062306a36Sopenharmony_ci		return -ERANGE;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	oobregion->offset = off;
176362306a36Sopenharmony_ci	oobregion->length = mtd->oobsize - off;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	return 0;
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops omap_ooblayout_ops = {
176962306a36Sopenharmony_ci	.ecc = omap_ooblayout_ecc,
177062306a36Sopenharmony_ci	.free = omap_ooblayout_free,
177162306a36Sopenharmony_ci};
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
177462306a36Sopenharmony_ci				 struct mtd_oob_region *oobregion)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
177762306a36Sopenharmony_ci	unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
177862306a36Sopenharmony_ci	unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
177962306a36Sopenharmony_ci	int off = BBM_LEN;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	if (section >= nsteps)
178262306a36Sopenharmony_ci		return -ERANGE;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/*
178562306a36Sopenharmony_ci	 * When SW correction is employed, one OMAP specific marker byte is
178662306a36Sopenharmony_ci	 * reserved after each ECC step.
178762306a36Sopenharmony_ci	 */
178862306a36Sopenharmony_ci	oobregion->offset = off + (section * (ecc_bytes + 1));
178962306a36Sopenharmony_ci	oobregion->length = ecc_bytes;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	return 0;
179262306a36Sopenharmony_ci}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
179562306a36Sopenharmony_ci				  struct mtd_oob_region *oobregion)
179662306a36Sopenharmony_ci{
179762306a36Sopenharmony_ci	struct nand_device *nand = mtd_to_nanddev(mtd);
179862306a36Sopenharmony_ci	unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
179962306a36Sopenharmony_ci	unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
180062306a36Sopenharmony_ci	int off = BBM_LEN;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	if (section)
180362306a36Sopenharmony_ci		return -ERANGE;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	/*
180662306a36Sopenharmony_ci	 * When SW correction is employed, one OMAP specific marker byte is
180762306a36Sopenharmony_ci	 * reserved after each ECC step.
180862306a36Sopenharmony_ci	 */
180962306a36Sopenharmony_ci	off += ((ecc_bytes + 1) * nsteps);
181062306a36Sopenharmony_ci	if (off >= mtd->oobsize)
181162306a36Sopenharmony_ci		return -ERANGE;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	oobregion->offset = off;
181462306a36Sopenharmony_ci	oobregion->length = mtd->oobsize - off;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	return 0;
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = {
182062306a36Sopenharmony_ci	.ecc = omap_sw_ooblayout_ecc,
182162306a36Sopenharmony_ci	.free = omap_sw_ooblayout_free,
182262306a36Sopenharmony_ci};
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cistatic int omap_nand_attach_chip(struct nand_chip *chip)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	struct mtd_info *mtd = nand_to_mtd(chip);
182762306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
182862306a36Sopenharmony_ci	struct device *dev = &info->pdev->dev;
182962306a36Sopenharmony_ci	int min_oobbytes = BBM_LEN;
183062306a36Sopenharmony_ci	int elm_bch_strength = -1;
183162306a36Sopenharmony_ci	int oobbytes_per_step;
183262306a36Sopenharmony_ci	dma_cap_mask_t mask;
183362306a36Sopenharmony_ci	int err;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	if (chip->bbt_options & NAND_BBT_USE_FLASH)
183662306a36Sopenharmony_ci		chip->bbt_options |= NAND_BBT_NO_OOB;
183762306a36Sopenharmony_ci	else
183862306a36Sopenharmony_ci		chip->options |= NAND_SKIP_BBTSCAN;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	/* Re-populate low-level callbacks based on xfer modes */
184162306a36Sopenharmony_ci	switch (info->xfer_type) {
184262306a36Sopenharmony_ci	case NAND_OMAP_PREFETCH_POLLED:
184362306a36Sopenharmony_ci		info->data_in = omap_nand_data_in_pref;
184462306a36Sopenharmony_ci		info->data_out = omap_nand_data_out_pref;
184562306a36Sopenharmony_ci		break;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	case NAND_OMAP_POLLED:
184862306a36Sopenharmony_ci		/* Use nand_base defaults for {read,write}_buf */
184962306a36Sopenharmony_ci		break;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	case NAND_OMAP_PREFETCH_DMA:
185262306a36Sopenharmony_ci		dma_cap_zero(mask);
185362306a36Sopenharmony_ci		dma_cap_set(DMA_SLAVE, mask);
185462306a36Sopenharmony_ci		info->dma = dma_request_chan(dev->parent, "rxtx");
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci		if (IS_ERR(info->dma)) {
185762306a36Sopenharmony_ci			dev_err(dev, "DMA engine request failed\n");
185862306a36Sopenharmony_ci			return PTR_ERR(info->dma);
185962306a36Sopenharmony_ci		} else {
186062306a36Sopenharmony_ci			struct dma_slave_config cfg;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci			memset(&cfg, 0, sizeof(cfg));
186362306a36Sopenharmony_ci			cfg.src_addr = info->phys_base;
186462306a36Sopenharmony_ci			cfg.dst_addr = info->phys_base;
186562306a36Sopenharmony_ci			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
186662306a36Sopenharmony_ci			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
186762306a36Sopenharmony_ci			cfg.src_maxburst = 16;
186862306a36Sopenharmony_ci			cfg.dst_maxburst = 16;
186962306a36Sopenharmony_ci			err = dmaengine_slave_config(info->dma, &cfg);
187062306a36Sopenharmony_ci			if (err) {
187162306a36Sopenharmony_ci				dev_err(dev,
187262306a36Sopenharmony_ci					"DMA engine slave config failed: %d\n",
187362306a36Sopenharmony_ci					err);
187462306a36Sopenharmony_ci				return err;
187562306a36Sopenharmony_ci			}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci			info->data_in = omap_nand_data_in_dma_pref;
187862306a36Sopenharmony_ci			info->data_out = omap_nand_data_out_dma_pref;
187962306a36Sopenharmony_ci		}
188062306a36Sopenharmony_ci		break;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	case NAND_OMAP_PREFETCH_IRQ:
188362306a36Sopenharmony_ci		info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0);
188462306a36Sopenharmony_ci		if (info->gpmc_irq_fifo <= 0)
188562306a36Sopenharmony_ci			return -ENODEV;
188662306a36Sopenharmony_ci		err = devm_request_irq(dev, info->gpmc_irq_fifo,
188762306a36Sopenharmony_ci				       omap_nand_irq, IRQF_SHARED,
188862306a36Sopenharmony_ci				       "gpmc-nand-fifo", info);
188962306a36Sopenharmony_ci		if (err) {
189062306a36Sopenharmony_ci			dev_err(dev, "Requesting IRQ %d, error %d\n",
189162306a36Sopenharmony_ci				info->gpmc_irq_fifo, err);
189262306a36Sopenharmony_ci			info->gpmc_irq_fifo = 0;
189362306a36Sopenharmony_ci			return err;
189462306a36Sopenharmony_ci		}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci		info->gpmc_irq_count = platform_get_irq(info->pdev, 1);
189762306a36Sopenharmony_ci		if (info->gpmc_irq_count <= 0)
189862306a36Sopenharmony_ci			return -ENODEV;
189962306a36Sopenharmony_ci		err = devm_request_irq(dev, info->gpmc_irq_count,
190062306a36Sopenharmony_ci				       omap_nand_irq, IRQF_SHARED,
190162306a36Sopenharmony_ci				       "gpmc-nand-count", info);
190262306a36Sopenharmony_ci		if (err) {
190362306a36Sopenharmony_ci			dev_err(dev, "Requesting IRQ %d, error %d\n",
190462306a36Sopenharmony_ci				info->gpmc_irq_count, err);
190562306a36Sopenharmony_ci			info->gpmc_irq_count = 0;
190662306a36Sopenharmony_ci			return err;
190762306a36Sopenharmony_ci		}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci		info->data_in = omap_nand_data_in_irq_pref;
191062306a36Sopenharmony_ci		info->data_out = omap_nand_data_out_irq_pref;
191162306a36Sopenharmony_ci		break;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	default:
191462306a36Sopenharmony_ci		dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type);
191562306a36Sopenharmony_ci		return -EINVAL;
191662306a36Sopenharmony_ci	}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	if (!omap2_nand_ecc_check(info))
191962306a36Sopenharmony_ci		return -EINVAL;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	/*
192262306a36Sopenharmony_ci	 * Bail out earlier to let NAND_ECC_ENGINE_TYPE_SOFT code create its own
192362306a36Sopenharmony_ci	 * ooblayout instead of using ours.
192462306a36Sopenharmony_ci	 */
192562306a36Sopenharmony_ci	if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) {
192662306a36Sopenharmony_ci		chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
192762306a36Sopenharmony_ci		chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
192862306a36Sopenharmony_ci		return 0;
192962306a36Sopenharmony_ci	}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	/* Populate MTD interface based on ECC scheme */
193262306a36Sopenharmony_ci	switch (info->ecc_opt) {
193362306a36Sopenharmony_ci	case OMAP_ECC_HAM1_CODE_HW:
193462306a36Sopenharmony_ci		dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n");
193562306a36Sopenharmony_ci		chip->ecc.engine_type	= NAND_ECC_ENGINE_TYPE_ON_HOST;
193662306a36Sopenharmony_ci		chip->ecc.bytes		= 3;
193762306a36Sopenharmony_ci		chip->ecc.size		= 512;
193862306a36Sopenharmony_ci		chip->ecc.strength	= 1;
193962306a36Sopenharmony_ci		chip->ecc.calculate	= omap_calculate_ecc;
194062306a36Sopenharmony_ci		chip->ecc.hwctl		= omap_enable_hwecc;
194162306a36Sopenharmony_ci		chip->ecc.correct	= omap_correct_data;
194262306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
194362306a36Sopenharmony_ci		oobbytes_per_step	= chip->ecc.bytes;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci		if (!(chip->options & NAND_BUSWIDTH_16))
194662306a36Sopenharmony_ci			min_oobbytes	= 1;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci		break;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
195162306a36Sopenharmony_ci		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n");
195262306a36Sopenharmony_ci		chip->ecc.engine_type	= NAND_ECC_ENGINE_TYPE_ON_HOST;
195362306a36Sopenharmony_ci		chip->ecc.size		= 512;
195462306a36Sopenharmony_ci		chip->ecc.bytes		= 7;
195562306a36Sopenharmony_ci		chip->ecc.strength	= 4;
195662306a36Sopenharmony_ci		chip->ecc.hwctl		= omap_enable_hwecc_bch;
195762306a36Sopenharmony_ci		chip->ecc.correct	= rawnand_sw_bch_correct;
195862306a36Sopenharmony_ci		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
195962306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
196062306a36Sopenharmony_ci		/* Reserve one byte for the OMAP marker */
196162306a36Sopenharmony_ci		oobbytes_per_step	= chip->ecc.bytes + 1;
196262306a36Sopenharmony_ci		/* Software BCH library is used for locating errors */
196362306a36Sopenharmony_ci		err = rawnand_sw_bch_init(chip);
196462306a36Sopenharmony_ci		if (err) {
196562306a36Sopenharmony_ci			dev_err(dev, "Unable to use BCH library\n");
196662306a36Sopenharmony_ci			return err;
196762306a36Sopenharmony_ci		}
196862306a36Sopenharmony_ci		break;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	case OMAP_ECC_BCH4_CODE_HW:
197162306a36Sopenharmony_ci		pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n");
197262306a36Sopenharmony_ci		chip->ecc.engine_type	= NAND_ECC_ENGINE_TYPE_ON_HOST;
197362306a36Sopenharmony_ci		chip->ecc.size		= 512;
197462306a36Sopenharmony_ci		/* 14th bit is kept reserved for ROM-code compatibility */
197562306a36Sopenharmony_ci		chip->ecc.bytes		= 7 + 1;
197662306a36Sopenharmony_ci		chip->ecc.strength	= 4;
197762306a36Sopenharmony_ci		chip->ecc.hwctl		= omap_enable_hwecc_bch;
197862306a36Sopenharmony_ci		chip->ecc.correct	= omap_elm_correct_data;
197962306a36Sopenharmony_ci		chip->ecc.read_page	= omap_read_page_bch;
198062306a36Sopenharmony_ci		chip->ecc.write_page	= omap_write_page_bch;
198162306a36Sopenharmony_ci		chip->ecc.write_subpage	= omap_write_subpage_bch;
198262306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
198362306a36Sopenharmony_ci		oobbytes_per_step	= chip->ecc.bytes;
198462306a36Sopenharmony_ci		elm_bch_strength = BCH4_ECC;
198562306a36Sopenharmony_ci		break;
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
198862306a36Sopenharmony_ci		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
198962306a36Sopenharmony_ci		chip->ecc.engine_type	= NAND_ECC_ENGINE_TYPE_ON_HOST;
199062306a36Sopenharmony_ci		chip->ecc.size		= 512;
199162306a36Sopenharmony_ci		chip->ecc.bytes		= 13;
199262306a36Sopenharmony_ci		chip->ecc.strength	= 8;
199362306a36Sopenharmony_ci		chip->ecc.hwctl		= omap_enable_hwecc_bch;
199462306a36Sopenharmony_ci		chip->ecc.correct	= rawnand_sw_bch_correct;
199562306a36Sopenharmony_ci		chip->ecc.calculate	= omap_calculate_ecc_bch_sw;
199662306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops);
199762306a36Sopenharmony_ci		/* Reserve one byte for the OMAP marker */
199862306a36Sopenharmony_ci		oobbytes_per_step	= chip->ecc.bytes + 1;
199962306a36Sopenharmony_ci		/* Software BCH library is used for locating errors */
200062306a36Sopenharmony_ci		err = rawnand_sw_bch_init(chip);
200162306a36Sopenharmony_ci		if (err) {
200262306a36Sopenharmony_ci			dev_err(dev, "unable to use BCH library\n");
200362306a36Sopenharmony_ci			return err;
200462306a36Sopenharmony_ci		}
200562306a36Sopenharmony_ci		break;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	case OMAP_ECC_BCH8_CODE_HW:
200862306a36Sopenharmony_ci		pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n");
200962306a36Sopenharmony_ci		chip->ecc.engine_type	= NAND_ECC_ENGINE_TYPE_ON_HOST;
201062306a36Sopenharmony_ci		chip->ecc.size		= 512;
201162306a36Sopenharmony_ci		/* 14th bit is kept reserved for ROM-code compatibility */
201262306a36Sopenharmony_ci		chip->ecc.bytes		= 13 + 1;
201362306a36Sopenharmony_ci		chip->ecc.strength	= 8;
201462306a36Sopenharmony_ci		chip->ecc.hwctl		= omap_enable_hwecc_bch;
201562306a36Sopenharmony_ci		chip->ecc.correct	= omap_elm_correct_data;
201662306a36Sopenharmony_ci		chip->ecc.read_page	= omap_read_page_bch;
201762306a36Sopenharmony_ci		chip->ecc.write_page	= omap_write_page_bch;
201862306a36Sopenharmony_ci		chip->ecc.write_subpage	= omap_write_subpage_bch;
201962306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
202062306a36Sopenharmony_ci		oobbytes_per_step	= chip->ecc.bytes;
202162306a36Sopenharmony_ci		elm_bch_strength = BCH8_ECC;
202262306a36Sopenharmony_ci		break;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	case OMAP_ECC_BCH16_CODE_HW:
202562306a36Sopenharmony_ci		pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n");
202662306a36Sopenharmony_ci		chip->ecc.engine_type	= NAND_ECC_ENGINE_TYPE_ON_HOST;
202762306a36Sopenharmony_ci		chip->ecc.size		= 512;
202862306a36Sopenharmony_ci		chip->ecc.bytes		= 26;
202962306a36Sopenharmony_ci		chip->ecc.strength	= 16;
203062306a36Sopenharmony_ci		chip->ecc.hwctl		= omap_enable_hwecc_bch;
203162306a36Sopenharmony_ci		chip->ecc.correct	= omap_elm_correct_data;
203262306a36Sopenharmony_ci		chip->ecc.read_page	= omap_read_page_bch;
203362306a36Sopenharmony_ci		chip->ecc.write_page	= omap_write_page_bch;
203462306a36Sopenharmony_ci		chip->ecc.write_subpage	= omap_write_subpage_bch;
203562306a36Sopenharmony_ci		mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
203662306a36Sopenharmony_ci		oobbytes_per_step	= chip->ecc.bytes;
203762306a36Sopenharmony_ci		elm_bch_strength = BCH16_ECC;
203862306a36Sopenharmony_ci		break;
203962306a36Sopenharmony_ci	default:
204062306a36Sopenharmony_ci		dev_err(dev, "Invalid or unsupported ECC scheme\n");
204162306a36Sopenharmony_ci		return -EINVAL;
204262306a36Sopenharmony_ci	}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	if (elm_bch_strength >= 0) {
204562306a36Sopenharmony_ci		chip->ecc.steps = mtd->writesize / chip->ecc.size;
204662306a36Sopenharmony_ci		info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX;
204762306a36Sopenharmony_ci		if (info->neccpg) {
204862306a36Sopenharmony_ci			info->nsteps_per_eccpg = ERROR_VECTOR_MAX;
204962306a36Sopenharmony_ci		} else {
205062306a36Sopenharmony_ci			info->neccpg = 1;
205162306a36Sopenharmony_ci			info->nsteps_per_eccpg = chip->ecc.steps;
205262306a36Sopenharmony_ci		}
205362306a36Sopenharmony_ci		info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size;
205462306a36Sopenharmony_ci		info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci		err = elm_config(info->elm_dev, elm_bch_strength,
205762306a36Sopenharmony_ci				 info->nsteps_per_eccpg, chip->ecc.size,
205862306a36Sopenharmony_ci				 chip->ecc.bytes);
205962306a36Sopenharmony_ci		if (err < 0)
206062306a36Sopenharmony_ci			return err;
206162306a36Sopenharmony_ci	}
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	/* Check if NAND device's OOB is enough to store ECC signatures */
206462306a36Sopenharmony_ci	min_oobbytes += (oobbytes_per_step *
206562306a36Sopenharmony_ci			 (mtd->writesize / chip->ecc.size));
206662306a36Sopenharmony_ci	if (mtd->oobsize < min_oobbytes) {
206762306a36Sopenharmony_ci		dev_err(dev,
206862306a36Sopenharmony_ci			"Not enough OOB bytes: required = %d, available=%d\n",
206962306a36Sopenharmony_ci			min_oobbytes, mtd->oobsize);
207062306a36Sopenharmony_ci		return -EINVAL;
207162306a36Sopenharmony_ci	}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	return 0;
207462306a36Sopenharmony_ci}
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_cistatic void omap_nand_data_in(struct nand_chip *chip, void *buf,
207762306a36Sopenharmony_ci			      unsigned int len, bool force_8bit)
207862306a36Sopenharmony_ci{
207962306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
208062306a36Sopenharmony_ci	u32 alignment = ((uintptr_t)buf | len) & 3;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (force_8bit || (alignment & 1))
208362306a36Sopenharmony_ci		ioread8_rep(info->fifo, buf, len);
208462306a36Sopenharmony_ci	else if (alignment & 3)
208562306a36Sopenharmony_ci		ioread16_rep(info->fifo, buf, len >> 1);
208662306a36Sopenharmony_ci	else
208762306a36Sopenharmony_ci		ioread32_rep(info->fifo, buf, len >> 2);
208862306a36Sopenharmony_ci}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_cistatic void omap_nand_data_out(struct nand_chip *chip,
209162306a36Sopenharmony_ci			       const void *buf, unsigned int len,
209262306a36Sopenharmony_ci			       bool force_8bit)
209362306a36Sopenharmony_ci{
209462306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
209562306a36Sopenharmony_ci	u32 alignment = ((uintptr_t)buf | len) & 3;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	if (force_8bit || (alignment & 1))
209862306a36Sopenharmony_ci		iowrite8_rep(info->fifo, buf, len);
209962306a36Sopenharmony_ci	else if (alignment & 3)
210062306a36Sopenharmony_ci		iowrite16_rep(info->fifo, buf, len >> 1);
210162306a36Sopenharmony_ci	else
210262306a36Sopenharmony_ci		iowrite32_rep(info->fifo, buf, len >> 2);
210362306a36Sopenharmony_ci}
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_cistatic int omap_nand_exec_instr(struct nand_chip *chip,
210662306a36Sopenharmony_ci				const struct nand_op_instr *instr)
210762306a36Sopenharmony_ci{
210862306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
210962306a36Sopenharmony_ci	unsigned int i;
211062306a36Sopenharmony_ci	int ret;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	switch (instr->type) {
211362306a36Sopenharmony_ci	case NAND_OP_CMD_INSTR:
211462306a36Sopenharmony_ci		iowrite8(instr->ctx.cmd.opcode,
211562306a36Sopenharmony_ci			 info->reg.gpmc_nand_command);
211662306a36Sopenharmony_ci		break;
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	case NAND_OP_ADDR_INSTR:
211962306a36Sopenharmony_ci		for (i = 0; i < instr->ctx.addr.naddrs; i++) {
212062306a36Sopenharmony_ci			iowrite8(instr->ctx.addr.addrs[i],
212162306a36Sopenharmony_ci				 info->reg.gpmc_nand_address);
212262306a36Sopenharmony_ci		}
212362306a36Sopenharmony_ci		break;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	case NAND_OP_DATA_IN_INSTR:
212662306a36Sopenharmony_ci		info->data_in(chip, instr->ctx.data.buf.in,
212762306a36Sopenharmony_ci			      instr->ctx.data.len,
212862306a36Sopenharmony_ci			      instr->ctx.data.force_8bit);
212962306a36Sopenharmony_ci		break;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	case NAND_OP_DATA_OUT_INSTR:
213262306a36Sopenharmony_ci		info->data_out(chip, instr->ctx.data.buf.out,
213362306a36Sopenharmony_ci			       instr->ctx.data.len,
213462306a36Sopenharmony_ci			       instr->ctx.data.force_8bit);
213562306a36Sopenharmony_ci		break;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	case NAND_OP_WAITRDY_INSTR:
213862306a36Sopenharmony_ci		ret = info->ready_gpiod ?
213962306a36Sopenharmony_ci			nand_gpio_waitrdy(chip, info->ready_gpiod, instr->ctx.waitrdy.timeout_ms) :
214062306a36Sopenharmony_ci			nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
214162306a36Sopenharmony_ci		if (ret)
214262306a36Sopenharmony_ci			return ret;
214362306a36Sopenharmony_ci		break;
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci	if (instr->delay_ns)
214762306a36Sopenharmony_ci		ndelay(instr->delay_ns);
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	return 0;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_cistatic int omap_nand_exec_op(struct nand_chip *chip,
215362306a36Sopenharmony_ci			     const struct nand_operation *op,
215462306a36Sopenharmony_ci			     bool check_only)
215562306a36Sopenharmony_ci{
215662306a36Sopenharmony_ci	unsigned int i;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	if (check_only)
215962306a36Sopenharmony_ci		return 0;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	for (i = 0; i < op->ninstrs; i++) {
216262306a36Sopenharmony_ci		int ret;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci		ret = omap_nand_exec_instr(chip, &op->instrs[i]);
216562306a36Sopenharmony_ci		if (ret)
216662306a36Sopenharmony_ci			return ret;
216762306a36Sopenharmony_ci	}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	return 0;
217062306a36Sopenharmony_ci}
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_cistatic const struct nand_controller_ops omap_nand_controller_ops = {
217362306a36Sopenharmony_ci	.attach_chip = omap_nand_attach_chip,
217462306a36Sopenharmony_ci	.exec_op = omap_nand_exec_op,
217562306a36Sopenharmony_ci};
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci/* Shared among all NAND instances to synchronize access to the ECC Engine */
217862306a36Sopenharmony_cistatic struct nand_controller omap_gpmc_controller;
217962306a36Sopenharmony_cistatic bool omap_gpmc_controller_initialized;
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic int omap_nand_probe(struct platform_device *pdev)
218262306a36Sopenharmony_ci{
218362306a36Sopenharmony_ci	struct omap_nand_info		*info;
218462306a36Sopenharmony_ci	struct mtd_info			*mtd;
218562306a36Sopenharmony_ci	struct nand_chip		*nand_chip;
218662306a36Sopenharmony_ci	int				err;
218762306a36Sopenharmony_ci	struct resource			*res;
218862306a36Sopenharmony_ci	struct device			*dev = &pdev->dev;
218962306a36Sopenharmony_ci	void __iomem *vaddr;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
219262306a36Sopenharmony_ci				GFP_KERNEL);
219362306a36Sopenharmony_ci	if (!info)
219462306a36Sopenharmony_ci		return -ENOMEM;
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	info->pdev = pdev;
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	err = omap_get_dt_info(dev, info);
219962306a36Sopenharmony_ci	if (err)
220062306a36Sopenharmony_ci		return err;
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_ci	info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs);
220362306a36Sopenharmony_ci	if (!info->ops) {
220462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n");
220562306a36Sopenharmony_ci		return -ENODEV;
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	nand_chip		= &info->nand;
220962306a36Sopenharmony_ci	mtd			= nand_to_mtd(nand_chip);
221062306a36Sopenharmony_ci	mtd->dev.parent		= &pdev->dev;
221162306a36Sopenharmony_ci	nand_set_flash_node(nand_chip, dev->of_node);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	if (!mtd->name) {
221462306a36Sopenharmony_ci		mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
221562306a36Sopenharmony_ci					   "omap2-nand.%d", info->gpmc_cs);
221662306a36Sopenharmony_ci		if (!mtd->name) {
221762306a36Sopenharmony_ci			dev_err(&pdev->dev, "Failed to set MTD name\n");
221862306a36Sopenharmony_ci			return -ENOMEM;
221962306a36Sopenharmony_ci		}
222062306a36Sopenharmony_ci	}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	vaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
222362306a36Sopenharmony_ci	if (IS_ERR(vaddr))
222462306a36Sopenharmony_ci		return PTR_ERR(vaddr);
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci	info->fifo = vaddr;
222762306a36Sopenharmony_ci	info->phys_base = res->start;
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	if (!omap_gpmc_controller_initialized) {
223062306a36Sopenharmony_ci		omap_gpmc_controller.ops = &omap_nand_controller_ops;
223162306a36Sopenharmony_ci		nand_controller_init(&omap_gpmc_controller);
223262306a36Sopenharmony_ci		omap_gpmc_controller_initialized = true;
223362306a36Sopenharmony_ci	}
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	nand_chip->controller = &omap_gpmc_controller;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
223862306a36Sopenharmony_ci						    GPIOD_IN);
223962306a36Sopenharmony_ci	if (IS_ERR(info->ready_gpiod)) {
224062306a36Sopenharmony_ci		dev_err(dev, "failed to get ready gpio\n");
224162306a36Sopenharmony_ci		return PTR_ERR(info->ready_gpiod);
224262306a36Sopenharmony_ci	}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	if (info->flash_bbt)
224562306a36Sopenharmony_ci		nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	/* default operations */
224862306a36Sopenharmony_ci	info->data_in = omap_nand_data_in;
224962306a36Sopenharmony_ci	info->data_out = omap_nand_data_out;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	err = nand_scan(nand_chip, 1);
225262306a36Sopenharmony_ci	if (err)
225362306a36Sopenharmony_ci		goto return_error;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	err = mtd_device_register(mtd, NULL, 0);
225662306a36Sopenharmony_ci	if (err)
225762306a36Sopenharmony_ci		goto cleanup_nand;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	platform_set_drvdata(pdev, mtd);
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	return 0;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_cicleanup_nand:
226462306a36Sopenharmony_ci	nand_cleanup(nand_chip);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_cireturn_error:
226762306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(info->dma))
226862306a36Sopenharmony_ci		dma_release_channel(info->dma);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	rawnand_sw_bch_cleanup(nand_chip);
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	return err;
227362306a36Sopenharmony_ci}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_cistatic void omap_nand_remove(struct platform_device *pdev)
227662306a36Sopenharmony_ci{
227762306a36Sopenharmony_ci	struct mtd_info *mtd = platform_get_drvdata(pdev);
227862306a36Sopenharmony_ci	struct nand_chip *nand_chip = mtd_to_nand(mtd);
227962306a36Sopenharmony_ci	struct omap_nand_info *info = mtd_to_omap(mtd);
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	rawnand_sw_bch_cleanup(nand_chip);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	if (info->dma)
228462306a36Sopenharmony_ci		dma_release_channel(info->dma);
228562306a36Sopenharmony_ci	WARN_ON(mtd_device_unregister(mtd));
228662306a36Sopenharmony_ci	nand_cleanup(nand_chip);
228762306a36Sopenharmony_ci}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
229062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_nand_ids);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_cistatic struct platform_driver omap_nand_driver = {
229362306a36Sopenharmony_ci	.probe		= omap_nand_probe,
229462306a36Sopenharmony_ci	.remove_new	= omap_nand_remove,
229562306a36Sopenharmony_ci	.driver		= {
229662306a36Sopenharmony_ci		.name	= DRIVER_NAME,
229762306a36Sopenharmony_ci		.of_match_table = omap_nand_ids,
229862306a36Sopenharmony_ci	},
229962306a36Sopenharmony_ci};
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_cimodule_platform_driver(omap_nand_driver);
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME);
230462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
230562306a36Sopenharmony_ciMODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
2306