11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
293db446aSBoris Brezillon /*
393db446aSBoris Brezillon  * Copyright © 2010-2015 Broadcom Corporation
493db446aSBoris Brezillon  */
593db446aSBoris Brezillon 
693db446aSBoris Brezillon #include <linux/clk.h>
793db446aSBoris Brezillon #include <linux/module.h>
893db446aSBoris Brezillon #include <linux/init.h>
993db446aSBoris Brezillon #include <linux/delay.h>
1093db446aSBoris Brezillon #include <linux/device.h>
1193db446aSBoris Brezillon #include <linux/platform_device.h>
1293db446aSBoris Brezillon #include <linux/err.h>
1393db446aSBoris Brezillon #include <linux/completion.h>
1493db446aSBoris Brezillon #include <linux/interrupt.h>
1593db446aSBoris Brezillon #include <linux/spinlock.h>
1693db446aSBoris Brezillon #include <linux/dma-mapping.h>
1793db446aSBoris Brezillon #include <linux/ioport.h>
1893db446aSBoris Brezillon #include <linux/bug.h>
1993db446aSBoris Brezillon #include <linux/kernel.h>
2093db446aSBoris Brezillon #include <linux/bitops.h>
2193db446aSBoris Brezillon #include <linux/mm.h>
2293db446aSBoris Brezillon #include <linux/mtd/mtd.h>
2393db446aSBoris Brezillon #include <linux/mtd/rawnand.h>
2493db446aSBoris Brezillon #include <linux/mtd/partitions.h>
2593db446aSBoris Brezillon #include <linux/of.h>
2693db446aSBoris Brezillon #include <linux/of_platform.h>
2793db446aSBoris Brezillon #include <linux/slab.h>
2893db446aSBoris Brezillon #include <linux/list.h>
2993db446aSBoris Brezillon #include <linux/log2.h>
3093db446aSBoris Brezillon 
3193db446aSBoris Brezillon #include "brcmnand.h"
3293db446aSBoris Brezillon 
3393db446aSBoris Brezillon /*
3493db446aSBoris Brezillon  * This flag controls if WP stays on between erase/write commands to mitigate
3593db446aSBoris Brezillon  * flash corruption due to power glitches. Values:
3693db446aSBoris Brezillon  * 0: NAND_WP is not used or not available
3793db446aSBoris Brezillon  * 1: NAND_WP is set by default, cleared for erase/write operations
3893db446aSBoris Brezillon  * 2: NAND_WP is always cleared
3993db446aSBoris Brezillon  */
4093db446aSBoris Brezillon static int wp_on = 1;
4193db446aSBoris Brezillon module_param(wp_on, int, 0444);
4293db446aSBoris Brezillon 
4393db446aSBoris Brezillon /***********************************************************************
4493db446aSBoris Brezillon  * Definitions
4593db446aSBoris Brezillon  ***********************************************************************/
4693db446aSBoris Brezillon 
4793db446aSBoris Brezillon #define DRV_NAME			"brcmnand"
4893db446aSBoris Brezillon 
4993db446aSBoris Brezillon #define CMD_NULL			0x00
5093db446aSBoris Brezillon #define CMD_PAGE_READ			0x01
5193db446aSBoris Brezillon #define CMD_SPARE_AREA_READ		0x02
5293db446aSBoris Brezillon #define CMD_STATUS_READ			0x03
5393db446aSBoris Brezillon #define CMD_PROGRAM_PAGE		0x04
5493db446aSBoris Brezillon #define CMD_PROGRAM_SPARE_AREA		0x05
5593db446aSBoris Brezillon #define CMD_COPY_BACK			0x06
5693db446aSBoris Brezillon #define CMD_DEVICE_ID_READ		0x07
5793db446aSBoris Brezillon #define CMD_BLOCK_ERASE			0x08
5893db446aSBoris Brezillon #define CMD_FLASH_RESET			0x09
5993db446aSBoris Brezillon #define CMD_BLOCKS_LOCK			0x0a
6093db446aSBoris Brezillon #define CMD_BLOCKS_LOCK_DOWN		0x0b
6193db446aSBoris Brezillon #define CMD_BLOCKS_UNLOCK		0x0c
6293db446aSBoris Brezillon #define CMD_READ_BLOCKS_LOCK_STATUS	0x0d
6393db446aSBoris Brezillon #define CMD_PARAMETER_READ		0x0e
6493db446aSBoris Brezillon #define CMD_PARAMETER_CHANGE_COL	0x0f
6593db446aSBoris Brezillon #define CMD_LOW_LEVEL_OP		0x10
6693db446aSBoris Brezillon 
6793db446aSBoris Brezillon struct brcm_nand_dma_desc {
6893db446aSBoris Brezillon 	u32 next_desc;
6993db446aSBoris Brezillon 	u32 next_desc_ext;
7093db446aSBoris Brezillon 	u32 cmd_irq;
7193db446aSBoris Brezillon 	u32 dram_addr;
7293db446aSBoris Brezillon 	u32 dram_addr_ext;
7393db446aSBoris Brezillon 	u32 tfr_len;
7493db446aSBoris Brezillon 	u32 total_len;
7593db446aSBoris Brezillon 	u32 flash_addr;
7693db446aSBoris Brezillon 	u32 flash_addr_ext;
7793db446aSBoris Brezillon 	u32 cs;
7893db446aSBoris Brezillon 	u32 pad2[5];
7993db446aSBoris Brezillon 	u32 status_valid;
8093db446aSBoris Brezillon } __packed;
8193db446aSBoris Brezillon 
8293db446aSBoris Brezillon /* Bitfields for brcm_nand_dma_desc::status_valid */
8393db446aSBoris Brezillon #define FLASH_DMA_ECC_ERROR	(1 << 8)
8493db446aSBoris Brezillon #define FLASH_DMA_CORR_ERROR	(1 << 9)
8593db446aSBoris Brezillon 
860c06da57SKamal Dasu /* Bitfields for DMA_MODE */
870c06da57SKamal Dasu #define FLASH_DMA_MODE_STOP_ON_ERROR	BIT(1) /* stop in Uncorr ECC error */
880c06da57SKamal Dasu #define FLASH_DMA_MODE_MODE		BIT(0) /* link list */
890c06da57SKamal Dasu #define FLASH_DMA_MODE_MASK		(FLASH_DMA_MODE_STOP_ON_ERROR |	\
900c06da57SKamal Dasu 						FLASH_DMA_MODE_MODE)
910c06da57SKamal Dasu 
9293db446aSBoris Brezillon /* 512B flash cache in the NAND controller HW */
9393db446aSBoris Brezillon #define FC_SHIFT		9U
9493db446aSBoris Brezillon #define FC_BYTES		512U
9593db446aSBoris Brezillon #define FC_WORDS		(FC_BYTES >> 2)
9693db446aSBoris Brezillon 
9793db446aSBoris Brezillon #define BRCMNAND_MIN_PAGESIZE	512
9893db446aSBoris Brezillon #define BRCMNAND_MIN_BLOCKSIZE	(8 * 1024)
9993db446aSBoris Brezillon #define BRCMNAND_MIN_DEVSIZE	(4ULL * 1024 * 1024)
10093db446aSBoris Brezillon 
10193db446aSBoris Brezillon #define NAND_CTRL_RDY			(INTFC_CTLR_READY | INTFC_FLASH_READY)
10293db446aSBoris Brezillon #define NAND_POLL_STATUS_TIMEOUT_MS	100
10393db446aSBoris Brezillon 
104a5d53ad2SKamal Dasu #define EDU_CMD_WRITE          0x00
105a5d53ad2SKamal Dasu #define EDU_CMD_READ           0x01
106a5d53ad2SKamal Dasu #define EDU_STATUS_ACTIVE      BIT(0)
107a5d53ad2SKamal Dasu #define EDU_ERR_STATUS_ERRACK  BIT(0)
108a5d53ad2SKamal Dasu #define EDU_DONE_MASK		GENMASK(1, 0)
109a5d53ad2SKamal Dasu 
110a5d53ad2SKamal Dasu #define EDU_CONFIG_MODE_NAND   BIT(0)
111a5d53ad2SKamal Dasu #define EDU_CONFIG_SWAP_BYTE   BIT(1)
112a5d53ad2SKamal Dasu #ifdef CONFIG_CPU_BIG_ENDIAN
113a5d53ad2SKamal Dasu #define EDU_CONFIG_SWAP_CFG     EDU_CONFIG_SWAP_BYTE
114a5d53ad2SKamal Dasu #else
115a5d53ad2SKamal Dasu #define EDU_CONFIG_SWAP_CFG     0
116a5d53ad2SKamal Dasu #endif
117a5d53ad2SKamal Dasu 
118a5d53ad2SKamal Dasu /* edu registers */
119a5d53ad2SKamal Dasu enum edu_reg {
120a5d53ad2SKamal Dasu 	EDU_CONFIG = 0,
121a5d53ad2SKamal Dasu 	EDU_DRAM_ADDR,
122a5d53ad2SKamal Dasu 	EDU_EXT_ADDR,
123a5d53ad2SKamal Dasu 	EDU_LENGTH,
124a5d53ad2SKamal Dasu 	EDU_CMD,
125a5d53ad2SKamal Dasu 	EDU_STOP,
126a5d53ad2SKamal Dasu 	EDU_STATUS,
127a5d53ad2SKamal Dasu 	EDU_DONE,
128a5d53ad2SKamal Dasu 	EDU_ERR_STATUS,
129a5d53ad2SKamal Dasu };
130a5d53ad2SKamal Dasu 
131a5d53ad2SKamal Dasu static const u16  edu_regs[] = {
132a5d53ad2SKamal Dasu 	[EDU_CONFIG] = 0x00,
133a5d53ad2SKamal Dasu 	[EDU_DRAM_ADDR] = 0x04,
134a5d53ad2SKamal Dasu 	[EDU_EXT_ADDR] = 0x08,
135a5d53ad2SKamal Dasu 	[EDU_LENGTH] = 0x0c,
136a5d53ad2SKamal Dasu 	[EDU_CMD] = 0x10,
137a5d53ad2SKamal Dasu 	[EDU_STOP] = 0x14,
138a5d53ad2SKamal Dasu 	[EDU_STATUS] = 0x18,
139a5d53ad2SKamal Dasu 	[EDU_DONE] = 0x1c,
140a5d53ad2SKamal Dasu 	[EDU_ERR_STATUS] = 0x20,
141a5d53ad2SKamal Dasu };
142a5d53ad2SKamal Dasu 
1430c06da57SKamal Dasu /* flash_dma registers */
1440c06da57SKamal Dasu enum flash_dma_reg {
1450c06da57SKamal Dasu 	FLASH_DMA_REVISION = 0,
1460c06da57SKamal Dasu 	FLASH_DMA_FIRST_DESC,
1470c06da57SKamal Dasu 	FLASH_DMA_FIRST_DESC_EXT,
1480c06da57SKamal Dasu 	FLASH_DMA_CTRL,
1490c06da57SKamal Dasu 	FLASH_DMA_MODE,
1500c06da57SKamal Dasu 	FLASH_DMA_STATUS,
1510c06da57SKamal Dasu 	FLASH_DMA_INTERRUPT_DESC,
1520c06da57SKamal Dasu 	FLASH_DMA_INTERRUPT_DESC_EXT,
1530c06da57SKamal Dasu 	FLASH_DMA_ERROR_STATUS,
1540c06da57SKamal Dasu 	FLASH_DMA_CURRENT_DESC,
1550c06da57SKamal Dasu 	FLASH_DMA_CURRENT_DESC_EXT,
1560c06da57SKamal Dasu };
1570c06da57SKamal Dasu 
15883156c1cSKamal Dasu /* flash_dma registers v0*/
15983156c1cSKamal Dasu static const u16 flash_dma_regs_v0[] = {
16083156c1cSKamal Dasu 	[FLASH_DMA_REVISION]		= 0x00,
16183156c1cSKamal Dasu 	[FLASH_DMA_FIRST_DESC]		= 0x04,
16283156c1cSKamal Dasu 	[FLASH_DMA_CTRL]		= 0x08,
16383156c1cSKamal Dasu 	[FLASH_DMA_MODE]		= 0x0c,
16483156c1cSKamal Dasu 	[FLASH_DMA_STATUS]		= 0x10,
16583156c1cSKamal Dasu 	[FLASH_DMA_INTERRUPT_DESC]	= 0x14,
16683156c1cSKamal Dasu 	[FLASH_DMA_ERROR_STATUS]	= 0x18,
16783156c1cSKamal Dasu 	[FLASH_DMA_CURRENT_DESC]	= 0x1c,
16883156c1cSKamal Dasu };
16983156c1cSKamal Dasu 
1700c06da57SKamal Dasu /* flash_dma registers v1*/
1710c06da57SKamal Dasu static const u16 flash_dma_regs_v1[] = {
1720c06da57SKamal Dasu 	[FLASH_DMA_REVISION]		= 0x00,
1730c06da57SKamal Dasu 	[FLASH_DMA_FIRST_DESC]		= 0x04,
1740c06da57SKamal Dasu 	[FLASH_DMA_FIRST_DESC_EXT]	= 0x08,
1750c06da57SKamal Dasu 	[FLASH_DMA_CTRL]		= 0x0c,
1760c06da57SKamal Dasu 	[FLASH_DMA_MODE]		= 0x10,
1770c06da57SKamal Dasu 	[FLASH_DMA_STATUS]		= 0x14,
1780c06da57SKamal Dasu 	[FLASH_DMA_INTERRUPT_DESC]	= 0x18,
1790c06da57SKamal Dasu 	[FLASH_DMA_INTERRUPT_DESC_EXT]	= 0x1c,
1800c06da57SKamal Dasu 	[FLASH_DMA_ERROR_STATUS]	= 0x20,
1810c06da57SKamal Dasu 	[FLASH_DMA_CURRENT_DESC]	= 0x24,
1820c06da57SKamal Dasu 	[FLASH_DMA_CURRENT_DESC_EXT]	= 0x28,
1830c06da57SKamal Dasu };
1840c06da57SKamal Dasu 
1850c06da57SKamal Dasu /* flash_dma registers v4 */
1860c06da57SKamal Dasu static const u16 flash_dma_regs_v4[] = {
1870c06da57SKamal Dasu 	[FLASH_DMA_REVISION]		= 0x00,
1880c06da57SKamal Dasu 	[FLASH_DMA_FIRST_DESC]		= 0x08,
1890c06da57SKamal Dasu 	[FLASH_DMA_FIRST_DESC_EXT]	= 0x0c,
1900c06da57SKamal Dasu 	[FLASH_DMA_CTRL]		= 0x10,
1910c06da57SKamal Dasu 	[FLASH_DMA_MODE]		= 0x14,
1920c06da57SKamal Dasu 	[FLASH_DMA_STATUS]		= 0x18,
1930c06da57SKamal Dasu 	[FLASH_DMA_INTERRUPT_DESC]	= 0x20,
1940c06da57SKamal Dasu 	[FLASH_DMA_INTERRUPT_DESC_EXT]	= 0x24,
1950c06da57SKamal Dasu 	[FLASH_DMA_ERROR_STATUS]	= 0x28,
1960c06da57SKamal Dasu 	[FLASH_DMA_CURRENT_DESC]	= 0x30,
1970c06da57SKamal Dasu 	[FLASH_DMA_CURRENT_DESC_EXT]	= 0x34,
1980c06da57SKamal Dasu };
1990c06da57SKamal Dasu 
20093db446aSBoris Brezillon /* Controller feature flags */
20193db446aSBoris Brezillon enum {
20293db446aSBoris Brezillon 	BRCMNAND_HAS_1K_SECTORS			= BIT(0),
20393db446aSBoris Brezillon 	BRCMNAND_HAS_PREFETCH			= BIT(1),
20493db446aSBoris Brezillon 	BRCMNAND_HAS_CACHE_MODE			= BIT(2),
20593db446aSBoris Brezillon 	BRCMNAND_HAS_WP				= BIT(3),
20693db446aSBoris Brezillon };
20793db446aSBoris Brezillon 
208a5d53ad2SKamal Dasu struct brcmnand_host;
209a5d53ad2SKamal Dasu 
21093db446aSBoris Brezillon struct brcmnand_controller {
21193db446aSBoris Brezillon 	struct device		*dev;
2127da45139SMiquel Raynal 	struct nand_controller	controller;
21393db446aSBoris Brezillon 	void __iomem		*nand_base;
21493db446aSBoris Brezillon 	void __iomem		*nand_fc; /* flash cache */
21593db446aSBoris Brezillon 	void __iomem		*flash_dma_base;
21693db446aSBoris Brezillon 	unsigned int		irq;
21793db446aSBoris Brezillon 	unsigned int		dma_irq;
21893db446aSBoris Brezillon 	int			nand_version;
21993db446aSBoris Brezillon 
22093db446aSBoris Brezillon 	/* Some SoCs provide custom interrupt status register(s) */
22193db446aSBoris Brezillon 	struct brcmnand_soc	*soc;
22293db446aSBoris Brezillon 
22393db446aSBoris Brezillon 	/* Some SoCs have a gateable clock for the controller */
22493db446aSBoris Brezillon 	struct clk		*clk;
22593db446aSBoris Brezillon 
22693db446aSBoris Brezillon 	int			cmd_pending;
22793db446aSBoris Brezillon 	bool			dma_pending;
228a5d53ad2SKamal Dasu 	bool                    edu_pending;
22993db446aSBoris Brezillon 	struct completion	done;
23093db446aSBoris Brezillon 	struct completion	dma_done;
231a5d53ad2SKamal Dasu 	struct completion       edu_done;
23293db446aSBoris Brezillon 
23393db446aSBoris Brezillon 	/* List of NAND hosts (one for each chip-select) */
23493db446aSBoris Brezillon 	struct list_head host_list;
23593db446aSBoris Brezillon 
236a5d53ad2SKamal Dasu 	/* EDU info, per-transaction */
237a5d53ad2SKamal Dasu 	const u16               *edu_offsets;
238a5d53ad2SKamal Dasu 	void __iomem            *edu_base;
239a5d53ad2SKamal Dasu 	int			edu_irq;
240a5d53ad2SKamal Dasu 	int                     edu_count;
241a5d53ad2SKamal Dasu 	u64                     edu_dram_addr;
242a5d53ad2SKamal Dasu 	u32                     edu_ext_addr;
243a5d53ad2SKamal Dasu 	u32                     edu_cmd;
244a5d53ad2SKamal Dasu 	u32                     edu_config;
245a5d53ad2SKamal Dasu 
2460c06da57SKamal Dasu 	/* flash_dma reg */
2470c06da57SKamal Dasu 	const u16		*flash_dma_offsets;
24893db446aSBoris Brezillon 	struct brcm_nand_dma_desc *dma_desc;
24993db446aSBoris Brezillon 	dma_addr_t		dma_pa;
25093db446aSBoris Brezillon 
251a5d53ad2SKamal Dasu 	int (*dma_trans)(struct brcmnand_host *host, u64 addr, u32 *buf,
252a5d53ad2SKamal Dasu 			 u32 len, u8 dma_cmd);
253a5d53ad2SKamal Dasu 
25493db446aSBoris Brezillon 	/* in-memory cache of the FLASH_CACHE, used only for some commands */
25593db446aSBoris Brezillon 	u8			flash_cache[FC_BYTES];
25693db446aSBoris Brezillon 
25793db446aSBoris Brezillon 	/* Controller revision details */
25893db446aSBoris Brezillon 	const u16		*reg_offsets;
25993db446aSBoris Brezillon 	unsigned int		reg_spacing; /* between CS1, CS2, ... regs */
26093db446aSBoris Brezillon 	const u8		*cs_offsets; /* within each chip-select */
26193db446aSBoris Brezillon 	const u8		*cs0_offsets; /* within CS0, if different */
26293db446aSBoris Brezillon 	unsigned int		max_block_size;
26393db446aSBoris Brezillon 	const unsigned int	*block_sizes;
26493db446aSBoris Brezillon 	unsigned int		max_page_size;
26593db446aSBoris Brezillon 	const unsigned int	*page_sizes;
2667e7c7df5SÁlvaro Fernández Rojas 	unsigned int		page_size_shift;
26793db446aSBoris Brezillon 	unsigned int		max_oob;
26893db446aSBoris Brezillon 	u32			features;
26993db446aSBoris Brezillon 
27093db446aSBoris Brezillon 	/* for low-power standby/resume only */
27193db446aSBoris Brezillon 	u32			nand_cs_nand_select;
27293db446aSBoris Brezillon 	u32			nand_cs_nand_xor;
27393db446aSBoris Brezillon 	u32			corr_stat_threshold;
27493db446aSBoris Brezillon 	u32			flash_dma_mode;
275a5d53ad2SKamal Dasu 	u32                     flash_edu_mode;
276c1ac2dc3SKamal Dasu 	bool			pio_poll_mode;
27793db446aSBoris Brezillon };
27893db446aSBoris Brezillon 
27993db446aSBoris Brezillon struct brcmnand_cfg {
28093db446aSBoris Brezillon 	u64			device_size;
28193db446aSBoris Brezillon 	unsigned int		block_size;
28293db446aSBoris Brezillon 	unsigned int		page_size;
28393db446aSBoris Brezillon 	unsigned int		spare_area_size;
28493db446aSBoris Brezillon 	unsigned int		device_width;
28593db446aSBoris Brezillon 	unsigned int		col_adr_bytes;
28693db446aSBoris Brezillon 	unsigned int		blk_adr_bytes;
28793db446aSBoris Brezillon 	unsigned int		ful_adr_bytes;
28893db446aSBoris Brezillon 	unsigned int		sector_size_1k;
28993db446aSBoris Brezillon 	unsigned int		ecc_level;
29093db446aSBoris Brezillon 	/* use for low-power standby/resume only */
29193db446aSBoris Brezillon 	u32			acc_control;
29293db446aSBoris Brezillon 	u32			config;
29393db446aSBoris Brezillon 	u32			config_ext;
29493db446aSBoris Brezillon 	u32			timing_1;
29593db446aSBoris Brezillon 	u32			timing_2;
29693db446aSBoris Brezillon };
29793db446aSBoris Brezillon 
29893db446aSBoris Brezillon struct brcmnand_host {
29993db446aSBoris Brezillon 	struct list_head	node;
30093db446aSBoris Brezillon 
30193db446aSBoris Brezillon 	struct nand_chip	chip;
30293db446aSBoris Brezillon 	struct platform_device	*pdev;
30393db446aSBoris Brezillon 	int			cs;
30493db446aSBoris Brezillon 
30593db446aSBoris Brezillon 	unsigned int		last_cmd;
30693db446aSBoris Brezillon 	unsigned int		last_byte;
30793db446aSBoris Brezillon 	u64			last_addr;
30893db446aSBoris Brezillon 	struct brcmnand_cfg	hwcfg;
30993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl;
31093db446aSBoris Brezillon };
31193db446aSBoris Brezillon 
31293db446aSBoris Brezillon enum brcmnand_reg {
31393db446aSBoris Brezillon 	BRCMNAND_CMD_START = 0,
31493db446aSBoris Brezillon 	BRCMNAND_CMD_EXT_ADDRESS,
31593db446aSBoris Brezillon 	BRCMNAND_CMD_ADDRESS,
31693db446aSBoris Brezillon 	BRCMNAND_INTFC_STATUS,
31793db446aSBoris Brezillon 	BRCMNAND_CS_SELECT,
31893db446aSBoris Brezillon 	BRCMNAND_CS_XOR,
31993db446aSBoris Brezillon 	BRCMNAND_LL_OP,
32093db446aSBoris Brezillon 	BRCMNAND_CS0_BASE,
32193db446aSBoris Brezillon 	BRCMNAND_CS1_BASE,		/* CS1 regs, if non-contiguous */
32293db446aSBoris Brezillon 	BRCMNAND_CORR_THRESHOLD,
32393db446aSBoris Brezillon 	BRCMNAND_CORR_THRESHOLD_EXT,
32493db446aSBoris Brezillon 	BRCMNAND_UNCORR_COUNT,
32593db446aSBoris Brezillon 	BRCMNAND_CORR_COUNT,
32693db446aSBoris Brezillon 	BRCMNAND_CORR_EXT_ADDR,
32793db446aSBoris Brezillon 	BRCMNAND_CORR_ADDR,
32893db446aSBoris Brezillon 	BRCMNAND_UNCORR_EXT_ADDR,
32993db446aSBoris Brezillon 	BRCMNAND_UNCORR_ADDR,
33093db446aSBoris Brezillon 	BRCMNAND_SEMAPHORE,
33193db446aSBoris Brezillon 	BRCMNAND_ID,
33293db446aSBoris Brezillon 	BRCMNAND_ID_EXT,
33393db446aSBoris Brezillon 	BRCMNAND_LL_RDATA,
33493db446aSBoris Brezillon 	BRCMNAND_OOB_READ_BASE,
33593db446aSBoris Brezillon 	BRCMNAND_OOB_READ_10_BASE,	/* offset 0x10, if non-contiguous */
33693db446aSBoris Brezillon 	BRCMNAND_OOB_WRITE_BASE,
33793db446aSBoris Brezillon 	BRCMNAND_OOB_WRITE_10_BASE,	/* offset 0x10, if non-contiguous */
33893db446aSBoris Brezillon 	BRCMNAND_FC_BASE,
33993db446aSBoris Brezillon };
34093db446aSBoris Brezillon 
3417e7c7df5SÁlvaro Fernández Rojas /* BRCMNAND v2.1-v2.2 */
3427e7c7df5SÁlvaro Fernández Rojas static const u16 brcmnand_regs_v21[] = {
3437e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CMD_START]		=  0x04,
3447e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
3457e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
3467e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_INTFC_STATUS]		=  0x5c,
3477e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CS_SELECT]		=  0x14,
3487e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CS_XOR]		=  0x18,
3497e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_LL_OP]		=     0,
3507e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CS0_BASE]		=  0x40,
3517e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CS1_BASE]		=     0,
3527e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CORR_THRESHOLD]	=     0,
3537e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
3547e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_UNCORR_COUNT]		=     0,
3557e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CORR_COUNT]		=     0,
3567e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CORR_EXT_ADDR]	=  0x60,
3577e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_CORR_ADDR]		=  0x64,
3587e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x68,
3597e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_UNCORR_ADDR]		=  0x6c,
3607e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_SEMAPHORE]		=  0x50,
3617e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_ID]			=  0x54,
3627e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_ID_EXT]		=     0,
3637e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_LL_RDATA]		=     0,
3647e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
3657e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
3667e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
3677e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
3687e7c7df5SÁlvaro Fernández Rojas 	[BRCMNAND_FC_BASE]		= 0x200,
3697e7c7df5SÁlvaro Fernández Rojas };
3707e7c7df5SÁlvaro Fernández Rojas 
3714fd63909SÁlvaro Fernández Rojas /* BRCMNAND v3.3-v4.0 */
3724fd63909SÁlvaro Fernández Rojas static const u16 brcmnand_regs_v33[] = {
37393db446aSBoris Brezillon 	[BRCMNAND_CMD_START]		=  0x04,
37493db446aSBoris Brezillon 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
37593db446aSBoris Brezillon 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
37693db446aSBoris Brezillon 	[BRCMNAND_INTFC_STATUS]		=  0x6c,
37793db446aSBoris Brezillon 	[BRCMNAND_CS_SELECT]		=  0x14,
37893db446aSBoris Brezillon 	[BRCMNAND_CS_XOR]		=  0x18,
37993db446aSBoris Brezillon 	[BRCMNAND_LL_OP]		= 0x178,
38093db446aSBoris Brezillon 	[BRCMNAND_CS0_BASE]		=  0x40,
38193db446aSBoris Brezillon 	[BRCMNAND_CS1_BASE]		=  0xd0,
38293db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
38393db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
38493db446aSBoris Brezillon 	[BRCMNAND_UNCORR_COUNT]		=     0,
38593db446aSBoris Brezillon 	[BRCMNAND_CORR_COUNT]		=     0,
38693db446aSBoris Brezillon 	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
38793db446aSBoris Brezillon 	[BRCMNAND_CORR_ADDR]		=  0x74,
38893db446aSBoris Brezillon 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
38993db446aSBoris Brezillon 	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
39093db446aSBoris Brezillon 	[BRCMNAND_SEMAPHORE]		=  0x58,
39193db446aSBoris Brezillon 	[BRCMNAND_ID]			=  0x60,
39293db446aSBoris Brezillon 	[BRCMNAND_ID_EXT]		=  0x64,
39393db446aSBoris Brezillon 	[BRCMNAND_LL_RDATA]		= 0x17c,
39493db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
39593db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
39693db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
39793db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
39893db446aSBoris Brezillon 	[BRCMNAND_FC_BASE]		= 0x200,
39993db446aSBoris Brezillon };
40093db446aSBoris Brezillon 
40193db446aSBoris Brezillon /* BRCMNAND v5.0 */
40293db446aSBoris Brezillon static const u16 brcmnand_regs_v50[] = {
40393db446aSBoris Brezillon 	[BRCMNAND_CMD_START]		=  0x04,
40493db446aSBoris Brezillon 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
40593db446aSBoris Brezillon 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
40693db446aSBoris Brezillon 	[BRCMNAND_INTFC_STATUS]		=  0x6c,
40793db446aSBoris Brezillon 	[BRCMNAND_CS_SELECT]		=  0x14,
40893db446aSBoris Brezillon 	[BRCMNAND_CS_XOR]		=  0x18,
40993db446aSBoris Brezillon 	[BRCMNAND_LL_OP]		= 0x178,
41093db446aSBoris Brezillon 	[BRCMNAND_CS0_BASE]		=  0x40,
41193db446aSBoris Brezillon 	[BRCMNAND_CS1_BASE]		=  0xd0,
41293db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
41393db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
41493db446aSBoris Brezillon 	[BRCMNAND_UNCORR_COUNT]		=     0,
41593db446aSBoris Brezillon 	[BRCMNAND_CORR_COUNT]		=     0,
41693db446aSBoris Brezillon 	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
41793db446aSBoris Brezillon 	[BRCMNAND_CORR_ADDR]		=  0x74,
41893db446aSBoris Brezillon 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
41993db446aSBoris Brezillon 	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
42093db446aSBoris Brezillon 	[BRCMNAND_SEMAPHORE]		=  0x58,
42193db446aSBoris Brezillon 	[BRCMNAND_ID]			=  0x60,
42293db446aSBoris Brezillon 	[BRCMNAND_ID_EXT]		=  0x64,
42393db446aSBoris Brezillon 	[BRCMNAND_LL_RDATA]		= 0x17c,
42493db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
42593db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
42693db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
42793db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_10_BASE]	= 0x140,
42893db446aSBoris Brezillon 	[BRCMNAND_FC_BASE]		= 0x200,
42993db446aSBoris Brezillon };
43093db446aSBoris Brezillon 
43193db446aSBoris Brezillon /* BRCMNAND v6.0 - v7.1 */
43293db446aSBoris Brezillon static const u16 brcmnand_regs_v60[] = {
43393db446aSBoris Brezillon 	[BRCMNAND_CMD_START]		=  0x04,
43493db446aSBoris Brezillon 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
43593db446aSBoris Brezillon 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
43693db446aSBoris Brezillon 	[BRCMNAND_INTFC_STATUS]		=  0x14,
43793db446aSBoris Brezillon 	[BRCMNAND_CS_SELECT]		=  0x18,
43893db446aSBoris Brezillon 	[BRCMNAND_CS_XOR]		=  0x1c,
43993db446aSBoris Brezillon 	[BRCMNAND_LL_OP]		=  0x20,
44093db446aSBoris Brezillon 	[BRCMNAND_CS0_BASE]		=  0x50,
44193db446aSBoris Brezillon 	[BRCMNAND_CS1_BASE]		=     0,
44293db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD]	=  0xc0,
44393db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xc4,
44493db446aSBoris Brezillon 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
44593db446aSBoris Brezillon 	[BRCMNAND_CORR_COUNT]		= 0x100,
44693db446aSBoris Brezillon 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
44793db446aSBoris Brezillon 	[BRCMNAND_CORR_ADDR]		= 0x110,
44893db446aSBoris Brezillon 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
44993db446aSBoris Brezillon 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
45093db446aSBoris Brezillon 	[BRCMNAND_SEMAPHORE]		= 0x150,
45193db446aSBoris Brezillon 	[BRCMNAND_ID]			= 0x194,
45293db446aSBoris Brezillon 	[BRCMNAND_ID_EXT]		= 0x198,
45393db446aSBoris Brezillon 	[BRCMNAND_LL_RDATA]		= 0x19c,
45493db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
45593db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
45693db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
45793db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
45893db446aSBoris Brezillon 	[BRCMNAND_FC_BASE]		= 0x400,
45993db446aSBoris Brezillon };
46093db446aSBoris Brezillon 
46193db446aSBoris Brezillon /* BRCMNAND v7.1 */
46293db446aSBoris Brezillon static const u16 brcmnand_regs_v71[] = {
46393db446aSBoris Brezillon 	[BRCMNAND_CMD_START]		=  0x04,
46493db446aSBoris Brezillon 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
46593db446aSBoris Brezillon 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
46693db446aSBoris Brezillon 	[BRCMNAND_INTFC_STATUS]		=  0x14,
46793db446aSBoris Brezillon 	[BRCMNAND_CS_SELECT]		=  0x18,
46893db446aSBoris Brezillon 	[BRCMNAND_CS_XOR]		=  0x1c,
46993db446aSBoris Brezillon 	[BRCMNAND_LL_OP]		=  0x20,
47093db446aSBoris Brezillon 	[BRCMNAND_CS0_BASE]		=  0x50,
47193db446aSBoris Brezillon 	[BRCMNAND_CS1_BASE]		=     0,
47293db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD]	=  0xdc,
47393db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xe0,
47493db446aSBoris Brezillon 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
47593db446aSBoris Brezillon 	[BRCMNAND_CORR_COUNT]		= 0x100,
47693db446aSBoris Brezillon 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
47793db446aSBoris Brezillon 	[BRCMNAND_CORR_ADDR]		= 0x110,
47893db446aSBoris Brezillon 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
47993db446aSBoris Brezillon 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
48093db446aSBoris Brezillon 	[BRCMNAND_SEMAPHORE]		= 0x150,
48193db446aSBoris Brezillon 	[BRCMNAND_ID]			= 0x194,
48293db446aSBoris Brezillon 	[BRCMNAND_ID_EXT]		= 0x198,
48393db446aSBoris Brezillon 	[BRCMNAND_LL_RDATA]		= 0x19c,
48493db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
48593db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
48693db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
48793db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
48893db446aSBoris Brezillon 	[BRCMNAND_FC_BASE]		= 0x400,
48993db446aSBoris Brezillon };
49093db446aSBoris Brezillon 
49193db446aSBoris Brezillon /* BRCMNAND v7.2 */
49293db446aSBoris Brezillon static const u16 brcmnand_regs_v72[] = {
49393db446aSBoris Brezillon 	[BRCMNAND_CMD_START]		=  0x04,
49493db446aSBoris Brezillon 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
49593db446aSBoris Brezillon 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
49693db446aSBoris Brezillon 	[BRCMNAND_INTFC_STATUS]		=  0x14,
49793db446aSBoris Brezillon 	[BRCMNAND_CS_SELECT]		=  0x18,
49893db446aSBoris Brezillon 	[BRCMNAND_CS_XOR]		=  0x1c,
49993db446aSBoris Brezillon 	[BRCMNAND_LL_OP]		=  0x20,
50093db446aSBoris Brezillon 	[BRCMNAND_CS0_BASE]		=  0x50,
50193db446aSBoris Brezillon 	[BRCMNAND_CS1_BASE]		=     0,
50293db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD]	=  0xdc,
50393db446aSBoris Brezillon 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xe0,
50493db446aSBoris Brezillon 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
50593db446aSBoris Brezillon 	[BRCMNAND_CORR_COUNT]		= 0x100,
50693db446aSBoris Brezillon 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
50793db446aSBoris Brezillon 	[BRCMNAND_CORR_ADDR]		= 0x110,
50893db446aSBoris Brezillon 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
50993db446aSBoris Brezillon 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
51093db446aSBoris Brezillon 	[BRCMNAND_SEMAPHORE]		= 0x150,
51193db446aSBoris Brezillon 	[BRCMNAND_ID]			= 0x194,
51293db446aSBoris Brezillon 	[BRCMNAND_ID_EXT]		= 0x198,
51393db446aSBoris Brezillon 	[BRCMNAND_LL_RDATA]		= 0x19c,
51493db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
51593db446aSBoris Brezillon 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
51693db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_BASE]	= 0x400,
51793db446aSBoris Brezillon 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
51893db446aSBoris Brezillon 	[BRCMNAND_FC_BASE]		= 0x600,
51993db446aSBoris Brezillon };
52093db446aSBoris Brezillon 
52193db446aSBoris Brezillon enum brcmnand_cs_reg {
52293db446aSBoris Brezillon 	BRCMNAND_CS_CFG_EXT = 0,
52393db446aSBoris Brezillon 	BRCMNAND_CS_CFG,
52493db446aSBoris Brezillon 	BRCMNAND_CS_ACC_CONTROL,
52593db446aSBoris Brezillon 	BRCMNAND_CS_TIMING1,
52693db446aSBoris Brezillon 	BRCMNAND_CS_TIMING2,
52793db446aSBoris Brezillon };
52893db446aSBoris Brezillon 
52993db446aSBoris Brezillon /* Per chip-select offsets for v7.1 */
53093db446aSBoris Brezillon static const u8 brcmnand_cs_offsets_v71[] = {
53193db446aSBoris Brezillon 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
53293db446aSBoris Brezillon 	[BRCMNAND_CS_CFG_EXT]		= 0x04,
53393db446aSBoris Brezillon 	[BRCMNAND_CS_CFG]		= 0x08,
53493db446aSBoris Brezillon 	[BRCMNAND_CS_TIMING1]		= 0x0c,
53593db446aSBoris Brezillon 	[BRCMNAND_CS_TIMING2]		= 0x10,
53693db446aSBoris Brezillon };
53793db446aSBoris Brezillon 
53893db446aSBoris Brezillon /* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
53993db446aSBoris Brezillon static const u8 brcmnand_cs_offsets[] = {
54093db446aSBoris Brezillon 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
54193db446aSBoris Brezillon 	[BRCMNAND_CS_CFG_EXT]		= 0x04,
54293db446aSBoris Brezillon 	[BRCMNAND_CS_CFG]		= 0x04,
54393db446aSBoris Brezillon 	[BRCMNAND_CS_TIMING1]		= 0x08,
54493db446aSBoris Brezillon 	[BRCMNAND_CS_TIMING2]		= 0x0c,
54593db446aSBoris Brezillon };
54693db446aSBoris Brezillon 
54793db446aSBoris Brezillon /* Per chip-select offset for <= v5.0 on CS0 only */
54893db446aSBoris Brezillon static const u8 brcmnand_cs_offsets_cs0[] = {
54993db446aSBoris Brezillon 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
55093db446aSBoris Brezillon 	[BRCMNAND_CS_CFG_EXT]		= 0x08,
55193db446aSBoris Brezillon 	[BRCMNAND_CS_CFG]		= 0x08,
55293db446aSBoris Brezillon 	[BRCMNAND_CS_TIMING1]		= 0x10,
55393db446aSBoris Brezillon 	[BRCMNAND_CS_TIMING2]		= 0x14,
55493db446aSBoris Brezillon };
55593db446aSBoris Brezillon 
55693db446aSBoris Brezillon /*
55793db446aSBoris Brezillon  * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
55893db446aSBoris Brezillon  * one config register, but once the bitfields overflowed, newer controllers
55993db446aSBoris Brezillon  * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
56093db446aSBoris Brezillon  */
56193db446aSBoris Brezillon enum {
56293db446aSBoris Brezillon 	CFG_BLK_ADR_BYTES_SHIFT		= 8,
56393db446aSBoris Brezillon 	CFG_COL_ADR_BYTES_SHIFT		= 12,
56493db446aSBoris Brezillon 	CFG_FUL_ADR_BYTES_SHIFT		= 16,
56593db446aSBoris Brezillon 	CFG_BUS_WIDTH_SHIFT		= 23,
56693db446aSBoris Brezillon 	CFG_BUS_WIDTH			= BIT(CFG_BUS_WIDTH_SHIFT),
56793db446aSBoris Brezillon 	CFG_DEVICE_SIZE_SHIFT		= 24,
56893db446aSBoris Brezillon 
5697e7c7df5SÁlvaro Fernández Rojas 	/* Only for v2.1 */
5707e7c7df5SÁlvaro Fernández Rojas 	CFG_PAGE_SIZE_SHIFT_v2_1	= 30,
5717e7c7df5SÁlvaro Fernández Rojas 
57293db446aSBoris Brezillon 	/* Only for pre-v7.1 (with no CFG_EXT register) */
57393db446aSBoris Brezillon 	CFG_PAGE_SIZE_SHIFT		= 20,
57493db446aSBoris Brezillon 	CFG_BLK_SIZE_SHIFT		= 28,
57593db446aSBoris Brezillon 
57693db446aSBoris Brezillon 	/* Only for v7.1+ (with CFG_EXT register) */
57793db446aSBoris Brezillon 	CFG_EXT_PAGE_SIZE_SHIFT		= 0,
57893db446aSBoris Brezillon 	CFG_EXT_BLK_SIZE_SHIFT		= 4,
57993db446aSBoris Brezillon };
58093db446aSBoris Brezillon 
58193db446aSBoris Brezillon /* BRCMNAND_INTFC_STATUS */
58293db446aSBoris Brezillon enum {
58393db446aSBoris Brezillon 	INTFC_FLASH_STATUS		= GENMASK(7, 0),
58493db446aSBoris Brezillon 
58593db446aSBoris Brezillon 	INTFC_ERASED			= BIT(27),
58693db446aSBoris Brezillon 	INTFC_OOB_VALID			= BIT(28),
58793db446aSBoris Brezillon 	INTFC_CACHE_VALID		= BIT(29),
58893db446aSBoris Brezillon 	INTFC_FLASH_READY		= BIT(30),
58993db446aSBoris Brezillon 	INTFC_CTLR_READY		= BIT(31),
59093db446aSBoris Brezillon };
59193db446aSBoris Brezillon 
59293db446aSBoris Brezillon static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
59393db446aSBoris Brezillon {
59493db446aSBoris Brezillon 	return brcmnand_readl(ctrl->nand_base + offs);
59593db446aSBoris Brezillon }
59693db446aSBoris Brezillon 
59793db446aSBoris Brezillon static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
59893db446aSBoris Brezillon 				 u32 val)
59993db446aSBoris Brezillon {
60093db446aSBoris Brezillon 	brcmnand_writel(val, ctrl->nand_base + offs);
60193db446aSBoris Brezillon }
60293db446aSBoris Brezillon 
60393db446aSBoris Brezillon static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
60493db446aSBoris Brezillon {
60593db446aSBoris Brezillon 	static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
60693db446aSBoris Brezillon 	static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
6077e7c7df5SÁlvaro Fernández Rojas 	static const unsigned int block_sizes_v2_2[] = { 16, 128, 8, 512, 256, 0 };
6087e7c7df5SÁlvaro Fernández Rojas 	static const unsigned int block_sizes_v2_1[] = { 16, 128, 8, 512, 0 };
609eeeac9cbSÁlvaro Fernández Rojas 	static const unsigned int page_sizes_v3_4[] = { 512, 2048, 4096, 8192, 0 };
6107e7c7df5SÁlvaro Fernández Rojas 	static const unsigned int page_sizes_v2_2[] = { 512, 2048, 4096, 0 };
6117e7c7df5SÁlvaro Fernández Rojas 	static const unsigned int page_sizes_v2_1[] = { 512, 2048, 0 };
61293db446aSBoris Brezillon 
61393db446aSBoris Brezillon 	ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
61493db446aSBoris Brezillon 
6157e7c7df5SÁlvaro Fernández Rojas 	/* Only support v2.1+ */
6167e7c7df5SÁlvaro Fernández Rojas 	if (ctrl->nand_version < 0x0201) {
61793db446aSBoris Brezillon 		dev_err(ctrl->dev, "version %#x not supported\n",
61893db446aSBoris Brezillon 			ctrl->nand_version);
61993db446aSBoris Brezillon 		return -ENODEV;
62093db446aSBoris Brezillon 	}
62193db446aSBoris Brezillon 
62293db446aSBoris Brezillon 	/* Register offsets */
62393db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0702)
62493db446aSBoris Brezillon 		ctrl->reg_offsets = brcmnand_regs_v72;
6250c06da57SKamal Dasu 	else if (ctrl->nand_version == 0x0701)
62693db446aSBoris Brezillon 		ctrl->reg_offsets = brcmnand_regs_v71;
62793db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0600)
62893db446aSBoris Brezillon 		ctrl->reg_offsets = brcmnand_regs_v60;
62993db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0500)
63093db446aSBoris Brezillon 		ctrl->reg_offsets = brcmnand_regs_v50;
6314fd63909SÁlvaro Fernández Rojas 	else if (ctrl->nand_version >= 0x0303)
6324fd63909SÁlvaro Fernández Rojas 		ctrl->reg_offsets = brcmnand_regs_v33;
6337e7c7df5SÁlvaro Fernández Rojas 	else if (ctrl->nand_version >= 0x0201)
6347e7c7df5SÁlvaro Fernández Rojas 		ctrl->reg_offsets = brcmnand_regs_v21;
63593db446aSBoris Brezillon 
63693db446aSBoris Brezillon 	/* Chip-select stride */
63793db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0701)
63893db446aSBoris Brezillon 		ctrl->reg_spacing = 0x14;
63993db446aSBoris Brezillon 	else
64093db446aSBoris Brezillon 		ctrl->reg_spacing = 0x10;
64193db446aSBoris Brezillon 
64293db446aSBoris Brezillon 	/* Per chip-select registers */
64393db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0701) {
64493db446aSBoris Brezillon 		ctrl->cs_offsets = brcmnand_cs_offsets_v71;
64593db446aSBoris Brezillon 	} else {
64693db446aSBoris Brezillon 		ctrl->cs_offsets = brcmnand_cs_offsets;
64793db446aSBoris Brezillon 
6483d3fb3c5SÁlvaro Fernández Rojas 		/* v3.3-5.0 have a different CS0 offset layout */
6493d3fb3c5SÁlvaro Fernández Rojas 		if (ctrl->nand_version >= 0x0303 &&
6503d3fb3c5SÁlvaro Fernández Rojas 		    ctrl->nand_version <= 0x0500)
65193db446aSBoris Brezillon 			ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
65293db446aSBoris Brezillon 	}
65393db446aSBoris Brezillon 
65493db446aSBoris Brezillon 	/* Page / block sizes */
65593db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0701) {
65693db446aSBoris Brezillon 		/* >= v7.1 use nice power-of-2 values! */
65793db446aSBoris Brezillon 		ctrl->max_page_size = 16 * 1024;
65893db446aSBoris Brezillon 		ctrl->max_block_size = 2 * 1024 * 1024;
65993db446aSBoris Brezillon 	} else {
6607e7c7df5SÁlvaro Fernández Rojas 		if (ctrl->nand_version >= 0x0304)
661eeeac9cbSÁlvaro Fernández Rojas 			ctrl->page_sizes = page_sizes_v3_4;
6627e7c7df5SÁlvaro Fernández Rojas 		else if (ctrl->nand_version >= 0x0202)
6637e7c7df5SÁlvaro Fernández Rojas 			ctrl->page_sizes = page_sizes_v2_2;
6647e7c7df5SÁlvaro Fernández Rojas 		else
6657e7c7df5SÁlvaro Fernández Rojas 			ctrl->page_sizes = page_sizes_v2_1;
6667e7c7df5SÁlvaro Fernández Rojas 
6677e7c7df5SÁlvaro Fernández Rojas 		if (ctrl->nand_version >= 0x0202)
6687e7c7df5SÁlvaro Fernández Rojas 			ctrl->page_size_shift = CFG_PAGE_SIZE_SHIFT;
6697e7c7df5SÁlvaro Fernández Rojas 		else
6707e7c7df5SÁlvaro Fernández Rojas 			ctrl->page_size_shift = CFG_PAGE_SIZE_SHIFT_v2_1;
6717e7c7df5SÁlvaro Fernández Rojas 
67293db446aSBoris Brezillon 		if (ctrl->nand_version >= 0x0600)
67393db446aSBoris Brezillon 			ctrl->block_sizes = block_sizes_v6;
6747e7c7df5SÁlvaro Fernández Rojas 		else if (ctrl->nand_version >= 0x0400)
67593db446aSBoris Brezillon 			ctrl->block_sizes = block_sizes_v4;
6767e7c7df5SÁlvaro Fernández Rojas 		else if (ctrl->nand_version >= 0x0202)
6777e7c7df5SÁlvaro Fernández Rojas 			ctrl->block_sizes = block_sizes_v2_2;
6787e7c7df5SÁlvaro Fernández Rojas 		else
6797e7c7df5SÁlvaro Fernández Rojas 			ctrl->block_sizes = block_sizes_v2_1;
68093db446aSBoris Brezillon 
68193db446aSBoris Brezillon 		if (ctrl->nand_version < 0x0400) {
6827e7c7df5SÁlvaro Fernández Rojas 			if (ctrl->nand_version < 0x0202)
6837e7c7df5SÁlvaro Fernández Rojas 				ctrl->max_page_size = 2048;
6847e7c7df5SÁlvaro Fernández Rojas 			else
68593db446aSBoris Brezillon 				ctrl->max_page_size = 4096;
68693db446aSBoris Brezillon 			ctrl->max_block_size = 512 * 1024;
68793db446aSBoris Brezillon 		}
68893db446aSBoris Brezillon 	}
68993db446aSBoris Brezillon 
69093db446aSBoris Brezillon 	/* Maximum spare area sector size (per 512B) */
6910c06da57SKamal Dasu 	if (ctrl->nand_version == 0x0702)
69293db446aSBoris Brezillon 		ctrl->max_oob = 128;
69393db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0600)
69493db446aSBoris Brezillon 		ctrl->max_oob = 64;
69593db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0500)
69693db446aSBoris Brezillon 		ctrl->max_oob = 32;
69793db446aSBoris Brezillon 	else
69893db446aSBoris Brezillon 		ctrl->max_oob = 16;
69993db446aSBoris Brezillon 
70093db446aSBoris Brezillon 	/* v6.0 and newer (except v6.1) have prefetch support */
70193db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
70293db446aSBoris Brezillon 		ctrl->features |= BRCMNAND_HAS_PREFETCH;
70393db446aSBoris Brezillon 
70493db446aSBoris Brezillon 	/*
70593db446aSBoris Brezillon 	 * v6.x has cache mode, but it's implemented differently. Ignore it for
70693db446aSBoris Brezillon 	 * now.
70793db446aSBoris Brezillon 	 */
70893db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0700)
70993db446aSBoris Brezillon 		ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
71093db446aSBoris Brezillon 
71193db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0500)
71293db446aSBoris Brezillon 		ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
71393db446aSBoris Brezillon 
71493db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0700)
71593db446aSBoris Brezillon 		ctrl->features |= BRCMNAND_HAS_WP;
71693db446aSBoris Brezillon 	else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
71793db446aSBoris Brezillon 		ctrl->features |= BRCMNAND_HAS_WP;
71893db446aSBoris Brezillon 
71993db446aSBoris Brezillon 	return 0;
72093db446aSBoris Brezillon }
72193db446aSBoris Brezillon 
7220c06da57SKamal Dasu static void brcmnand_flash_dma_revision_init(struct brcmnand_controller *ctrl)
7230c06da57SKamal Dasu {
7240c06da57SKamal Dasu 	/* flash_dma register offsets */
7250c06da57SKamal Dasu 	if (ctrl->nand_version >= 0x0703)
7260c06da57SKamal Dasu 		ctrl->flash_dma_offsets = flash_dma_regs_v4;
72783156c1cSKamal Dasu 	else if (ctrl->nand_version == 0x0602)
72883156c1cSKamal Dasu 		ctrl->flash_dma_offsets = flash_dma_regs_v0;
7290c06da57SKamal Dasu 	else
7300c06da57SKamal Dasu 		ctrl->flash_dma_offsets = flash_dma_regs_v1;
7310c06da57SKamal Dasu }
7320c06da57SKamal Dasu 
73393db446aSBoris Brezillon static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
73493db446aSBoris Brezillon 		enum brcmnand_reg reg)
73593db446aSBoris Brezillon {
73693db446aSBoris Brezillon 	u16 offs = ctrl->reg_offsets[reg];
73793db446aSBoris Brezillon 
73893db446aSBoris Brezillon 	if (offs)
73993db446aSBoris Brezillon 		return nand_readreg(ctrl, offs);
74093db446aSBoris Brezillon 	else
74193db446aSBoris Brezillon 		return 0;
74293db446aSBoris Brezillon }
74393db446aSBoris Brezillon 
74493db446aSBoris Brezillon static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
74593db446aSBoris Brezillon 				      enum brcmnand_reg reg, u32 val)
74693db446aSBoris Brezillon {
74793db446aSBoris Brezillon 	u16 offs = ctrl->reg_offsets[reg];
74893db446aSBoris Brezillon 
74993db446aSBoris Brezillon 	if (offs)
75093db446aSBoris Brezillon 		nand_writereg(ctrl, offs, val);
75193db446aSBoris Brezillon }
75293db446aSBoris Brezillon 
75393db446aSBoris Brezillon static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
75493db446aSBoris Brezillon 				    enum brcmnand_reg reg, u32 mask, unsigned
75593db446aSBoris Brezillon 				    int shift, u32 val)
75693db446aSBoris Brezillon {
75793db446aSBoris Brezillon 	u32 tmp = brcmnand_read_reg(ctrl, reg);
75893db446aSBoris Brezillon 
75993db446aSBoris Brezillon 	tmp &= ~mask;
76093db446aSBoris Brezillon 	tmp |= val << shift;
76193db446aSBoris Brezillon 	brcmnand_write_reg(ctrl, reg, tmp);
76293db446aSBoris Brezillon }
76393db446aSBoris Brezillon 
76493db446aSBoris Brezillon static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
76593db446aSBoris Brezillon {
76693db446aSBoris Brezillon 	return __raw_readl(ctrl->nand_fc + word * 4);
76793db446aSBoris Brezillon }
76893db446aSBoris Brezillon 
76993db446aSBoris Brezillon static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
77093db446aSBoris Brezillon 				     int word, u32 val)
77193db446aSBoris Brezillon {
77293db446aSBoris Brezillon 	__raw_writel(val, ctrl->nand_fc + word * 4);
77393db446aSBoris Brezillon }
77493db446aSBoris Brezillon 
775a5d53ad2SKamal Dasu static inline void edu_writel(struct brcmnand_controller *ctrl,
776a5d53ad2SKamal Dasu 			      enum edu_reg reg, u32 val)
777a5d53ad2SKamal Dasu {
778a5d53ad2SKamal Dasu 	u16 offs = ctrl->edu_offsets[reg];
779a5d53ad2SKamal Dasu 
780a5d53ad2SKamal Dasu 	brcmnand_writel(val, ctrl->edu_base + offs);
781a5d53ad2SKamal Dasu }
782a5d53ad2SKamal Dasu 
783a5d53ad2SKamal Dasu static inline u32 edu_readl(struct brcmnand_controller *ctrl,
784a5d53ad2SKamal Dasu 			    enum edu_reg reg)
785a5d53ad2SKamal Dasu {
786a5d53ad2SKamal Dasu 	u16 offs = ctrl->edu_offsets[reg];
787a5d53ad2SKamal Dasu 
788a5d53ad2SKamal Dasu 	return brcmnand_readl(ctrl->edu_base + offs);
789a5d53ad2SKamal Dasu }
790a5d53ad2SKamal Dasu 
7913c7c1e45SKamal Dasu static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl)
7923c7c1e45SKamal Dasu {
7933c7c1e45SKamal Dasu 
7943c7c1e45SKamal Dasu 	/* Clear error addresses */
7953c7c1e45SKamal Dasu 	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
7963c7c1e45SKamal Dasu 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
7973c7c1e45SKamal Dasu 	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0);
7983c7c1e45SKamal Dasu 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0);
7993c7c1e45SKamal Dasu }
8003c7c1e45SKamal Dasu 
8013c7c1e45SKamal Dasu static u64 brcmnand_get_uncorrecc_addr(struct brcmnand_controller *ctrl)
8023c7c1e45SKamal Dasu {
8033c7c1e45SKamal Dasu 	u64 err_addr;
8043c7c1e45SKamal Dasu 
8053c7c1e45SKamal Dasu 	err_addr = brcmnand_read_reg(ctrl, BRCMNAND_UNCORR_ADDR);
8063c7c1e45SKamal Dasu 	err_addr |= ((u64)(brcmnand_read_reg(ctrl,
8073c7c1e45SKamal Dasu 					     BRCMNAND_UNCORR_EXT_ADDR)
8083c7c1e45SKamal Dasu 					     & 0xffff) << 32);
8093c7c1e45SKamal Dasu 
8103c7c1e45SKamal Dasu 	return err_addr;
8113c7c1e45SKamal Dasu }
8123c7c1e45SKamal Dasu 
8133c7c1e45SKamal Dasu static u64 brcmnand_get_correcc_addr(struct brcmnand_controller *ctrl)
8143c7c1e45SKamal Dasu {
8153c7c1e45SKamal Dasu 	u64 err_addr;
8163c7c1e45SKamal Dasu 
8173c7c1e45SKamal Dasu 	err_addr = brcmnand_read_reg(ctrl, BRCMNAND_CORR_ADDR);
8183c7c1e45SKamal Dasu 	err_addr |= ((u64)(brcmnand_read_reg(ctrl,
8193c7c1e45SKamal Dasu 					     BRCMNAND_CORR_EXT_ADDR)
8203c7c1e45SKamal Dasu 					     & 0xffff) << 32);
8213c7c1e45SKamal Dasu 
8223c7c1e45SKamal Dasu 	return err_addr;
8233c7c1e45SKamal Dasu }
8243c7c1e45SKamal Dasu 
8253c7c1e45SKamal Dasu static void brcmnand_set_cmd_addr(struct mtd_info *mtd, u64 addr)
8263c7c1e45SKamal Dasu {
8273c7c1e45SKamal Dasu 	struct nand_chip *chip =  mtd_to_nand(mtd);
8283c7c1e45SKamal Dasu 	struct brcmnand_host *host = nand_get_controller_data(chip);
8293c7c1e45SKamal Dasu 	struct brcmnand_controller *ctrl = host->ctrl;
8303c7c1e45SKamal Dasu 
8313c7c1e45SKamal Dasu 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
8323c7c1e45SKamal Dasu 			   (host->cs << 16) | ((addr >> 32) & 0xffff));
8333c7c1e45SKamal Dasu 	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
8343c7c1e45SKamal Dasu 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
8353c7c1e45SKamal Dasu 			   lower_32_bits(addr));
8363c7c1e45SKamal Dasu 	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
8373c7c1e45SKamal Dasu }
8383c7c1e45SKamal Dasu 
83993db446aSBoris Brezillon static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
84093db446aSBoris Brezillon 				     enum brcmnand_cs_reg reg)
84193db446aSBoris Brezillon {
84293db446aSBoris Brezillon 	u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
84393db446aSBoris Brezillon 	u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
84493db446aSBoris Brezillon 	u8 cs_offs;
84593db446aSBoris Brezillon 
84693db446aSBoris Brezillon 	if (cs == 0 && ctrl->cs0_offsets)
84793db446aSBoris Brezillon 		cs_offs = ctrl->cs0_offsets[reg];
84893db446aSBoris Brezillon 	else
84993db446aSBoris Brezillon 		cs_offs = ctrl->cs_offsets[reg];
85093db446aSBoris Brezillon 
85193db446aSBoris Brezillon 	if (cs && offs_cs1)
85293db446aSBoris Brezillon 		return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
85393db446aSBoris Brezillon 
85493db446aSBoris Brezillon 	return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
85593db446aSBoris Brezillon }
85693db446aSBoris Brezillon 
85793db446aSBoris Brezillon static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
85893db446aSBoris Brezillon {
85993db446aSBoris Brezillon 	if (ctrl->nand_version < 0x0600)
86093db446aSBoris Brezillon 		return 1;
86193db446aSBoris Brezillon 	return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
86293db446aSBoris Brezillon }
86393db446aSBoris Brezillon 
86493db446aSBoris Brezillon static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
86593db446aSBoris Brezillon {
86693db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
86793db446aSBoris Brezillon 	unsigned int shift = 0, bits;
86893db446aSBoris Brezillon 	enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
86993db446aSBoris Brezillon 	int cs = host->cs;
87093db446aSBoris Brezillon 
8717e7c7df5SÁlvaro Fernández Rojas 	if (!ctrl->reg_offsets[reg])
8727e7c7df5SÁlvaro Fernández Rojas 		return;
8737e7c7df5SÁlvaro Fernández Rojas 
8740c06da57SKamal Dasu 	if (ctrl->nand_version == 0x0702)
87593db446aSBoris Brezillon 		bits = 7;
87693db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0600)
87793db446aSBoris Brezillon 		bits = 6;
87893db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0500)
87993db446aSBoris Brezillon 		bits = 5;
88093db446aSBoris Brezillon 	else
88193db446aSBoris Brezillon 		bits = 4;
88293db446aSBoris Brezillon 
88393db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0702) {
88493db446aSBoris Brezillon 		if (cs >= 4)
88593db446aSBoris Brezillon 			reg = BRCMNAND_CORR_THRESHOLD_EXT;
88693db446aSBoris Brezillon 		shift = (cs % 4) * bits;
88793db446aSBoris Brezillon 	} else if (ctrl->nand_version >= 0x0600) {
88893db446aSBoris Brezillon 		if (cs >= 5)
88993db446aSBoris Brezillon 			reg = BRCMNAND_CORR_THRESHOLD_EXT;
89093db446aSBoris Brezillon 		shift = (cs % 5) * bits;
89193db446aSBoris Brezillon 	}
89293db446aSBoris Brezillon 	brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
89393db446aSBoris Brezillon }
89493db446aSBoris Brezillon 
89593db446aSBoris Brezillon static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
89693db446aSBoris Brezillon {
89793db446aSBoris Brezillon 	if (ctrl->nand_version < 0x0602)
89893db446aSBoris Brezillon 		return 24;
89993db446aSBoris Brezillon 	return 0;
90093db446aSBoris Brezillon }
90193db446aSBoris Brezillon 
90293db446aSBoris Brezillon /***********************************************************************
90393db446aSBoris Brezillon  * NAND ACC CONTROL bitfield
90493db446aSBoris Brezillon  *
90593db446aSBoris Brezillon  * Some bits have remained constant throughout hardware revision, while
90693db446aSBoris Brezillon  * others have shifted around.
90793db446aSBoris Brezillon  ***********************************************************************/
90893db446aSBoris Brezillon 
90993db446aSBoris Brezillon /* Constant for all versions (where supported) */
91093db446aSBoris Brezillon enum {
91193db446aSBoris Brezillon 	/* See BRCMNAND_HAS_CACHE_MODE */
91293db446aSBoris Brezillon 	ACC_CONTROL_CACHE_MODE				= BIT(22),
91393db446aSBoris Brezillon 
91493db446aSBoris Brezillon 	/* See BRCMNAND_HAS_PREFETCH */
91593db446aSBoris Brezillon 	ACC_CONTROL_PREFETCH				= BIT(23),
91693db446aSBoris Brezillon 
91793db446aSBoris Brezillon 	ACC_CONTROL_PAGE_HIT				= BIT(24),
91893db446aSBoris Brezillon 	ACC_CONTROL_WR_PREEMPT				= BIT(25),
91993db446aSBoris Brezillon 	ACC_CONTROL_PARTIAL_PAGE			= BIT(26),
92093db446aSBoris Brezillon 	ACC_CONTROL_RD_ERASED				= BIT(27),
92193db446aSBoris Brezillon 	ACC_CONTROL_FAST_PGM_RDIN			= BIT(28),
92293db446aSBoris Brezillon 	ACC_CONTROL_WR_ECC				= BIT(30),
92393db446aSBoris Brezillon 	ACC_CONTROL_RD_ECC				= BIT(31),
92493db446aSBoris Brezillon };
92593db446aSBoris Brezillon 
92693db446aSBoris Brezillon static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
92793db446aSBoris Brezillon {
9280c06da57SKamal Dasu 	if (ctrl->nand_version == 0x0702)
92993db446aSBoris Brezillon 		return GENMASK(7, 0);
93093db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0600)
93193db446aSBoris Brezillon 		return GENMASK(6, 0);
9327e7c7df5SÁlvaro Fernández Rojas 	else if (ctrl->nand_version >= 0x0303)
93393db446aSBoris Brezillon 		return GENMASK(5, 0);
9347e7c7df5SÁlvaro Fernández Rojas 	else
9357e7c7df5SÁlvaro Fernández Rojas 		return GENMASK(4, 0);
93693db446aSBoris Brezillon }
93793db446aSBoris Brezillon 
93893db446aSBoris Brezillon #define NAND_ACC_CONTROL_ECC_SHIFT	16
93993db446aSBoris Brezillon #define NAND_ACC_CONTROL_ECC_EXT_SHIFT	13
94093db446aSBoris Brezillon 
94193db446aSBoris Brezillon static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
94293db446aSBoris Brezillon {
94393db446aSBoris Brezillon 	u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
94493db446aSBoris Brezillon 
94593db446aSBoris Brezillon 	mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
94693db446aSBoris Brezillon 
94793db446aSBoris Brezillon 	/* v7.2 includes additional ECC levels */
94893db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0702)
94993db446aSBoris Brezillon 		mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
95093db446aSBoris Brezillon 
95193db446aSBoris Brezillon 	return mask;
95293db446aSBoris Brezillon }
95393db446aSBoris Brezillon 
95493db446aSBoris Brezillon static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
95593db446aSBoris Brezillon {
95693db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
95793db446aSBoris Brezillon 	u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
95893db446aSBoris Brezillon 	u32 acc_control = nand_readreg(ctrl, offs);
95993db446aSBoris Brezillon 	u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
96093db446aSBoris Brezillon 
96193db446aSBoris Brezillon 	if (en) {
96293db446aSBoris Brezillon 		acc_control |= ecc_flags; /* enable RD/WR ECC */
96393db446aSBoris Brezillon 		acc_control |= host->hwcfg.ecc_level
96493db446aSBoris Brezillon 			       << NAND_ACC_CONTROL_ECC_SHIFT;
96593db446aSBoris Brezillon 	} else {
96693db446aSBoris Brezillon 		acc_control &= ~ecc_flags; /* disable RD/WR ECC */
96793db446aSBoris Brezillon 		acc_control &= ~brcmnand_ecc_level_mask(ctrl);
96893db446aSBoris Brezillon 	}
96993db446aSBoris Brezillon 
97093db446aSBoris Brezillon 	nand_writereg(ctrl, offs, acc_control);
97193db446aSBoris Brezillon }
97293db446aSBoris Brezillon 
97393db446aSBoris Brezillon static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
97493db446aSBoris Brezillon {
97593db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0702)
97693db446aSBoris Brezillon 		return 9;
97793db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0600)
97893db446aSBoris Brezillon 		return 7;
97993db446aSBoris Brezillon 	else if (ctrl->nand_version >= 0x0500)
98093db446aSBoris Brezillon 		return 6;
98193db446aSBoris Brezillon 	else
98293db446aSBoris Brezillon 		return -1;
98393db446aSBoris Brezillon }
98493db446aSBoris Brezillon 
98593db446aSBoris Brezillon static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
98693db446aSBoris Brezillon {
98793db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
98893db446aSBoris Brezillon 	int shift = brcmnand_sector_1k_shift(ctrl);
98993db446aSBoris Brezillon 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
99093db446aSBoris Brezillon 						  BRCMNAND_CS_ACC_CONTROL);
99193db446aSBoris Brezillon 
99293db446aSBoris Brezillon 	if (shift < 0)
99393db446aSBoris Brezillon 		return 0;
99493db446aSBoris Brezillon 
99593db446aSBoris Brezillon 	return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
99693db446aSBoris Brezillon }
99793db446aSBoris Brezillon 
99893db446aSBoris Brezillon static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
99993db446aSBoris Brezillon {
100093db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
100193db446aSBoris Brezillon 	int shift = brcmnand_sector_1k_shift(ctrl);
100293db446aSBoris Brezillon 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
100393db446aSBoris Brezillon 						  BRCMNAND_CS_ACC_CONTROL);
100493db446aSBoris Brezillon 	u32 tmp;
100593db446aSBoris Brezillon 
100693db446aSBoris Brezillon 	if (shift < 0)
100793db446aSBoris Brezillon 		return;
100893db446aSBoris Brezillon 
100993db446aSBoris Brezillon 	tmp = nand_readreg(ctrl, acc_control_offs);
101093db446aSBoris Brezillon 	tmp &= ~(1 << shift);
101193db446aSBoris Brezillon 	tmp |= (!!val) << shift;
101293db446aSBoris Brezillon 	nand_writereg(ctrl, acc_control_offs, tmp);
101393db446aSBoris Brezillon }
101493db446aSBoris Brezillon 
101593db446aSBoris Brezillon /***********************************************************************
101693db446aSBoris Brezillon  * CS_NAND_SELECT
101793db446aSBoris Brezillon  ***********************************************************************/
101893db446aSBoris Brezillon 
101993db446aSBoris Brezillon enum {
102093db446aSBoris Brezillon 	CS_SELECT_NAND_WP			= BIT(29),
102193db446aSBoris Brezillon 	CS_SELECT_AUTO_DEVICE_ID_CFG		= BIT(30),
102293db446aSBoris Brezillon };
102393db446aSBoris Brezillon 
102493db446aSBoris Brezillon static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
102593db446aSBoris Brezillon 				    u32 mask, u32 expected_val,
102693db446aSBoris Brezillon 				    unsigned long timeout_ms)
102793db446aSBoris Brezillon {
102893db446aSBoris Brezillon 	unsigned long limit;
102993db446aSBoris Brezillon 	u32 val;
103093db446aSBoris Brezillon 
103193db446aSBoris Brezillon 	if (!timeout_ms)
103293db446aSBoris Brezillon 		timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
103393db446aSBoris Brezillon 
103493db446aSBoris Brezillon 	limit = jiffies + msecs_to_jiffies(timeout_ms);
103593db446aSBoris Brezillon 	do {
103693db446aSBoris Brezillon 		val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
103793db446aSBoris Brezillon 		if ((val & mask) == expected_val)
103893db446aSBoris Brezillon 			return 0;
103993db446aSBoris Brezillon 
104093db446aSBoris Brezillon 		cpu_relax();
104193db446aSBoris Brezillon 	} while (time_after(limit, jiffies));
104293db446aSBoris Brezillon 
104393db446aSBoris Brezillon 	dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
104493db446aSBoris Brezillon 		 expected_val, val & mask);
104593db446aSBoris Brezillon 
104693db446aSBoris Brezillon 	return -ETIMEDOUT;
104793db446aSBoris Brezillon }
104893db446aSBoris Brezillon 
104993db446aSBoris Brezillon static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
105093db446aSBoris Brezillon {
105193db446aSBoris Brezillon 	u32 val = en ? CS_SELECT_NAND_WP : 0;
105293db446aSBoris Brezillon 
105393db446aSBoris Brezillon 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
105493db446aSBoris Brezillon }
105593db446aSBoris Brezillon 
105693db446aSBoris Brezillon /***********************************************************************
105793db446aSBoris Brezillon  * Flash DMA
105893db446aSBoris Brezillon  ***********************************************************************/
105993db446aSBoris Brezillon 
106093db446aSBoris Brezillon static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
106193db446aSBoris Brezillon {
106293db446aSBoris Brezillon 	return ctrl->flash_dma_base;
106393db446aSBoris Brezillon }
106493db446aSBoris Brezillon 
1065a5d53ad2SKamal Dasu static inline bool has_edu(struct brcmnand_controller *ctrl)
1066a5d53ad2SKamal Dasu {
1067a5d53ad2SKamal Dasu 	return ctrl->edu_base;
1068a5d53ad2SKamal Dasu }
1069a5d53ad2SKamal Dasu 
1070a5d53ad2SKamal Dasu static inline bool use_dma(struct brcmnand_controller *ctrl)
1071a5d53ad2SKamal Dasu {
1072a5d53ad2SKamal Dasu 	return has_flash_dma(ctrl) || has_edu(ctrl);
1073a5d53ad2SKamal Dasu }
1074a5d53ad2SKamal Dasu 
1075c1ac2dc3SKamal Dasu static inline void disable_ctrl_irqs(struct brcmnand_controller *ctrl)
1076c1ac2dc3SKamal Dasu {
1077c1ac2dc3SKamal Dasu 	if (ctrl->pio_poll_mode)
1078c1ac2dc3SKamal Dasu 		return;
1079c1ac2dc3SKamal Dasu 
1080c1ac2dc3SKamal Dasu 	if (has_flash_dma(ctrl)) {
10810e04b2ffSFlorian Fainelli 		ctrl->flash_dma_base = NULL;
1082c1ac2dc3SKamal Dasu 		disable_irq(ctrl->dma_irq);
1083c1ac2dc3SKamal Dasu 	}
1084c1ac2dc3SKamal Dasu 
1085c1ac2dc3SKamal Dasu 	disable_irq(ctrl->irq);
1086c1ac2dc3SKamal Dasu 	ctrl->pio_poll_mode = true;
1087c1ac2dc3SKamal Dasu }
1088c1ac2dc3SKamal Dasu 
108993db446aSBoris Brezillon static inline bool flash_dma_buf_ok(const void *buf)
109093db446aSBoris Brezillon {
109193db446aSBoris Brezillon 	return buf && !is_vmalloc_addr(buf) &&
109293db446aSBoris Brezillon 		likely(IS_ALIGNED((uintptr_t)buf, 4));
109393db446aSBoris Brezillon }
109493db446aSBoris Brezillon 
10950c06da57SKamal Dasu static inline void flash_dma_writel(struct brcmnand_controller *ctrl,
10960c06da57SKamal Dasu 				    enum flash_dma_reg dma_reg, u32 val)
109793db446aSBoris Brezillon {
10980c06da57SKamal Dasu 	u16 offs = ctrl->flash_dma_offsets[dma_reg];
10990c06da57SKamal Dasu 
110093db446aSBoris Brezillon 	brcmnand_writel(val, ctrl->flash_dma_base + offs);
110193db446aSBoris Brezillon }
110293db446aSBoris Brezillon 
11030c06da57SKamal Dasu static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl,
11040c06da57SKamal Dasu 				  enum flash_dma_reg dma_reg)
110593db446aSBoris Brezillon {
11060c06da57SKamal Dasu 	u16 offs = ctrl->flash_dma_offsets[dma_reg];
11070c06da57SKamal Dasu 
110893db446aSBoris Brezillon 	return brcmnand_readl(ctrl->flash_dma_base + offs);
110993db446aSBoris Brezillon }
111093db446aSBoris Brezillon 
111193db446aSBoris Brezillon /* Low-level operation types: command, address, write, or read */
111293db446aSBoris Brezillon enum brcmnand_llop_type {
111393db446aSBoris Brezillon 	LL_OP_CMD,
111493db446aSBoris Brezillon 	LL_OP_ADDR,
111593db446aSBoris Brezillon 	LL_OP_WR,
111693db446aSBoris Brezillon 	LL_OP_RD,
111793db446aSBoris Brezillon };
111893db446aSBoris Brezillon 
111993db446aSBoris Brezillon /***********************************************************************
112093db446aSBoris Brezillon  * Internal support functions
112193db446aSBoris Brezillon  ***********************************************************************/
112293db446aSBoris Brezillon 
112393db446aSBoris Brezillon static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
112493db446aSBoris Brezillon 				  struct brcmnand_cfg *cfg)
112593db446aSBoris Brezillon {
112693db446aSBoris Brezillon 	if (ctrl->nand_version <= 0x0701)
112793db446aSBoris Brezillon 		return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
112893db446aSBoris Brezillon 			cfg->ecc_level == 15;
112993db446aSBoris Brezillon 	else
113093db446aSBoris Brezillon 		return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
113193db446aSBoris Brezillon 			cfg->ecc_level == 15) ||
113293db446aSBoris Brezillon 			(cfg->spare_area_size == 28 && cfg->ecc_level == 16));
113393db446aSBoris Brezillon }
113493db446aSBoris Brezillon 
113593db446aSBoris Brezillon /*
113693db446aSBoris Brezillon  * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
113793db446aSBoris Brezillon  * the layout/configuration.
113893db446aSBoris Brezillon  * Returns -ERRCODE on failure.
113993db446aSBoris Brezillon  */
114093db446aSBoris Brezillon static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
114193db446aSBoris Brezillon 					  struct mtd_oob_region *oobregion)
114293db446aSBoris Brezillon {
114393db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
114493db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
114593db446aSBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
114693db446aSBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
114793db446aSBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
114893db446aSBoris Brezillon 
114993db446aSBoris Brezillon 	if (section >= sectors)
115093db446aSBoris Brezillon 		return -ERANGE;
115193db446aSBoris Brezillon 
115293db446aSBoris Brezillon 	oobregion->offset = (section * sas) + 6;
115393db446aSBoris Brezillon 	oobregion->length = 3;
115493db446aSBoris Brezillon 
115593db446aSBoris Brezillon 	return 0;
115693db446aSBoris Brezillon }
115793db446aSBoris Brezillon 
115893db446aSBoris Brezillon static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
115993db446aSBoris Brezillon 					   struct mtd_oob_region *oobregion)
116093db446aSBoris Brezillon {
116193db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
116293db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
116393db446aSBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
116493db446aSBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
116593db446aSBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
1166d00358d7SÁlvaro Fernández Rojas 	u32 next;
116793db446aSBoris Brezillon 
1168d00358d7SÁlvaro Fernández Rojas 	if (section > sectors)
116993db446aSBoris Brezillon 		return -ERANGE;
117093db446aSBoris Brezillon 
1171d00358d7SÁlvaro Fernández Rojas 	next = (section * sas);
1172d00358d7SÁlvaro Fernández Rojas 	if (section < sectors)
1173d00358d7SÁlvaro Fernández Rojas 		next += 6;
117493db446aSBoris Brezillon 
1175d00358d7SÁlvaro Fernández Rojas 	if (section) {
1176d00358d7SÁlvaro Fernández Rojas 		oobregion->offset = ((section - 1) * sas) + 9;
117793db446aSBoris Brezillon 	} else {
1178130bbde4SÁlvaro Fernández Rojas 		if (cfg->page_size > 512) {
1179d00358d7SÁlvaro Fernández Rojas 			/* Large page NAND uses first 2 bytes for BBI */
1180d00358d7SÁlvaro Fernández Rojas 			oobregion->offset = 2;
1181130bbde4SÁlvaro Fernández Rojas 		} else {
1182d00358d7SÁlvaro Fernández Rojas 			/* Small page NAND uses last byte before ECC for BBI */
1183d00358d7SÁlvaro Fernández Rojas 			oobregion->offset = 0;
1184d00358d7SÁlvaro Fernández Rojas 			next--;
118593db446aSBoris Brezillon 		}
118693db446aSBoris Brezillon 	}
118793db446aSBoris Brezillon 
1188d00358d7SÁlvaro Fernández Rojas 	oobregion->length = next - oobregion->offset;
118993db446aSBoris Brezillon 
119093db446aSBoris Brezillon 	return 0;
119193db446aSBoris Brezillon }
119293db446aSBoris Brezillon 
119393db446aSBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
119493db446aSBoris Brezillon 	.ecc = brcmnand_hamming_ooblayout_ecc,
119593db446aSBoris Brezillon 	.free = brcmnand_hamming_ooblayout_free,
119693db446aSBoris Brezillon };
119793db446aSBoris Brezillon 
119893db446aSBoris Brezillon static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
119993db446aSBoris Brezillon 				      struct mtd_oob_region *oobregion)
120093db446aSBoris Brezillon {
120193db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
120293db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
120393db446aSBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
120493db446aSBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
120593db446aSBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
120693db446aSBoris Brezillon 
120793db446aSBoris Brezillon 	if (section >= sectors)
120893db446aSBoris Brezillon 		return -ERANGE;
120993db446aSBoris Brezillon 
1210917cc594SKamal Dasu 	oobregion->offset = ((section + 1) * sas) - chip->ecc.bytes;
121193db446aSBoris Brezillon 	oobregion->length = chip->ecc.bytes;
121293db446aSBoris Brezillon 
121393db446aSBoris Brezillon 	return 0;
121493db446aSBoris Brezillon }
121593db446aSBoris Brezillon 
121693db446aSBoris Brezillon static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
121793db446aSBoris Brezillon 					  struct mtd_oob_region *oobregion)
121893db446aSBoris Brezillon {
121993db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
122093db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
122193db446aSBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
122293db446aSBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
122393db446aSBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
122493db446aSBoris Brezillon 
122593db446aSBoris Brezillon 	if (section >= sectors)
122693db446aSBoris Brezillon 		return -ERANGE;
122793db446aSBoris Brezillon 
122893db446aSBoris Brezillon 	if (sas <= chip->ecc.bytes)
122993db446aSBoris Brezillon 		return 0;
123093db446aSBoris Brezillon 
123193db446aSBoris Brezillon 	oobregion->offset = section * sas;
123293db446aSBoris Brezillon 	oobregion->length = sas - chip->ecc.bytes;
123393db446aSBoris Brezillon 
123493db446aSBoris Brezillon 	if (!section) {
123593db446aSBoris Brezillon 		oobregion->offset++;
123693db446aSBoris Brezillon 		oobregion->length--;
123793db446aSBoris Brezillon 	}
123893db446aSBoris Brezillon 
123993db446aSBoris Brezillon 	return 0;
124093db446aSBoris Brezillon }
124193db446aSBoris Brezillon 
124293db446aSBoris Brezillon static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
124393db446aSBoris Brezillon 					  struct mtd_oob_region *oobregion)
124493db446aSBoris Brezillon {
124593db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
124693db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
124793db446aSBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
124893db446aSBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
124993db446aSBoris Brezillon 
125093db446aSBoris Brezillon 	if (section > 1 || sas - chip->ecc.bytes < 6 ||
125193db446aSBoris Brezillon 	    (section && sas - chip->ecc.bytes == 6))
125293db446aSBoris Brezillon 		return -ERANGE;
125393db446aSBoris Brezillon 
125493db446aSBoris Brezillon 	if (!section) {
125593db446aSBoris Brezillon 		oobregion->offset = 0;
125693db446aSBoris Brezillon 		oobregion->length = 5;
125793db446aSBoris Brezillon 	} else {
125893db446aSBoris Brezillon 		oobregion->offset = 6;
125993db446aSBoris Brezillon 		oobregion->length = sas - chip->ecc.bytes - 6;
126093db446aSBoris Brezillon 	}
126193db446aSBoris Brezillon 
126293db446aSBoris Brezillon 	return 0;
126393db446aSBoris Brezillon }
126493db446aSBoris Brezillon 
126593db446aSBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
126693db446aSBoris Brezillon 	.ecc = brcmnand_bch_ooblayout_ecc,
126793db446aSBoris Brezillon 	.free = brcmnand_bch_ooblayout_free_lp,
126893db446aSBoris Brezillon };
126993db446aSBoris Brezillon 
127093db446aSBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
127193db446aSBoris Brezillon 	.ecc = brcmnand_bch_ooblayout_ecc,
127293db446aSBoris Brezillon 	.free = brcmnand_bch_ooblayout_free_sp,
127393db446aSBoris Brezillon };
127493db446aSBoris Brezillon 
127593db446aSBoris Brezillon static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
127693db446aSBoris Brezillon {
127793db446aSBoris Brezillon 	struct brcmnand_cfg *p = &host->hwcfg;
127893db446aSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
127993db446aSBoris Brezillon 	struct nand_ecc_ctrl *ecc = &host->chip.ecc;
128093db446aSBoris Brezillon 	unsigned int ecc_level = p->ecc_level;
128193db446aSBoris Brezillon 	int sas = p->spare_area_size << p->sector_size_1k;
128293db446aSBoris Brezillon 	int sectors = p->page_size / (512 << p->sector_size_1k);
128393db446aSBoris Brezillon 
128493db446aSBoris Brezillon 	if (p->sector_size_1k)
128593db446aSBoris Brezillon 		ecc_level <<= 1;
128693db446aSBoris Brezillon 
128793db446aSBoris Brezillon 	if (is_hamming_ecc(host->ctrl, p)) {
128893db446aSBoris Brezillon 		ecc->bytes = 3 * sectors;
128993db446aSBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
129093db446aSBoris Brezillon 		return 0;
129193db446aSBoris Brezillon 	}
129293db446aSBoris Brezillon 
129393db446aSBoris Brezillon 	/*
129493db446aSBoris Brezillon 	 * CONTROLLER_VERSION:
129593db446aSBoris Brezillon 	 *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
129693db446aSBoris Brezillon 	 *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
129793db446aSBoris Brezillon 	 * But we will just be conservative.
129893db446aSBoris Brezillon 	 */
129993db446aSBoris Brezillon 	ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
130093db446aSBoris Brezillon 	if (p->page_size == 512)
130193db446aSBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
130293db446aSBoris Brezillon 	else
130393db446aSBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
130493db446aSBoris Brezillon 
130593db446aSBoris Brezillon 	if (ecc->bytes >= sas) {
130693db446aSBoris Brezillon 		dev_err(&host->pdev->dev,
130793db446aSBoris Brezillon 			"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
130893db446aSBoris Brezillon 			ecc->bytes, sas);
130993db446aSBoris Brezillon 		return -EINVAL;
131093db446aSBoris Brezillon 	}
131193db446aSBoris Brezillon 
131293db446aSBoris Brezillon 	return 0;
131393db446aSBoris Brezillon }
131493db446aSBoris Brezillon 
131593db446aSBoris Brezillon static void brcmnand_wp(struct mtd_info *mtd, int wp)
131693db446aSBoris Brezillon {
131793db446aSBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
131893db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
131993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
132093db446aSBoris Brezillon 
132193db446aSBoris Brezillon 	if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
132293db446aSBoris Brezillon 		static int old_wp = -1;
132393db446aSBoris Brezillon 		int ret;
132493db446aSBoris Brezillon 
132593db446aSBoris Brezillon 		if (old_wp != wp) {
132693db446aSBoris Brezillon 			dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
132793db446aSBoris Brezillon 			old_wp = wp;
132893db446aSBoris Brezillon 		}
132993db446aSBoris Brezillon 
133093db446aSBoris Brezillon 		/*
133193db446aSBoris Brezillon 		 * make sure ctrl/flash ready before and after
133293db446aSBoris Brezillon 		 * changing state of #WP pin
133393db446aSBoris Brezillon 		 */
133493db446aSBoris Brezillon 		ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
133593db446aSBoris Brezillon 					       NAND_STATUS_READY,
133693db446aSBoris Brezillon 					       NAND_CTRL_RDY |
133793db446aSBoris Brezillon 					       NAND_STATUS_READY, 0);
133893db446aSBoris Brezillon 		if (ret)
133993db446aSBoris Brezillon 			return;
134093db446aSBoris Brezillon 
134193db446aSBoris Brezillon 		brcmnand_set_wp(ctrl, wp);
134293db446aSBoris Brezillon 		nand_status_op(chip, NULL);
134393db446aSBoris Brezillon 		/* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
134493db446aSBoris Brezillon 		ret = bcmnand_ctrl_poll_status(ctrl,
134593db446aSBoris Brezillon 					       NAND_CTRL_RDY |
134693db446aSBoris Brezillon 					       NAND_STATUS_READY |
134793db446aSBoris Brezillon 					       NAND_STATUS_WP,
134893db446aSBoris Brezillon 					       NAND_CTRL_RDY |
134993db446aSBoris Brezillon 					       NAND_STATUS_READY |
135093db446aSBoris Brezillon 					       (wp ? 0 : NAND_STATUS_WP), 0);
135193db446aSBoris Brezillon 
135293db446aSBoris Brezillon 		if (ret)
135393db446aSBoris Brezillon 			dev_err_ratelimited(&host->pdev->dev,
135493db446aSBoris Brezillon 					    "nand #WP expected %s\n",
135593db446aSBoris Brezillon 					    wp ? "on" : "off");
135693db446aSBoris Brezillon 	}
135793db446aSBoris Brezillon }
135893db446aSBoris Brezillon 
135993db446aSBoris Brezillon /* Helper functions for reading and writing OOB registers */
136093db446aSBoris Brezillon static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
136193db446aSBoris Brezillon {
136293db446aSBoris Brezillon 	u16 offset0, offset10, reg_offs;
136393db446aSBoris Brezillon 
136493db446aSBoris Brezillon 	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
136593db446aSBoris Brezillon 	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
136693db446aSBoris Brezillon 
136793db446aSBoris Brezillon 	if (offs >= ctrl->max_oob)
136893db446aSBoris Brezillon 		return 0x77;
136993db446aSBoris Brezillon 
137093db446aSBoris Brezillon 	if (offs >= 16 && offset10)
137193db446aSBoris Brezillon 		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
137293db446aSBoris Brezillon 	else
137393db446aSBoris Brezillon 		reg_offs = offset0 + (offs & ~0x03);
137493db446aSBoris Brezillon 
137593db446aSBoris Brezillon 	return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
137693db446aSBoris Brezillon }
137793db446aSBoris Brezillon 
137893db446aSBoris Brezillon static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
137993db446aSBoris Brezillon 				 u32 data)
138093db446aSBoris Brezillon {
138193db446aSBoris Brezillon 	u16 offset0, offset10, reg_offs;
138293db446aSBoris Brezillon 
138393db446aSBoris Brezillon 	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
138493db446aSBoris Brezillon 	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
138593db446aSBoris Brezillon 
138693db446aSBoris Brezillon 	if (offs >= ctrl->max_oob)
138793db446aSBoris Brezillon 		return;
138893db446aSBoris Brezillon 
138993db446aSBoris Brezillon 	if (offs >= 16 && offset10)
139093db446aSBoris Brezillon 		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
139193db446aSBoris Brezillon 	else
139293db446aSBoris Brezillon 		reg_offs = offset0 + (offs & ~0x03);
139393db446aSBoris Brezillon 
139493db446aSBoris Brezillon 	nand_writereg(ctrl, reg_offs, data);
139593db446aSBoris Brezillon }
139693db446aSBoris Brezillon 
139793db446aSBoris Brezillon /*
139893db446aSBoris Brezillon  * read_oob_from_regs - read data from OOB registers
139993db446aSBoris Brezillon  * @ctrl: NAND controller
140093db446aSBoris Brezillon  * @i: sub-page sector index
140193db446aSBoris Brezillon  * @oob: buffer to read to
140293db446aSBoris Brezillon  * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
140393db446aSBoris Brezillon  * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
140493db446aSBoris Brezillon  */
140593db446aSBoris Brezillon static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
140693db446aSBoris Brezillon 			      int sas, int sector_1k)
140793db446aSBoris Brezillon {
140893db446aSBoris Brezillon 	int tbytes = sas << sector_1k;
140993db446aSBoris Brezillon 	int j;
141093db446aSBoris Brezillon 
141193db446aSBoris Brezillon 	/* Adjust OOB values for 1K sector size */
141293db446aSBoris Brezillon 	if (sector_1k && (i & 0x01))
141393db446aSBoris Brezillon 		tbytes = max(0, tbytes - (int)ctrl->max_oob);
141493db446aSBoris Brezillon 	tbytes = min_t(int, tbytes, ctrl->max_oob);
141593db446aSBoris Brezillon 
141693db446aSBoris Brezillon 	for (j = 0; j < tbytes; j++)
141793db446aSBoris Brezillon 		oob[j] = oob_reg_read(ctrl, j);
141893db446aSBoris Brezillon 	return tbytes;
141993db446aSBoris Brezillon }
142093db446aSBoris Brezillon 
142193db446aSBoris Brezillon /*
142293db446aSBoris Brezillon  * write_oob_to_regs - write data to OOB registers
142393db446aSBoris Brezillon  * @i: sub-page sector index
142493db446aSBoris Brezillon  * @oob: buffer to write from
142593db446aSBoris Brezillon  * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
142693db446aSBoris Brezillon  * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
142793db446aSBoris Brezillon  */
142893db446aSBoris Brezillon static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
142993db446aSBoris Brezillon 			     const u8 *oob, int sas, int sector_1k)
143093db446aSBoris Brezillon {
143193db446aSBoris Brezillon 	int tbytes = sas << sector_1k;
143293db446aSBoris Brezillon 	int j;
143393db446aSBoris Brezillon 
143493db446aSBoris Brezillon 	/* Adjust OOB values for 1K sector size */
143593db446aSBoris Brezillon 	if (sector_1k && (i & 0x01))
143693db446aSBoris Brezillon 		tbytes = max(0, tbytes - (int)ctrl->max_oob);
143793db446aSBoris Brezillon 	tbytes = min_t(int, tbytes, ctrl->max_oob);
143893db446aSBoris Brezillon 
143993db446aSBoris Brezillon 	for (j = 0; j < tbytes; j += 4)
144093db446aSBoris Brezillon 		oob_reg_write(ctrl, j,
144193db446aSBoris Brezillon 				(oob[j + 0] << 24) |
144293db446aSBoris Brezillon 				(oob[j + 1] << 16) |
144393db446aSBoris Brezillon 				(oob[j + 2] <<  8) |
144493db446aSBoris Brezillon 				(oob[j + 3] <<  0));
144593db446aSBoris Brezillon 	return tbytes;
144693db446aSBoris Brezillon }
144793db446aSBoris Brezillon 
1448a5d53ad2SKamal Dasu static void brcmnand_edu_init(struct brcmnand_controller *ctrl)
1449a5d53ad2SKamal Dasu {
1450a5d53ad2SKamal Dasu 	/* initialize edu */
1451a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_ERR_STATUS, 0);
1452a5d53ad2SKamal Dasu 	edu_readl(ctrl, EDU_ERR_STATUS);
1453a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_DONE, 0);
1454a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_DONE, 0);
1455a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_DONE, 0);
1456a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_DONE, 0);
1457a5d53ad2SKamal Dasu 	edu_readl(ctrl, EDU_DONE);
1458a5d53ad2SKamal Dasu }
1459a5d53ad2SKamal Dasu 
1460a5d53ad2SKamal Dasu /* edu irq */
1461a5d53ad2SKamal Dasu static irqreturn_t brcmnand_edu_irq(int irq, void *data)
1462a5d53ad2SKamal Dasu {
1463a5d53ad2SKamal Dasu 	struct brcmnand_controller *ctrl = data;
1464a5d53ad2SKamal Dasu 
1465a5d53ad2SKamal Dasu 	if (ctrl->edu_count) {
1466a5d53ad2SKamal Dasu 		ctrl->edu_count--;
1467a5d53ad2SKamal Dasu 		while (!(edu_readl(ctrl, EDU_DONE) & EDU_DONE_MASK))
1468a5d53ad2SKamal Dasu 			udelay(1);
1469a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_DONE, 0);
1470a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_DONE);
1471a5d53ad2SKamal Dasu 	}
1472a5d53ad2SKamal Dasu 
1473a5d53ad2SKamal Dasu 	if (ctrl->edu_count) {
1474a5d53ad2SKamal Dasu 		ctrl->edu_dram_addr += FC_BYTES;
1475a5d53ad2SKamal Dasu 		ctrl->edu_ext_addr += FC_BYTES;
1476a5d53ad2SKamal Dasu 
1477a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr);
1478a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_DRAM_ADDR);
1479a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr);
1480a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_EXT_ADDR);
1481a5d53ad2SKamal Dasu 
1482a5d53ad2SKamal Dasu 		mb(); /* flush previous writes */
1483a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd);
1484a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_CMD);
1485a5d53ad2SKamal Dasu 
1486a5d53ad2SKamal Dasu 		return IRQ_HANDLED;
1487a5d53ad2SKamal Dasu 	}
1488a5d53ad2SKamal Dasu 
1489a5d53ad2SKamal Dasu 	complete(&ctrl->edu_done);
1490a5d53ad2SKamal Dasu 
1491a5d53ad2SKamal Dasu 	return IRQ_HANDLED;
1492a5d53ad2SKamal Dasu }
1493a5d53ad2SKamal Dasu 
149493db446aSBoris Brezillon static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
149593db446aSBoris Brezillon {
149693db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = data;
149793db446aSBoris Brezillon 
149893db446aSBoris Brezillon 	/* Discard all NAND_CTLRDY interrupts during DMA */
149993db446aSBoris Brezillon 	if (ctrl->dma_pending)
150093db446aSBoris Brezillon 		return IRQ_HANDLED;
150193db446aSBoris Brezillon 
1502a5d53ad2SKamal Dasu 	/* check if you need to piggy back on the ctrlrdy irq */
1503a5d53ad2SKamal Dasu 	if (ctrl->edu_pending) {
1504a5d53ad2SKamal Dasu 		if (irq == ctrl->irq && ((int)ctrl->edu_irq >= 0))
1505a5d53ad2SKamal Dasu 	/* Discard interrupts while using dedicated edu irq */
1506a5d53ad2SKamal Dasu 			return IRQ_HANDLED;
1507a5d53ad2SKamal Dasu 
1508a5d53ad2SKamal Dasu 	/* no registered edu irq, call handler */
1509a5d53ad2SKamal Dasu 		return brcmnand_edu_irq(irq, data);
1510a5d53ad2SKamal Dasu 	}
1511a5d53ad2SKamal Dasu 
151293db446aSBoris Brezillon 	complete(&ctrl->done);
151393db446aSBoris Brezillon 	return IRQ_HANDLED;
151493db446aSBoris Brezillon }
151593db446aSBoris Brezillon 
151693db446aSBoris Brezillon /* Handle SoC-specific interrupt hardware */
151793db446aSBoris Brezillon static irqreturn_t brcmnand_irq(int irq, void *data)
151893db446aSBoris Brezillon {
151993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = data;
152093db446aSBoris Brezillon 
152193db446aSBoris Brezillon 	if (ctrl->soc->ctlrdy_ack(ctrl->soc))
152293db446aSBoris Brezillon 		return brcmnand_ctlrdy_irq(irq, data);
152393db446aSBoris Brezillon 
152493db446aSBoris Brezillon 	return IRQ_NONE;
152593db446aSBoris Brezillon }
152693db446aSBoris Brezillon 
152793db446aSBoris Brezillon static irqreturn_t brcmnand_dma_irq(int irq, void *data)
152893db446aSBoris Brezillon {
152993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = data;
153093db446aSBoris Brezillon 
153193db446aSBoris Brezillon 	complete(&ctrl->dma_done);
153293db446aSBoris Brezillon 
153393db446aSBoris Brezillon 	return IRQ_HANDLED;
153493db446aSBoris Brezillon }
153593db446aSBoris Brezillon 
153693db446aSBoris Brezillon static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
153793db446aSBoris Brezillon {
153893db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
153993db446aSBoris Brezillon 	int ret;
15403c7c1e45SKamal Dasu 	u64 cmd_addr;
154193db446aSBoris Brezillon 
15423c7c1e45SKamal Dasu 	cmd_addr = brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
15433c7c1e45SKamal Dasu 
15443c7c1e45SKamal Dasu 	dev_dbg(ctrl->dev, "send native cmd %d addr 0x%llx\n", cmd, cmd_addr);
15453c7c1e45SKamal Dasu 
154693db446aSBoris Brezillon 	BUG_ON(ctrl->cmd_pending != 0);
154793db446aSBoris Brezillon 	ctrl->cmd_pending = cmd;
154893db446aSBoris Brezillon 
154993db446aSBoris Brezillon 	ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
155093db446aSBoris Brezillon 	WARN_ON(ret);
155193db446aSBoris Brezillon 
155293db446aSBoris Brezillon 	mb(); /* flush previous writes */
155393db446aSBoris Brezillon 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
155493db446aSBoris Brezillon 			   cmd << brcmnand_cmd_shift(ctrl));
155593db446aSBoris Brezillon }
155693db446aSBoris Brezillon 
155793db446aSBoris Brezillon /***********************************************************************
155893db446aSBoris Brezillon  * NAND MTD API: read/program/erase
155993db446aSBoris Brezillon  ***********************************************************************/
156093db446aSBoris Brezillon 
15610f808c16SBoris Brezillon static void brcmnand_cmd_ctrl(struct nand_chip *chip, int dat,
156293db446aSBoris Brezillon 			      unsigned int ctrl)
156393db446aSBoris Brezillon {
156493db446aSBoris Brezillon 	/* intentionally left blank */
156593db446aSBoris Brezillon }
156693db446aSBoris Brezillon 
1567c1ac2dc3SKamal Dasu static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip)
1568c1ac2dc3SKamal Dasu {
1569c1ac2dc3SKamal Dasu 	struct brcmnand_host *host = nand_get_controller_data(chip);
1570c1ac2dc3SKamal Dasu 	struct brcmnand_controller *ctrl = host->ctrl;
1571c1ac2dc3SKamal Dasu 	struct mtd_info *mtd = nand_to_mtd(chip);
1572c1ac2dc3SKamal Dasu 	bool err = false;
1573c1ac2dc3SKamal Dasu 	int sts;
1574c1ac2dc3SKamal Dasu 
1575c1ac2dc3SKamal Dasu 	if (mtd->oops_panic_write) {
1576c1ac2dc3SKamal Dasu 		/* switch to interrupt polling and PIO mode */
1577c1ac2dc3SKamal Dasu 		disable_ctrl_irqs(ctrl);
1578c1ac2dc3SKamal Dasu 		sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY,
1579c1ac2dc3SKamal Dasu 					       NAND_CTRL_RDY, 0);
1580c1ac2dc3SKamal Dasu 		err = (sts < 0) ? true : false;
1581c1ac2dc3SKamal Dasu 	} else {
1582c1ac2dc3SKamal Dasu 		unsigned long timeo = msecs_to_jiffies(
1583c1ac2dc3SKamal Dasu 						NAND_POLL_STATUS_TIMEOUT_MS);
1584c1ac2dc3SKamal Dasu 		/* wait for completion interrupt */
1585c1ac2dc3SKamal Dasu 		sts = wait_for_completion_timeout(&ctrl->done, timeo);
1586c1ac2dc3SKamal Dasu 		err = (sts <= 0) ? true : false;
1587c1ac2dc3SKamal Dasu 	}
1588c1ac2dc3SKamal Dasu 
1589c1ac2dc3SKamal Dasu 	return err;
1590c1ac2dc3SKamal Dasu }
1591c1ac2dc3SKamal Dasu 
1592f1d46942SBoris Brezillon static int brcmnand_waitfunc(struct nand_chip *chip)
159393db446aSBoris Brezillon {
159493db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
159593db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
1596c1ac2dc3SKamal Dasu 	bool err = false;
159793db446aSBoris Brezillon 
159893db446aSBoris Brezillon 	dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
1599c1ac2dc3SKamal Dasu 	if (ctrl->cmd_pending)
1600c1ac2dc3SKamal Dasu 		err = brcmstb_nand_wait_for_completion(chip);
1601c1ac2dc3SKamal Dasu 
1602c1ac2dc3SKamal Dasu 	if (err) {
160393db446aSBoris Brezillon 		u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
160493db446aSBoris Brezillon 					>> brcmnand_cmd_shift(ctrl);
160593db446aSBoris Brezillon 
160693db446aSBoris Brezillon 		dev_err_ratelimited(ctrl->dev,
160793db446aSBoris Brezillon 			"timeout waiting for command %#02x\n", cmd);
160893db446aSBoris Brezillon 		dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
160993db446aSBoris Brezillon 			brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
161093db446aSBoris Brezillon 	}
161193db446aSBoris Brezillon 	ctrl->cmd_pending = 0;
161293db446aSBoris Brezillon 	return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
161393db446aSBoris Brezillon 				 INTFC_FLASH_STATUS;
161493db446aSBoris Brezillon }
161593db446aSBoris Brezillon 
161693db446aSBoris Brezillon enum {
161793db446aSBoris Brezillon 	LLOP_RE				= BIT(16),
161893db446aSBoris Brezillon 	LLOP_WE				= BIT(17),
161993db446aSBoris Brezillon 	LLOP_ALE			= BIT(18),
162093db446aSBoris Brezillon 	LLOP_CLE			= BIT(19),
162193db446aSBoris Brezillon 	LLOP_RETURN_IDLE		= BIT(31),
162293db446aSBoris Brezillon 
162393db446aSBoris Brezillon 	LLOP_DATA_MASK			= GENMASK(15, 0),
162493db446aSBoris Brezillon };
162593db446aSBoris Brezillon 
162693db446aSBoris Brezillon static int brcmnand_low_level_op(struct brcmnand_host *host,
162793db446aSBoris Brezillon 				 enum brcmnand_llop_type type, u32 data,
162893db446aSBoris Brezillon 				 bool last_op)
162993db446aSBoris Brezillon {
163093db446aSBoris Brezillon 	struct nand_chip *chip = &host->chip;
163193db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
163293db446aSBoris Brezillon 	u32 tmp;
163393db446aSBoris Brezillon 
163493db446aSBoris Brezillon 	tmp = data & LLOP_DATA_MASK;
163593db446aSBoris Brezillon 	switch (type) {
163693db446aSBoris Brezillon 	case LL_OP_CMD:
163793db446aSBoris Brezillon 		tmp |= LLOP_WE | LLOP_CLE;
163893db446aSBoris Brezillon 		break;
163993db446aSBoris Brezillon 	case LL_OP_ADDR:
164093db446aSBoris Brezillon 		/* WE | ALE */
164193db446aSBoris Brezillon 		tmp |= LLOP_WE | LLOP_ALE;
164293db446aSBoris Brezillon 		break;
164393db446aSBoris Brezillon 	case LL_OP_WR:
164493db446aSBoris Brezillon 		/* WE */
164593db446aSBoris Brezillon 		tmp |= LLOP_WE;
164693db446aSBoris Brezillon 		break;
164793db446aSBoris Brezillon 	case LL_OP_RD:
164893db446aSBoris Brezillon 		/* RE */
164993db446aSBoris Brezillon 		tmp |= LLOP_RE;
165093db446aSBoris Brezillon 		break;
165193db446aSBoris Brezillon 	}
165293db446aSBoris Brezillon 	if (last_op)
165393db446aSBoris Brezillon 		/* RETURN_IDLE */
165493db446aSBoris Brezillon 		tmp |= LLOP_RETURN_IDLE;
165593db446aSBoris Brezillon 
165693db446aSBoris Brezillon 	dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
165793db446aSBoris Brezillon 
165893db446aSBoris Brezillon 	brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
165993db446aSBoris Brezillon 	(void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
166093db446aSBoris Brezillon 
166193db446aSBoris Brezillon 	brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
1662f1d46942SBoris Brezillon 	return brcmnand_waitfunc(chip);
166393db446aSBoris Brezillon }
166493db446aSBoris Brezillon 
16655295cf2eSBoris Brezillon static void brcmnand_cmdfunc(struct nand_chip *chip, unsigned command,
166693db446aSBoris Brezillon 			     int column, int page_addr)
166793db446aSBoris Brezillon {
16685295cf2eSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
166993db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
167093db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
167193db446aSBoris Brezillon 	u64 addr = (u64)page_addr << chip->page_shift;
167293db446aSBoris Brezillon 	int native_cmd = 0;
167393db446aSBoris Brezillon 
167493db446aSBoris Brezillon 	if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
167593db446aSBoris Brezillon 			command == NAND_CMD_RNDOUT)
167693db446aSBoris Brezillon 		addr = (u64)column;
167793db446aSBoris Brezillon 	/* Avoid propagating a negative, don't-care address */
167893db446aSBoris Brezillon 	else if (page_addr < 0)
167993db446aSBoris Brezillon 		addr = 0;
168093db446aSBoris Brezillon 
168193db446aSBoris Brezillon 	dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
168293db446aSBoris Brezillon 		(unsigned long long)addr);
168393db446aSBoris Brezillon 
168493db446aSBoris Brezillon 	host->last_cmd = command;
168593db446aSBoris Brezillon 	host->last_byte = 0;
168693db446aSBoris Brezillon 	host->last_addr = addr;
168793db446aSBoris Brezillon 
168893db446aSBoris Brezillon 	switch (command) {
168993db446aSBoris Brezillon 	case NAND_CMD_RESET:
169093db446aSBoris Brezillon 		native_cmd = CMD_FLASH_RESET;
169193db446aSBoris Brezillon 		break;
169293db446aSBoris Brezillon 	case NAND_CMD_STATUS:
169393db446aSBoris Brezillon 		native_cmd = CMD_STATUS_READ;
169493db446aSBoris Brezillon 		break;
169593db446aSBoris Brezillon 	case NAND_CMD_READID:
169693db446aSBoris Brezillon 		native_cmd = CMD_DEVICE_ID_READ;
169793db446aSBoris Brezillon 		break;
169893db446aSBoris Brezillon 	case NAND_CMD_READOOB:
169993db446aSBoris Brezillon 		native_cmd = CMD_SPARE_AREA_READ;
170093db446aSBoris Brezillon 		break;
170193db446aSBoris Brezillon 	case NAND_CMD_ERASE1:
170293db446aSBoris Brezillon 		native_cmd = CMD_BLOCK_ERASE;
170393db446aSBoris Brezillon 		brcmnand_wp(mtd, 0);
170493db446aSBoris Brezillon 		break;
170593db446aSBoris Brezillon 	case NAND_CMD_PARAM:
170693db446aSBoris Brezillon 		native_cmd = CMD_PARAMETER_READ;
170793db446aSBoris Brezillon 		break;
170893db446aSBoris Brezillon 	case NAND_CMD_SET_FEATURES:
170993db446aSBoris Brezillon 	case NAND_CMD_GET_FEATURES:
171093db446aSBoris Brezillon 		brcmnand_low_level_op(host, LL_OP_CMD, command, false);
171193db446aSBoris Brezillon 		brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
171293db446aSBoris Brezillon 		break;
171393db446aSBoris Brezillon 	case NAND_CMD_RNDOUT:
171493db446aSBoris Brezillon 		native_cmd = CMD_PARAMETER_CHANGE_COL;
171593db446aSBoris Brezillon 		addr &= ~((u64)(FC_BYTES - 1));
171693db446aSBoris Brezillon 		/*
171793db446aSBoris Brezillon 		 * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
171893db446aSBoris Brezillon 		 * NB: hwcfg.sector_size_1k may not be initialized yet
171993db446aSBoris Brezillon 		 */
172093db446aSBoris Brezillon 		if (brcmnand_get_sector_size_1k(host)) {
172193db446aSBoris Brezillon 			host->hwcfg.sector_size_1k =
172293db446aSBoris Brezillon 				brcmnand_get_sector_size_1k(host);
172393db446aSBoris Brezillon 			brcmnand_set_sector_size_1k(host, 0);
172493db446aSBoris Brezillon 		}
172593db446aSBoris Brezillon 		break;
172693db446aSBoris Brezillon 	}
172793db446aSBoris Brezillon 
172893db446aSBoris Brezillon 	if (!native_cmd)
172993db446aSBoris Brezillon 		return;
173093db446aSBoris Brezillon 
17313c7c1e45SKamal Dasu 	brcmnand_set_cmd_addr(mtd, addr);
173293db446aSBoris Brezillon 	brcmnand_send_cmd(host, native_cmd);
1733f1d46942SBoris Brezillon 	brcmnand_waitfunc(chip);
173493db446aSBoris Brezillon 
173593db446aSBoris Brezillon 	if (native_cmd == CMD_PARAMETER_READ ||
173693db446aSBoris Brezillon 			native_cmd == CMD_PARAMETER_CHANGE_COL) {
173793db446aSBoris Brezillon 		/* Copy flash cache word-wise */
173893db446aSBoris Brezillon 		u32 *flash_cache = (u32 *)ctrl->flash_cache;
173993db446aSBoris Brezillon 		int i;
174093db446aSBoris Brezillon 
174193db446aSBoris Brezillon 		brcmnand_soc_data_bus_prepare(ctrl->soc, true);
174293db446aSBoris Brezillon 
174393db446aSBoris Brezillon 		/*
174493db446aSBoris Brezillon 		 * Must cache the FLASH_CACHE now, since changes in
174593db446aSBoris Brezillon 		 * SECTOR_SIZE_1K may invalidate it
174693db446aSBoris Brezillon 		 */
174793db446aSBoris Brezillon 		for (i = 0; i < FC_WORDS; i++)
174893db446aSBoris Brezillon 			/*
174993db446aSBoris Brezillon 			 * Flash cache is big endian for parameter pages, at
175093db446aSBoris Brezillon 			 * least on STB SoCs
175193db446aSBoris Brezillon 			 */
175293db446aSBoris Brezillon 			flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
175393db446aSBoris Brezillon 
175493db446aSBoris Brezillon 		brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
175593db446aSBoris Brezillon 
175693db446aSBoris Brezillon 		/* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
175793db446aSBoris Brezillon 		if (host->hwcfg.sector_size_1k)
175893db446aSBoris Brezillon 			brcmnand_set_sector_size_1k(host,
175993db446aSBoris Brezillon 						    host->hwcfg.sector_size_1k);
176093db446aSBoris Brezillon 	}
176193db446aSBoris Brezillon 
176293db446aSBoris Brezillon 	/* Re-enable protection is necessary only after erase */
176393db446aSBoris Brezillon 	if (command == NAND_CMD_ERASE1)
176493db446aSBoris Brezillon 		brcmnand_wp(mtd, 1);
176593db446aSBoris Brezillon }
176693db446aSBoris Brezillon 
17677e534323SBoris Brezillon static uint8_t brcmnand_read_byte(struct nand_chip *chip)
176893db446aSBoris Brezillon {
176993db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
177093db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
177193db446aSBoris Brezillon 	uint8_t ret = 0;
177293db446aSBoris Brezillon 	int addr, offs;
177393db446aSBoris Brezillon 
177493db446aSBoris Brezillon 	switch (host->last_cmd) {
177593db446aSBoris Brezillon 	case NAND_CMD_READID:
177693db446aSBoris Brezillon 		if (host->last_byte < 4)
177793db446aSBoris Brezillon 			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
177893db446aSBoris Brezillon 				(24 - (host->last_byte << 3));
177993db446aSBoris Brezillon 		else if (host->last_byte < 8)
178093db446aSBoris Brezillon 			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
178193db446aSBoris Brezillon 				(56 - (host->last_byte << 3));
178293db446aSBoris Brezillon 		break;
178393db446aSBoris Brezillon 
178493db446aSBoris Brezillon 	case NAND_CMD_READOOB:
178593db446aSBoris Brezillon 		ret = oob_reg_read(ctrl, host->last_byte);
178693db446aSBoris Brezillon 		break;
178793db446aSBoris Brezillon 
178893db446aSBoris Brezillon 	case NAND_CMD_STATUS:
178993db446aSBoris Brezillon 		ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
179093db446aSBoris Brezillon 					INTFC_FLASH_STATUS;
179193db446aSBoris Brezillon 		if (wp_on) /* hide WP status */
179293db446aSBoris Brezillon 			ret |= NAND_STATUS_WP;
179393db446aSBoris Brezillon 		break;
179493db446aSBoris Brezillon 
179593db446aSBoris Brezillon 	case NAND_CMD_PARAM:
179693db446aSBoris Brezillon 	case NAND_CMD_RNDOUT:
179793db446aSBoris Brezillon 		addr = host->last_addr + host->last_byte;
179893db446aSBoris Brezillon 		offs = addr & (FC_BYTES - 1);
179993db446aSBoris Brezillon 
180093db446aSBoris Brezillon 		/* At FC_BYTES boundary, switch to next column */
180193db446aSBoris Brezillon 		if (host->last_byte > 0 && offs == 0)
180293db446aSBoris Brezillon 			nand_change_read_column_op(chip, addr, NULL, 0, false);
180393db446aSBoris Brezillon 
180493db446aSBoris Brezillon 		ret = ctrl->flash_cache[offs];
180593db446aSBoris Brezillon 		break;
180693db446aSBoris Brezillon 	case NAND_CMD_GET_FEATURES:
180793db446aSBoris Brezillon 		if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
180893db446aSBoris Brezillon 			ret = 0;
180993db446aSBoris Brezillon 		} else {
181093db446aSBoris Brezillon 			bool last = host->last_byte ==
181193db446aSBoris Brezillon 				ONFI_SUBFEATURE_PARAM_LEN - 1;
181293db446aSBoris Brezillon 			brcmnand_low_level_op(host, LL_OP_RD, 0, last);
181393db446aSBoris Brezillon 			ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
181493db446aSBoris Brezillon 		}
181593db446aSBoris Brezillon 	}
181693db446aSBoris Brezillon 
181793db446aSBoris Brezillon 	dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
181893db446aSBoris Brezillon 	host->last_byte++;
181993db446aSBoris Brezillon 
182093db446aSBoris Brezillon 	return ret;
182193db446aSBoris Brezillon }
182293db446aSBoris Brezillon 
18237e534323SBoris Brezillon static void brcmnand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
182493db446aSBoris Brezillon {
182593db446aSBoris Brezillon 	int i;
182693db446aSBoris Brezillon 
182793db446aSBoris Brezillon 	for (i = 0; i < len; i++, buf++)
18287e534323SBoris Brezillon 		*buf = brcmnand_read_byte(chip);
182993db446aSBoris Brezillon }
183093db446aSBoris Brezillon 
1831c0739d85SBoris Brezillon static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf,
183293db446aSBoris Brezillon 			       int len)
183393db446aSBoris Brezillon {
183493db446aSBoris Brezillon 	int i;
183593db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
183693db446aSBoris Brezillon 
183793db446aSBoris Brezillon 	switch (host->last_cmd) {
183893db446aSBoris Brezillon 	case NAND_CMD_SET_FEATURES:
183993db446aSBoris Brezillon 		for (i = 0; i < len; i++)
184093db446aSBoris Brezillon 			brcmnand_low_level_op(host, LL_OP_WR, buf[i],
184193db446aSBoris Brezillon 						  (i + 1) == len);
184293db446aSBoris Brezillon 		break;
184393db446aSBoris Brezillon 	default:
184493db446aSBoris Brezillon 		BUG();
184593db446aSBoris Brezillon 		break;
184693db446aSBoris Brezillon 	}
184793db446aSBoris Brezillon }
184893db446aSBoris Brezillon 
1849*fa985e22SLee Jones /*
1850a5d53ad2SKamal Dasu  *  Kick EDU engine
1851a5d53ad2SKamal Dasu  */
1852a5d53ad2SKamal Dasu static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
1853a5d53ad2SKamal Dasu 			      u32 len, u8 cmd)
1854a5d53ad2SKamal Dasu {
1855a5d53ad2SKamal Dasu 	struct brcmnand_controller *ctrl = host->ctrl;
1856a5d53ad2SKamal Dasu 	unsigned long timeo = msecs_to_jiffies(200);
1857a5d53ad2SKamal Dasu 	int ret = 0;
1858a5d53ad2SKamal Dasu 	int dir = (cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
1859a5d53ad2SKamal Dasu 	u8 edu_cmd = (cmd == CMD_PAGE_READ ? EDU_CMD_READ : EDU_CMD_WRITE);
1860a5d53ad2SKamal Dasu 	unsigned int trans = len >> FC_SHIFT;
1861a5d53ad2SKamal Dasu 	dma_addr_t pa;
1862a5d53ad2SKamal Dasu 
1863a5d53ad2SKamal Dasu 	pa = dma_map_single(ctrl->dev, buf, len, dir);
1864a5d53ad2SKamal Dasu 	if (dma_mapping_error(ctrl->dev, pa)) {
1865a5d53ad2SKamal Dasu 		dev_err(ctrl->dev, "unable to map buffer for EDU DMA\n");
1866a5d53ad2SKamal Dasu 		return -ENOMEM;
1867a5d53ad2SKamal Dasu 	}
1868a5d53ad2SKamal Dasu 
1869a5d53ad2SKamal Dasu 	ctrl->edu_pending = true;
1870a5d53ad2SKamal Dasu 	ctrl->edu_dram_addr = pa;
1871a5d53ad2SKamal Dasu 	ctrl->edu_ext_addr = addr;
1872a5d53ad2SKamal Dasu 	ctrl->edu_cmd = edu_cmd;
1873a5d53ad2SKamal Dasu 	ctrl->edu_count = trans;
1874a5d53ad2SKamal Dasu 
1875a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_DRAM_ADDR, (u32)ctrl->edu_dram_addr);
1876a5d53ad2SKamal Dasu 	edu_readl(ctrl,  EDU_DRAM_ADDR);
1877a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_EXT_ADDR, ctrl->edu_ext_addr);
1878a5d53ad2SKamal Dasu 	edu_readl(ctrl, EDU_EXT_ADDR);
1879a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_LENGTH, FC_BYTES);
1880a5d53ad2SKamal Dasu 	edu_readl(ctrl, EDU_LENGTH);
1881a5d53ad2SKamal Dasu 
1882a5d53ad2SKamal Dasu 	/* Start edu engine */
1883a5d53ad2SKamal Dasu 	mb(); /* flush previous writes */
1884a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_CMD, ctrl->edu_cmd);
1885a5d53ad2SKamal Dasu 	edu_readl(ctrl, EDU_CMD);
1886a5d53ad2SKamal Dasu 
1887a5d53ad2SKamal Dasu 	if (wait_for_completion_timeout(&ctrl->edu_done, timeo) <= 0) {
1888a5d53ad2SKamal Dasu 		dev_err(ctrl->dev,
1889a5d53ad2SKamal Dasu 			"timeout waiting for EDU; status %#x, error status %#x\n",
1890a5d53ad2SKamal Dasu 			edu_readl(ctrl, EDU_STATUS),
1891a5d53ad2SKamal Dasu 			edu_readl(ctrl, EDU_ERR_STATUS));
1892a5d53ad2SKamal Dasu 	}
1893a5d53ad2SKamal Dasu 
1894a5d53ad2SKamal Dasu 	dma_unmap_single(ctrl->dev, pa, len, dir);
1895a5d53ad2SKamal Dasu 
1896a5d53ad2SKamal Dasu 	/* for program page check NAND status */
1897a5d53ad2SKamal Dasu 	if (((brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
1898a5d53ad2SKamal Dasu 	      INTFC_FLASH_STATUS) & NAND_STATUS_FAIL) &&
1899a5d53ad2SKamal Dasu 	    edu_cmd == EDU_CMD_WRITE) {
1900a5d53ad2SKamal Dasu 		dev_info(ctrl->dev, "program failed at %llx\n",
1901a5d53ad2SKamal Dasu 			 (unsigned long long)addr);
1902a5d53ad2SKamal Dasu 		ret = -EIO;
1903a5d53ad2SKamal Dasu 	}
1904a5d53ad2SKamal Dasu 
1905a5d53ad2SKamal Dasu 	/* Make sure the EDU status is clean */
1906a5d53ad2SKamal Dasu 	if (edu_readl(ctrl, EDU_STATUS) & EDU_STATUS_ACTIVE)
1907a5d53ad2SKamal Dasu 		dev_warn(ctrl->dev, "EDU still active: %#x\n",
1908a5d53ad2SKamal Dasu 			 edu_readl(ctrl, EDU_STATUS));
1909a5d53ad2SKamal Dasu 
1910a5d53ad2SKamal Dasu 	if (unlikely(edu_readl(ctrl, EDU_ERR_STATUS) & EDU_ERR_STATUS_ERRACK)) {
1911a5d53ad2SKamal Dasu 		dev_warn(ctrl->dev, "EDU RBUS error at addr %llx\n",
1912a5d53ad2SKamal Dasu 			 (unsigned long long)addr);
1913a5d53ad2SKamal Dasu 		ret = -EIO;
1914a5d53ad2SKamal Dasu 	}
1915a5d53ad2SKamal Dasu 
1916a5d53ad2SKamal Dasu 	ctrl->edu_pending = false;
1917a5d53ad2SKamal Dasu 	brcmnand_edu_init(ctrl);
1918a5d53ad2SKamal Dasu 	edu_writel(ctrl, EDU_STOP, 0); /* force stop */
1919a5d53ad2SKamal Dasu 	edu_readl(ctrl, EDU_STOP);
1920a5d53ad2SKamal Dasu 
19214551e78aSKamal Dasu 	if (!ret && edu_cmd == EDU_CMD_READ) {
19224551e78aSKamal Dasu 		u64 err_addr = 0;
19234551e78aSKamal Dasu 
19244551e78aSKamal Dasu 		/*
19254551e78aSKamal Dasu 		 * check for ECC errors here, subpage ECC errors are
19264551e78aSKamal Dasu 		 * retained in ECC error address register
19274551e78aSKamal Dasu 		 */
19284551e78aSKamal Dasu 		err_addr = brcmnand_get_uncorrecc_addr(ctrl);
19294551e78aSKamal Dasu 		if (!err_addr) {
19304551e78aSKamal Dasu 			err_addr = brcmnand_get_correcc_addr(ctrl);
19314551e78aSKamal Dasu 			if (err_addr)
19324551e78aSKamal Dasu 				ret = -EUCLEAN;
19334551e78aSKamal Dasu 		} else
19344551e78aSKamal Dasu 			ret = -EBADMSG;
19354551e78aSKamal Dasu 	}
19364551e78aSKamal Dasu 
1937a5d53ad2SKamal Dasu 	return ret;
1938a5d53ad2SKamal Dasu }
1939a5d53ad2SKamal Dasu 
1940*fa985e22SLee Jones /*
194193db446aSBoris Brezillon  * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
194293db446aSBoris Brezillon  * following ahead of time:
194393db446aSBoris Brezillon  *  - Is this descriptor the beginning or end of a linked list?
194493db446aSBoris Brezillon  *  - What is the (DMA) address of the next descriptor in the linked list?
194593db446aSBoris Brezillon  */
194693db446aSBoris Brezillon static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
194793db446aSBoris Brezillon 				  struct brcm_nand_dma_desc *desc, u64 addr,
194893db446aSBoris Brezillon 				  dma_addr_t buf, u32 len, u8 dma_cmd,
194993db446aSBoris Brezillon 				  bool begin, bool end,
195093db446aSBoris Brezillon 				  dma_addr_t next_desc)
195193db446aSBoris Brezillon {
195293db446aSBoris Brezillon 	memset(desc, 0, sizeof(*desc));
195393db446aSBoris Brezillon 	/* Descriptors are written in native byte order (wordwise) */
195493db446aSBoris Brezillon 	desc->next_desc = lower_32_bits(next_desc);
195593db446aSBoris Brezillon 	desc->next_desc_ext = upper_32_bits(next_desc);
195693db446aSBoris Brezillon 	desc->cmd_irq = (dma_cmd << 24) |
195793db446aSBoris Brezillon 		(end ? (0x03 << 8) : 0) | /* IRQ | STOP */
195893db446aSBoris Brezillon 		(!!begin) | ((!!end) << 1); /* head, tail */
195993db446aSBoris Brezillon #ifdef CONFIG_CPU_BIG_ENDIAN
196093db446aSBoris Brezillon 	desc->cmd_irq |= 0x01 << 12;
196193db446aSBoris Brezillon #endif
196293db446aSBoris Brezillon 	desc->dram_addr = lower_32_bits(buf);
196393db446aSBoris Brezillon 	desc->dram_addr_ext = upper_32_bits(buf);
196493db446aSBoris Brezillon 	desc->tfr_len = len;
196593db446aSBoris Brezillon 	desc->total_len = len;
196693db446aSBoris Brezillon 	desc->flash_addr = lower_32_bits(addr);
196793db446aSBoris Brezillon 	desc->flash_addr_ext = upper_32_bits(addr);
196893db446aSBoris Brezillon 	desc->cs = host->cs;
196993db446aSBoris Brezillon 	desc->status_valid = 0x01;
197093db446aSBoris Brezillon 	return 0;
197193db446aSBoris Brezillon }
197293db446aSBoris Brezillon 
1973*fa985e22SLee Jones /*
197493db446aSBoris Brezillon  * Kick the FLASH_DMA engine, with a given DMA descriptor
197593db446aSBoris Brezillon  */
197693db446aSBoris Brezillon static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
197793db446aSBoris Brezillon {
197893db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
197993db446aSBoris Brezillon 	unsigned long timeo = msecs_to_jiffies(100);
198093db446aSBoris Brezillon 
198193db446aSBoris Brezillon 	flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
198293db446aSBoris Brezillon 	(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
198383156c1cSKamal Dasu 	if (ctrl->nand_version > 0x0602) {
198483156c1cSKamal Dasu 		flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT,
198583156c1cSKamal Dasu 				 upper_32_bits(desc));
198693db446aSBoris Brezillon 		(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
198783156c1cSKamal Dasu 	}
198893db446aSBoris Brezillon 
198993db446aSBoris Brezillon 	/* Start FLASH_DMA engine */
199093db446aSBoris Brezillon 	ctrl->dma_pending = true;
199193db446aSBoris Brezillon 	mb(); /* flush previous writes */
199293db446aSBoris Brezillon 	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
199393db446aSBoris Brezillon 
199493db446aSBoris Brezillon 	if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
199593db446aSBoris Brezillon 		dev_err(ctrl->dev,
199693db446aSBoris Brezillon 				"timeout waiting for DMA; status %#x, error status %#x\n",
199793db446aSBoris Brezillon 				flash_dma_readl(ctrl, FLASH_DMA_STATUS),
199893db446aSBoris Brezillon 				flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
199993db446aSBoris Brezillon 	}
200093db446aSBoris Brezillon 	ctrl->dma_pending = false;
200193db446aSBoris Brezillon 	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
200293db446aSBoris Brezillon }
200393db446aSBoris Brezillon 
200493db446aSBoris Brezillon static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
200593db446aSBoris Brezillon 			      u32 len, u8 dma_cmd)
200693db446aSBoris Brezillon {
200793db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
200893db446aSBoris Brezillon 	dma_addr_t buf_pa;
200993db446aSBoris Brezillon 	int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
201093db446aSBoris Brezillon 
201193db446aSBoris Brezillon 	buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
201293db446aSBoris Brezillon 	if (dma_mapping_error(ctrl->dev, buf_pa)) {
201393db446aSBoris Brezillon 		dev_err(ctrl->dev, "unable to map buffer for DMA\n");
201493db446aSBoris Brezillon 		return -ENOMEM;
201593db446aSBoris Brezillon 	}
201693db446aSBoris Brezillon 
201793db446aSBoris Brezillon 	brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
201893db446aSBoris Brezillon 				   dma_cmd, true, true, 0);
201993db446aSBoris Brezillon 
202093db446aSBoris Brezillon 	brcmnand_dma_run(host, ctrl->dma_pa);
202193db446aSBoris Brezillon 
202293db446aSBoris Brezillon 	dma_unmap_single(ctrl->dev, buf_pa, len, dir);
202393db446aSBoris Brezillon 
202493db446aSBoris Brezillon 	if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
202593db446aSBoris Brezillon 		return -EBADMSG;
202693db446aSBoris Brezillon 	else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
202793db446aSBoris Brezillon 		return -EUCLEAN;
202893db446aSBoris Brezillon 
202993db446aSBoris Brezillon 	return 0;
203093db446aSBoris Brezillon }
203193db446aSBoris Brezillon 
203293db446aSBoris Brezillon /*
203393db446aSBoris Brezillon  * Assumes proper CS is already set
203493db446aSBoris Brezillon  */
203593db446aSBoris Brezillon static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
203693db446aSBoris Brezillon 				u64 addr, unsigned int trans, u32 *buf,
203793db446aSBoris Brezillon 				u8 *oob, u64 *err_addr)
203893db446aSBoris Brezillon {
203993db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
204093db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
204193db446aSBoris Brezillon 	int i, j, ret = 0;
204293db446aSBoris Brezillon 
20433c7c1e45SKamal Dasu 	brcmnand_clear_ecc_addr(ctrl);
204493db446aSBoris Brezillon 
204593db446aSBoris Brezillon 	for (i = 0; i < trans; i++, addr += FC_BYTES) {
20463c7c1e45SKamal Dasu 		brcmnand_set_cmd_addr(mtd, addr);
204793db446aSBoris Brezillon 		/* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
204893db446aSBoris Brezillon 		brcmnand_send_cmd(host, CMD_PAGE_READ);
2049f1d46942SBoris Brezillon 		brcmnand_waitfunc(chip);
205093db446aSBoris Brezillon 
205193db446aSBoris Brezillon 		if (likely(buf)) {
205293db446aSBoris Brezillon 			brcmnand_soc_data_bus_prepare(ctrl->soc, false);
205393db446aSBoris Brezillon 
205493db446aSBoris Brezillon 			for (j = 0; j < FC_WORDS; j++, buf++)
205593db446aSBoris Brezillon 				*buf = brcmnand_read_fc(ctrl, j);
205693db446aSBoris Brezillon 
205793db446aSBoris Brezillon 			brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
205893db446aSBoris Brezillon 		}
205993db446aSBoris Brezillon 
206093db446aSBoris Brezillon 		if (oob)
206193db446aSBoris Brezillon 			oob += read_oob_from_regs(ctrl, i, oob,
206293db446aSBoris Brezillon 					mtd->oobsize / trans,
206393db446aSBoris Brezillon 					host->hwcfg.sector_size_1k);
206493db446aSBoris Brezillon 
206593db446aSBoris Brezillon 		if (!ret) {
20663c7c1e45SKamal Dasu 			*err_addr = brcmnand_get_uncorrecc_addr(ctrl);
20673c7c1e45SKamal Dasu 
206893db446aSBoris Brezillon 			if (*err_addr)
206993db446aSBoris Brezillon 				ret = -EBADMSG;
207093db446aSBoris Brezillon 		}
207193db446aSBoris Brezillon 
207293db446aSBoris Brezillon 		if (!ret) {
20733c7c1e45SKamal Dasu 			*err_addr = brcmnand_get_correcc_addr(ctrl);
20743c7c1e45SKamal Dasu 
207593db446aSBoris Brezillon 			if (*err_addr)
207693db446aSBoris Brezillon 				ret = -EUCLEAN;
207793db446aSBoris Brezillon 		}
207893db446aSBoris Brezillon 	}
207993db446aSBoris Brezillon 
208093db446aSBoris Brezillon 	return ret;
208193db446aSBoris Brezillon }
208293db446aSBoris Brezillon 
208393db446aSBoris Brezillon /*
208493db446aSBoris Brezillon  * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
208593db446aSBoris Brezillon  * error
208693db446aSBoris Brezillon  *
208793db446aSBoris Brezillon  * Because the HW ECC signals an ECC error if an erase paged has even a single
208893db446aSBoris Brezillon  * bitflip, we must check each ECC error to see if it is actually an erased
208993db446aSBoris Brezillon  * page with bitflips, not a truly corrupted page.
209093db446aSBoris Brezillon  *
209193db446aSBoris Brezillon  * On a real error, return a negative error code (-EBADMSG for ECC error), and
209293db446aSBoris Brezillon  * buf will contain raw data.
209393db446aSBoris Brezillon  * Otherwise, buf gets filled with 0xffs and return the maximum number of
209493db446aSBoris Brezillon  * bitflips-per-ECC-sector to the caller.
209593db446aSBoris Brezillon  *
209693db446aSBoris Brezillon  */
209793db446aSBoris Brezillon static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
209893db446aSBoris Brezillon 		  struct nand_chip *chip, void *buf, u64 addr)
209993db446aSBoris Brezillon {
2100dcb351c0SÁlvaro Fernández Rojas 	struct mtd_oob_region ecc;
2101dcb351c0SÁlvaro Fernández Rojas 	int i;
210293db446aSBoris Brezillon 	int bitflips = 0;
210393db446aSBoris Brezillon 	int page = addr >> chip->page_shift;
210493db446aSBoris Brezillon 	int ret;
2105dcb351c0SÁlvaro Fernández Rojas 	void *ecc_bytes;
21067f852cc1SClaire Lin 	void *ecc_chunk;
210793db446aSBoris Brezillon 
2108eeab7174SBoris Brezillon 	if (!buf)
2109eeab7174SBoris Brezillon 		buf = nand_get_data_buf(chip);
211093db446aSBoris Brezillon 
211193db446aSBoris Brezillon 	/* read without ecc for verification */
2112b9761687SBoris Brezillon 	ret = chip->ecc.read_page_raw(chip, buf, true, page);
211393db446aSBoris Brezillon 	if (ret)
211493db446aSBoris Brezillon 		return ret;
211593db446aSBoris Brezillon 
2116dcb351c0SÁlvaro Fernández Rojas 	for (i = 0; i < chip->ecc.steps; i++) {
21177f852cc1SClaire Lin 		ecc_chunk = buf + chip->ecc.size * i;
2118dcb351c0SÁlvaro Fernández Rojas 
2119dcb351c0SÁlvaro Fernández Rojas 		mtd_ooblayout_ecc(mtd, i, &ecc);
2120dcb351c0SÁlvaro Fernández Rojas 		ecc_bytes = chip->oob_poi + ecc.offset;
2121dcb351c0SÁlvaro Fernández Rojas 
2122dcb351c0SÁlvaro Fernández Rojas 		ret = nand_check_erased_ecc_chunk(ecc_chunk, chip->ecc.size,
2123dcb351c0SÁlvaro Fernández Rojas 						  ecc_bytes, ecc.length,
2124dcb351c0SÁlvaro Fernández Rojas 						  NULL, 0,
212593db446aSBoris Brezillon 						  chip->ecc.strength);
212693db446aSBoris Brezillon 		if (ret < 0)
212793db446aSBoris Brezillon 			return ret;
212893db446aSBoris Brezillon 
212993db446aSBoris Brezillon 		bitflips = max(bitflips, ret);
213093db446aSBoris Brezillon 	}
213193db446aSBoris Brezillon 
213293db446aSBoris Brezillon 	return bitflips;
213393db446aSBoris Brezillon }
213493db446aSBoris Brezillon 
213593db446aSBoris Brezillon static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
213693db446aSBoris Brezillon 			 u64 addr, unsigned int trans, u32 *buf, u8 *oob)
213793db446aSBoris Brezillon {
213893db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
213993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
214093db446aSBoris Brezillon 	u64 err_addr = 0;
214193db446aSBoris Brezillon 	int err;
214293db446aSBoris Brezillon 	bool retry = true;
21434551e78aSKamal Dasu 	bool edu_err = false;
214493db446aSBoris Brezillon 
214593db446aSBoris Brezillon 	dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
214693db446aSBoris Brezillon 
214793db446aSBoris Brezillon try_dmaread:
21483c7c1e45SKamal Dasu 	brcmnand_clear_ecc_addr(ctrl);
214993db446aSBoris Brezillon 
2150a5d53ad2SKamal Dasu 	if (ctrl->dma_trans && !oob && flash_dma_buf_ok(buf)) {
2151a5d53ad2SKamal Dasu 		err = ctrl->dma_trans(host, addr, buf,
2152a5d53ad2SKamal Dasu 				      trans * FC_BYTES,
215393db446aSBoris Brezillon 				      CMD_PAGE_READ);
2154a5d53ad2SKamal Dasu 
215593db446aSBoris Brezillon 		if (err) {
215693db446aSBoris Brezillon 			if (mtd_is_bitflip_or_eccerr(err))
215793db446aSBoris Brezillon 				err_addr = addr;
215893db446aSBoris Brezillon 			else
215993db446aSBoris Brezillon 				return -EIO;
216093db446aSBoris Brezillon 		}
21614551e78aSKamal Dasu 
21624551e78aSKamal Dasu 		if (has_edu(ctrl) && err_addr)
21634551e78aSKamal Dasu 			edu_err = true;
21644551e78aSKamal Dasu 
216593db446aSBoris Brezillon 	} else {
216693db446aSBoris Brezillon 		if (oob)
216793db446aSBoris Brezillon 			memset(oob, 0x99, mtd->oobsize);
216893db446aSBoris Brezillon 
216993db446aSBoris Brezillon 		err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
217093db446aSBoris Brezillon 					       oob, &err_addr);
217193db446aSBoris Brezillon 	}
217293db446aSBoris Brezillon 
217393db446aSBoris Brezillon 	if (mtd_is_eccerr(err)) {
217493db446aSBoris Brezillon 		/*
217593db446aSBoris Brezillon 		 * On controller version and 7.0, 7.1 , DMA read after a
217693db446aSBoris Brezillon 		 * prior PIO read that reported uncorrectable error,
217793db446aSBoris Brezillon 		 * the DMA engine captures this error following DMA read
217893db446aSBoris Brezillon 		 * cleared only on subsequent DMA read, so just retry once
217993db446aSBoris Brezillon 		 * to clear a possible false error reported for current DMA
218093db446aSBoris Brezillon 		 * read
218193db446aSBoris Brezillon 		 */
218293db446aSBoris Brezillon 		if ((ctrl->nand_version == 0x0700) ||
218393db446aSBoris Brezillon 		    (ctrl->nand_version == 0x0701)) {
218493db446aSBoris Brezillon 			if (retry) {
218593db446aSBoris Brezillon 				retry = false;
218693db446aSBoris Brezillon 				goto try_dmaread;
218793db446aSBoris Brezillon 			}
218893db446aSBoris Brezillon 		}
218993db446aSBoris Brezillon 
219093db446aSBoris Brezillon 		/*
219193db446aSBoris Brezillon 		 * Controller version 7.2 has hw encoder to detect erased page
219293db446aSBoris Brezillon 		 * bitflips, apply sw verification for older controllers only
219393db446aSBoris Brezillon 		 */
219493db446aSBoris Brezillon 		if (ctrl->nand_version < 0x0702) {
219593db446aSBoris Brezillon 			err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
219693db446aSBoris Brezillon 							      addr);
219793db446aSBoris Brezillon 			/* erased page bitflips corrected */
219893db446aSBoris Brezillon 			if (err >= 0)
219993db446aSBoris Brezillon 				return err;
220093db446aSBoris Brezillon 		}
220193db446aSBoris Brezillon 
220293db446aSBoris Brezillon 		dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
220393db446aSBoris Brezillon 			(unsigned long long)err_addr);
220493db446aSBoris Brezillon 		mtd->ecc_stats.failed++;
220593db446aSBoris Brezillon 		/* NAND layer expects zero on ECC errors */
220693db446aSBoris Brezillon 		return 0;
220793db446aSBoris Brezillon 	}
220893db446aSBoris Brezillon 
220993db446aSBoris Brezillon 	if (mtd_is_bitflip(err)) {
221093db446aSBoris Brezillon 		unsigned int corrected = brcmnand_count_corrected(ctrl);
221193db446aSBoris Brezillon 
22124551e78aSKamal Dasu 		/* in case of EDU correctable error we read again using PIO */
22134551e78aSKamal Dasu 		if (edu_err)
22144551e78aSKamal Dasu 			err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
22154551e78aSKamal Dasu 						   oob, &err_addr);
22164551e78aSKamal Dasu 
221793db446aSBoris Brezillon 		dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
221893db446aSBoris Brezillon 			(unsigned long long)err_addr);
221993db446aSBoris Brezillon 		mtd->ecc_stats.corrected += corrected;
222093db446aSBoris Brezillon 		/* Always exceed the software-imposed threshold */
222193db446aSBoris Brezillon 		return max(mtd->bitflip_threshold, corrected);
222293db446aSBoris Brezillon 	}
222393db446aSBoris Brezillon 
222493db446aSBoris Brezillon 	return 0;
222593db446aSBoris Brezillon }
222693db446aSBoris Brezillon 
2227b9761687SBoris Brezillon static int brcmnand_read_page(struct nand_chip *chip, uint8_t *buf,
2228b9761687SBoris Brezillon 			      int oob_required, int page)
222993db446aSBoris Brezillon {
2230b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
223193db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
223293db446aSBoris Brezillon 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
223393db446aSBoris Brezillon 
223493db446aSBoris Brezillon 	nand_read_page_op(chip, page, 0, NULL, 0);
223593db446aSBoris Brezillon 
223693db446aSBoris Brezillon 	return brcmnand_read(mtd, chip, host->last_addr,
223793db446aSBoris Brezillon 			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
223893db446aSBoris Brezillon }
223993db446aSBoris Brezillon 
2240b9761687SBoris Brezillon static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
2241b9761687SBoris Brezillon 				  int oob_required, int page)
224293db446aSBoris Brezillon {
224393db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
2244b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
224593db446aSBoris Brezillon 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
224693db446aSBoris Brezillon 	int ret;
224793db446aSBoris Brezillon 
224893db446aSBoris Brezillon 	nand_read_page_op(chip, page, 0, NULL, 0);
224993db446aSBoris Brezillon 
225093db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 0);
225193db446aSBoris Brezillon 	ret = brcmnand_read(mtd, chip, host->last_addr,
225293db446aSBoris Brezillon 			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
225393db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 1);
225493db446aSBoris Brezillon 	return ret;
225593db446aSBoris Brezillon }
225693db446aSBoris Brezillon 
2257b9761687SBoris Brezillon static int brcmnand_read_oob(struct nand_chip *chip, int page)
225893db446aSBoris Brezillon {
2259b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2260b9761687SBoris Brezillon 
226193db446aSBoris Brezillon 	return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
226293db446aSBoris Brezillon 			mtd->writesize >> FC_SHIFT,
226393db446aSBoris Brezillon 			NULL, (u8 *)chip->oob_poi);
226493db446aSBoris Brezillon }
226593db446aSBoris Brezillon 
2266b9761687SBoris Brezillon static int brcmnand_read_oob_raw(struct nand_chip *chip, int page)
226793db446aSBoris Brezillon {
2268b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
226993db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
227093db446aSBoris Brezillon 
227193db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 0);
227293db446aSBoris Brezillon 	brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
227393db446aSBoris Brezillon 		mtd->writesize >> FC_SHIFT,
227493db446aSBoris Brezillon 		NULL, (u8 *)chip->oob_poi);
227593db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 1);
227693db446aSBoris Brezillon 	return 0;
227793db446aSBoris Brezillon }
227893db446aSBoris Brezillon 
227993db446aSBoris Brezillon static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
228093db446aSBoris Brezillon 			  u64 addr, const u32 *buf, u8 *oob)
228193db446aSBoris Brezillon {
228293db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
228393db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
228493db446aSBoris Brezillon 	unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
228593db446aSBoris Brezillon 	int status, ret = 0;
228693db446aSBoris Brezillon 
228793db446aSBoris Brezillon 	dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
228893db446aSBoris Brezillon 
228993db446aSBoris Brezillon 	if (unlikely((unsigned long)buf & 0x03)) {
229093db446aSBoris Brezillon 		dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
229193db446aSBoris Brezillon 		buf = (u32 *)((unsigned long)buf & ~0x03);
229293db446aSBoris Brezillon 	}
229393db446aSBoris Brezillon 
229493db446aSBoris Brezillon 	brcmnand_wp(mtd, 0);
229593db446aSBoris Brezillon 
229693db446aSBoris Brezillon 	for (i = 0; i < ctrl->max_oob; i += 4)
229793db446aSBoris Brezillon 		oob_reg_write(ctrl, i, 0xffffffff);
229893db446aSBoris Brezillon 
2299a5d53ad2SKamal Dasu 	if (use_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
2300a5d53ad2SKamal Dasu 		if (ctrl->dma_trans(host, addr, (u32 *)buf, mtd->writesize,
2301a5d53ad2SKamal Dasu 				    CMD_PROGRAM_PAGE))
2302a5d53ad2SKamal Dasu 
230393db446aSBoris Brezillon 			ret = -EIO;
2304a5d53ad2SKamal Dasu 
230593db446aSBoris Brezillon 		goto out;
230693db446aSBoris Brezillon 	}
230793db446aSBoris Brezillon 
230893db446aSBoris Brezillon 	for (i = 0; i < trans; i++, addr += FC_BYTES) {
230993db446aSBoris Brezillon 		/* full address MUST be set before populating FC */
23103c7c1e45SKamal Dasu 		brcmnand_set_cmd_addr(mtd, addr);
231193db446aSBoris Brezillon 
231293db446aSBoris Brezillon 		if (buf) {
231393db446aSBoris Brezillon 			brcmnand_soc_data_bus_prepare(ctrl->soc, false);
231493db446aSBoris Brezillon 
231593db446aSBoris Brezillon 			for (j = 0; j < FC_WORDS; j++, buf++)
231693db446aSBoris Brezillon 				brcmnand_write_fc(ctrl, j, *buf);
231793db446aSBoris Brezillon 
231893db446aSBoris Brezillon 			brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
231993db446aSBoris Brezillon 		} else if (oob) {
232093db446aSBoris Brezillon 			for (j = 0; j < FC_WORDS; j++)
232193db446aSBoris Brezillon 				brcmnand_write_fc(ctrl, j, 0xffffffff);
232293db446aSBoris Brezillon 		}
232393db446aSBoris Brezillon 
232493db446aSBoris Brezillon 		if (oob) {
232593db446aSBoris Brezillon 			oob += write_oob_to_regs(ctrl, i, oob,
232693db446aSBoris Brezillon 					mtd->oobsize / trans,
232793db446aSBoris Brezillon 					host->hwcfg.sector_size_1k);
232893db446aSBoris Brezillon 		}
232993db446aSBoris Brezillon 
233093db446aSBoris Brezillon 		/* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
233193db446aSBoris Brezillon 		brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
2332f1d46942SBoris Brezillon 		status = brcmnand_waitfunc(chip);
233393db446aSBoris Brezillon 
233493db446aSBoris Brezillon 		if (status & NAND_STATUS_FAIL) {
233593db446aSBoris Brezillon 			dev_info(ctrl->dev, "program failed at %llx\n",
233693db446aSBoris Brezillon 				(unsigned long long)addr);
233793db446aSBoris Brezillon 			ret = -EIO;
233893db446aSBoris Brezillon 			goto out;
233993db446aSBoris Brezillon 		}
234093db446aSBoris Brezillon 	}
234193db446aSBoris Brezillon out:
234293db446aSBoris Brezillon 	brcmnand_wp(mtd, 1);
234393db446aSBoris Brezillon 	return ret;
234493db446aSBoris Brezillon }
234593db446aSBoris Brezillon 
2346767eb6fbSBoris Brezillon static int brcmnand_write_page(struct nand_chip *chip, const uint8_t *buf,
2347767eb6fbSBoris Brezillon 			       int oob_required, int page)
234893db446aSBoris Brezillon {
2349767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
235093db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
235193db446aSBoris Brezillon 	void *oob = oob_required ? chip->oob_poi : NULL;
235293db446aSBoris Brezillon 
235393db446aSBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
235493db446aSBoris Brezillon 	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
235593db446aSBoris Brezillon 
235693db446aSBoris Brezillon 	return nand_prog_page_end_op(chip);
235793db446aSBoris Brezillon }
235893db446aSBoris Brezillon 
2359767eb6fbSBoris Brezillon static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
236093db446aSBoris Brezillon 				   int oob_required, int page)
236193db446aSBoris Brezillon {
2362767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
236393db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
236493db446aSBoris Brezillon 	void *oob = oob_required ? chip->oob_poi : NULL;
236593db446aSBoris Brezillon 
236693db446aSBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
236793db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 0);
236893db446aSBoris Brezillon 	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
236993db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 1);
237093db446aSBoris Brezillon 
237193db446aSBoris Brezillon 	return nand_prog_page_end_op(chip);
237293db446aSBoris Brezillon }
237393db446aSBoris Brezillon 
2374767eb6fbSBoris Brezillon static int brcmnand_write_oob(struct nand_chip *chip, int page)
237593db446aSBoris Brezillon {
2376767eb6fbSBoris Brezillon 	return brcmnand_write(nand_to_mtd(chip), chip,
2377767eb6fbSBoris Brezillon 			      (u64)page << chip->page_shift, NULL,
2378767eb6fbSBoris Brezillon 			      chip->oob_poi);
237993db446aSBoris Brezillon }
238093db446aSBoris Brezillon 
2381767eb6fbSBoris Brezillon static int brcmnand_write_oob_raw(struct nand_chip *chip, int page)
238293db446aSBoris Brezillon {
2383767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
238493db446aSBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
238593db446aSBoris Brezillon 	int ret;
238693db446aSBoris Brezillon 
238793db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 0);
238893db446aSBoris Brezillon 	ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
238993db446aSBoris Brezillon 				 (u8 *)chip->oob_poi);
239093db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 1);
239193db446aSBoris Brezillon 
239293db446aSBoris Brezillon 	return ret;
239393db446aSBoris Brezillon }
239493db446aSBoris Brezillon 
239593db446aSBoris Brezillon /***********************************************************************
239693db446aSBoris Brezillon  * Per-CS setup (1 NAND device)
239793db446aSBoris Brezillon  ***********************************************************************/
239893db446aSBoris Brezillon 
239993db446aSBoris Brezillon static int brcmnand_set_cfg(struct brcmnand_host *host,
240093db446aSBoris Brezillon 			    struct brcmnand_cfg *cfg)
240193db446aSBoris Brezillon {
240293db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
240393db446aSBoris Brezillon 	struct nand_chip *chip = &host->chip;
240493db446aSBoris Brezillon 	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
240593db446aSBoris Brezillon 	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
240693db446aSBoris Brezillon 			BRCMNAND_CS_CFG_EXT);
240793db446aSBoris Brezillon 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
240893db446aSBoris Brezillon 			BRCMNAND_CS_ACC_CONTROL);
240993db446aSBoris Brezillon 	u8 block_size = 0, page_size = 0, device_size = 0;
241093db446aSBoris Brezillon 	u32 tmp;
241193db446aSBoris Brezillon 
241293db446aSBoris Brezillon 	if (ctrl->block_sizes) {
241393db446aSBoris Brezillon 		int i, found;
241493db446aSBoris Brezillon 
241593db446aSBoris Brezillon 		for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
241693db446aSBoris Brezillon 			if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
241793db446aSBoris Brezillon 				block_size = i;
241893db446aSBoris Brezillon 				found = 1;
241993db446aSBoris Brezillon 			}
242093db446aSBoris Brezillon 		if (!found) {
242193db446aSBoris Brezillon 			dev_warn(ctrl->dev, "invalid block size %u\n",
242293db446aSBoris Brezillon 					cfg->block_size);
242393db446aSBoris Brezillon 			return -EINVAL;
242493db446aSBoris Brezillon 		}
242593db446aSBoris Brezillon 	} else {
242693db446aSBoris Brezillon 		block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
242793db446aSBoris Brezillon 	}
242893db446aSBoris Brezillon 
242993db446aSBoris Brezillon 	if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
243093db446aSBoris Brezillon 				cfg->block_size > ctrl->max_block_size)) {
243193db446aSBoris Brezillon 		dev_warn(ctrl->dev, "invalid block size %u\n",
243293db446aSBoris Brezillon 				cfg->block_size);
243393db446aSBoris Brezillon 		block_size = 0;
243493db446aSBoris Brezillon 	}
243593db446aSBoris Brezillon 
243693db446aSBoris Brezillon 	if (ctrl->page_sizes) {
243793db446aSBoris Brezillon 		int i, found;
243893db446aSBoris Brezillon 
243993db446aSBoris Brezillon 		for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
244093db446aSBoris Brezillon 			if (ctrl->page_sizes[i] == cfg->page_size) {
244193db446aSBoris Brezillon 				page_size = i;
244293db446aSBoris Brezillon 				found = 1;
244393db446aSBoris Brezillon 			}
244493db446aSBoris Brezillon 		if (!found) {
244593db446aSBoris Brezillon 			dev_warn(ctrl->dev, "invalid page size %u\n",
244693db446aSBoris Brezillon 					cfg->page_size);
244793db446aSBoris Brezillon 			return -EINVAL;
244893db446aSBoris Brezillon 		}
244993db446aSBoris Brezillon 	} else {
245093db446aSBoris Brezillon 		page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
245193db446aSBoris Brezillon 	}
245293db446aSBoris Brezillon 
245393db446aSBoris Brezillon 	if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
245493db446aSBoris Brezillon 				cfg->page_size > ctrl->max_page_size)) {
245593db446aSBoris Brezillon 		dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
245693db446aSBoris Brezillon 		return -EINVAL;
245793db446aSBoris Brezillon 	}
245893db446aSBoris Brezillon 
245993db446aSBoris Brezillon 	if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
246093db446aSBoris Brezillon 		dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
246193db446aSBoris Brezillon 			(unsigned long long)cfg->device_size);
246293db446aSBoris Brezillon 		return -EINVAL;
246393db446aSBoris Brezillon 	}
246493db446aSBoris Brezillon 	device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
246593db446aSBoris Brezillon 
246693db446aSBoris Brezillon 	tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) |
246793db446aSBoris Brezillon 		(cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) |
246893db446aSBoris Brezillon 		(cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) |
246993db446aSBoris Brezillon 		(!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) |
247093db446aSBoris Brezillon 		(device_size << CFG_DEVICE_SIZE_SHIFT);
247193db446aSBoris Brezillon 	if (cfg_offs == cfg_ext_offs) {
24727e7c7df5SÁlvaro Fernández Rojas 		tmp |= (page_size << ctrl->page_size_shift) |
247393db446aSBoris Brezillon 		       (block_size << CFG_BLK_SIZE_SHIFT);
247493db446aSBoris Brezillon 		nand_writereg(ctrl, cfg_offs, tmp);
247593db446aSBoris Brezillon 	} else {
247693db446aSBoris Brezillon 		nand_writereg(ctrl, cfg_offs, tmp);
247793db446aSBoris Brezillon 		tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) |
247893db446aSBoris Brezillon 		      (block_size << CFG_EXT_BLK_SIZE_SHIFT);
247993db446aSBoris Brezillon 		nand_writereg(ctrl, cfg_ext_offs, tmp);
248093db446aSBoris Brezillon 	}
248193db446aSBoris Brezillon 
248293db446aSBoris Brezillon 	tmp = nand_readreg(ctrl, acc_control_offs);
248393db446aSBoris Brezillon 	tmp &= ~brcmnand_ecc_level_mask(ctrl);
248493db446aSBoris Brezillon 	tmp &= ~brcmnand_spare_area_mask(ctrl);
24857e7c7df5SÁlvaro Fernández Rojas 	if (ctrl->nand_version >= 0x0302) {
24867e7c7df5SÁlvaro Fernández Rojas 		tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
248793db446aSBoris Brezillon 		tmp |= cfg->spare_area_size;
24887e7c7df5SÁlvaro Fernández Rojas 	}
248993db446aSBoris Brezillon 	nand_writereg(ctrl, acc_control_offs, tmp);
249093db446aSBoris Brezillon 
249193db446aSBoris Brezillon 	brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
249293db446aSBoris Brezillon 
249393db446aSBoris Brezillon 	/* threshold = ceil(BCH-level * 0.75) */
249493db446aSBoris Brezillon 	brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
249593db446aSBoris Brezillon 
249693db446aSBoris Brezillon 	return 0;
249793db446aSBoris Brezillon }
249893db446aSBoris Brezillon 
249993db446aSBoris Brezillon static void brcmnand_print_cfg(struct brcmnand_host *host,
250093db446aSBoris Brezillon 			       char *buf, struct brcmnand_cfg *cfg)
250193db446aSBoris Brezillon {
250293db446aSBoris Brezillon 	buf += sprintf(buf,
250393db446aSBoris Brezillon 		"%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
250493db446aSBoris Brezillon 		(unsigned long long)cfg->device_size >> 20,
250593db446aSBoris Brezillon 		cfg->block_size >> 10,
250693db446aSBoris Brezillon 		cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
250793db446aSBoris Brezillon 		cfg->page_size >= 1024 ? "KiB" : "B",
250893db446aSBoris Brezillon 		cfg->spare_area_size, cfg->device_width);
250993db446aSBoris Brezillon 
251093db446aSBoris Brezillon 	/* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
251193db446aSBoris Brezillon 	if (is_hamming_ecc(host->ctrl, cfg))
251293db446aSBoris Brezillon 		sprintf(buf, ", Hamming ECC");
251393db446aSBoris Brezillon 	else if (cfg->sector_size_1k)
251493db446aSBoris Brezillon 		sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
251593db446aSBoris Brezillon 	else
251693db446aSBoris Brezillon 		sprintf(buf, ", BCH-%u", cfg->ecc_level);
251793db446aSBoris Brezillon }
251893db446aSBoris Brezillon 
251993db446aSBoris Brezillon /*
252093db446aSBoris Brezillon  * Minimum number of bytes to address a page. Calculated as:
252193db446aSBoris Brezillon  *     roundup(log2(size / page-size) / 8)
252293db446aSBoris Brezillon  *
252393db446aSBoris Brezillon  * NB: the following does not "round up" for non-power-of-2 'size'; but this is
252493db446aSBoris Brezillon  *     OK because many other things will break if 'size' is irregular...
252593db446aSBoris Brezillon  */
252693db446aSBoris Brezillon static inline int get_blk_adr_bytes(u64 size, u32 writesize)
252793db446aSBoris Brezillon {
252893db446aSBoris Brezillon 	return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
252993db446aSBoris Brezillon }
253093db446aSBoris Brezillon 
253193db446aSBoris Brezillon static int brcmnand_setup_dev(struct brcmnand_host *host)
253293db446aSBoris Brezillon {
253393db446aSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
253493db446aSBoris Brezillon 	struct nand_chip *chip = &host->chip;
253553576c7bSMiquel Raynal 	const struct nand_ecc_props *requirements =
253653576c7bSMiquel Raynal 		nanddev_get_ecc_requirements(&chip->base);
253793db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
253893db446aSBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
253993db446aSBoris Brezillon 	char msg[128];
254093db446aSBoris Brezillon 	u32 offs, tmp, oob_sector;
254193db446aSBoris Brezillon 	int ret;
254293db446aSBoris Brezillon 
254393db446aSBoris Brezillon 	memset(cfg, 0, sizeof(*cfg));
254493db446aSBoris Brezillon 
254593db446aSBoris Brezillon 	ret = of_property_read_u32(nand_get_flash_node(chip),
254693db446aSBoris Brezillon 				   "brcm,nand-oob-sector-size",
254793db446aSBoris Brezillon 				   &oob_sector);
254893db446aSBoris Brezillon 	if (ret) {
254993db446aSBoris Brezillon 		/* Use detected size */
255093db446aSBoris Brezillon 		cfg->spare_area_size = mtd->oobsize /
255193db446aSBoris Brezillon 					(mtd->writesize >> FC_SHIFT);
255293db446aSBoris Brezillon 	} else {
255393db446aSBoris Brezillon 		cfg->spare_area_size = oob_sector;
255493db446aSBoris Brezillon 	}
255593db446aSBoris Brezillon 	if (cfg->spare_area_size > ctrl->max_oob)
255693db446aSBoris Brezillon 		cfg->spare_area_size = ctrl->max_oob;
255793db446aSBoris Brezillon 	/*
255893db446aSBoris Brezillon 	 * Set oobsize to be consistent with controller's spare_area_size, as
255993db446aSBoris Brezillon 	 * the rest is inaccessible.
256093db446aSBoris Brezillon 	 */
256193db446aSBoris Brezillon 	mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
256293db446aSBoris Brezillon 
256393db446aSBoris Brezillon 	cfg->device_size = mtd->size;
256493db446aSBoris Brezillon 	cfg->block_size = mtd->erasesize;
256593db446aSBoris Brezillon 	cfg->page_size = mtd->writesize;
256693db446aSBoris Brezillon 	cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
256793db446aSBoris Brezillon 	cfg->col_adr_bytes = 2;
256893db446aSBoris Brezillon 	cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
256993db446aSBoris Brezillon 
2570bace41f8SMiquel Raynal 	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) {
257193db446aSBoris Brezillon 		dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
2572bace41f8SMiquel Raynal 			chip->ecc.engine_type);
257393db446aSBoris Brezillon 		return -EINVAL;
257493db446aSBoris Brezillon 	}
257593db446aSBoris Brezillon 
2576e0a564aeSMiquel Raynal 	if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) {
257793db446aSBoris Brezillon 		if (chip->ecc.strength == 1 && chip->ecc.size == 512)
257893db446aSBoris Brezillon 			/* Default to Hamming for 1-bit ECC, if unspecified */
2579e0a564aeSMiquel Raynal 			chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
258093db446aSBoris Brezillon 		else
258193db446aSBoris Brezillon 			/* Otherwise, BCH */
2582e0a564aeSMiquel Raynal 			chip->ecc.algo = NAND_ECC_ALGO_BCH;
258393db446aSBoris Brezillon 	}
258493db446aSBoris Brezillon 
2585e0a564aeSMiquel Raynal 	if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING &&
2586e0a564aeSMiquel Raynal 	    (chip->ecc.strength != 1 || chip->ecc.size != 512)) {
258793db446aSBoris Brezillon 		dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
258893db446aSBoris Brezillon 			chip->ecc.strength, chip->ecc.size);
258993db446aSBoris Brezillon 		return -EINVAL;
259093db446aSBoris Brezillon 	}
259193db446aSBoris Brezillon 
2592bace41f8SMiquel Raynal 	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_NONE &&
259378933218SKamal Dasu 	    (!chip->ecc.size || !chip->ecc.strength)) {
259453576c7bSMiquel Raynal 		if (requirements->step_size && requirements->strength) {
259578933218SKamal Dasu 			/* use detected ECC parameters */
259653576c7bSMiquel Raynal 			chip->ecc.size = requirements->step_size;
259753576c7bSMiquel Raynal 			chip->ecc.strength = requirements->strength;
259878933218SKamal Dasu 			dev_info(ctrl->dev, "Using ECC step-size %d, strength %d\n",
259978933218SKamal Dasu 				chip->ecc.size, chip->ecc.strength);
260078933218SKamal Dasu 		}
260178933218SKamal Dasu 	}
260278933218SKamal Dasu 
260393db446aSBoris Brezillon 	switch (chip->ecc.size) {
260493db446aSBoris Brezillon 	case 512:
2605e0a564aeSMiquel Raynal 		if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING)
260693db446aSBoris Brezillon 			cfg->ecc_level = 15;
260793db446aSBoris Brezillon 		else
260893db446aSBoris Brezillon 			cfg->ecc_level = chip->ecc.strength;
260993db446aSBoris Brezillon 		cfg->sector_size_1k = 0;
261093db446aSBoris Brezillon 		break;
261193db446aSBoris Brezillon 	case 1024:
261293db446aSBoris Brezillon 		if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
261393db446aSBoris Brezillon 			dev_err(ctrl->dev, "1KB sectors not supported\n");
261493db446aSBoris Brezillon 			return -EINVAL;
261593db446aSBoris Brezillon 		}
261693db446aSBoris Brezillon 		if (chip->ecc.strength & 0x1) {
261793db446aSBoris Brezillon 			dev_err(ctrl->dev,
261893db446aSBoris Brezillon 				"odd ECC not supported with 1KB sectors\n");
261993db446aSBoris Brezillon 			return -EINVAL;
262093db446aSBoris Brezillon 		}
262193db446aSBoris Brezillon 
262293db446aSBoris Brezillon 		cfg->ecc_level = chip->ecc.strength >> 1;
262393db446aSBoris Brezillon 		cfg->sector_size_1k = 1;
262493db446aSBoris Brezillon 		break;
262593db446aSBoris Brezillon 	default:
262693db446aSBoris Brezillon 		dev_err(ctrl->dev, "unsupported ECC size: %d\n",
262793db446aSBoris Brezillon 			chip->ecc.size);
262893db446aSBoris Brezillon 		return -EINVAL;
262993db446aSBoris Brezillon 	}
263093db446aSBoris Brezillon 
263193db446aSBoris Brezillon 	cfg->ful_adr_bytes = cfg->blk_adr_bytes;
263293db446aSBoris Brezillon 	if (mtd->writesize > 512)
263393db446aSBoris Brezillon 		cfg->ful_adr_bytes += cfg->col_adr_bytes;
263493db446aSBoris Brezillon 	else
263593db446aSBoris Brezillon 		cfg->ful_adr_bytes += 1;
263693db446aSBoris Brezillon 
263793db446aSBoris Brezillon 	ret = brcmnand_set_cfg(host, cfg);
263893db446aSBoris Brezillon 	if (ret)
263993db446aSBoris Brezillon 		return ret;
264093db446aSBoris Brezillon 
264193db446aSBoris Brezillon 	brcmnand_set_ecc_enabled(host, 1);
264293db446aSBoris Brezillon 
264393db446aSBoris Brezillon 	brcmnand_print_cfg(host, msg, cfg);
264493db446aSBoris Brezillon 	dev_info(ctrl->dev, "detected %s\n", msg);
264593db446aSBoris Brezillon 
264693db446aSBoris Brezillon 	/* Configure ACC_CONTROL */
264793db446aSBoris Brezillon 	offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
264893db446aSBoris Brezillon 	tmp = nand_readreg(ctrl, offs);
264993db446aSBoris Brezillon 	tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
265093db446aSBoris Brezillon 	tmp &= ~ACC_CONTROL_RD_ERASED;
265193db446aSBoris Brezillon 
265293db446aSBoris Brezillon 	/* We need to turn on Read from erased paged protected by ECC */
265393db446aSBoris Brezillon 	if (ctrl->nand_version >= 0x0702)
265493db446aSBoris Brezillon 		tmp |= ACC_CONTROL_RD_ERASED;
265593db446aSBoris Brezillon 	tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
265693db446aSBoris Brezillon 	if (ctrl->features & BRCMNAND_HAS_PREFETCH)
265793db446aSBoris Brezillon 		tmp &= ~ACC_CONTROL_PREFETCH;
265893db446aSBoris Brezillon 
265993db446aSBoris Brezillon 	nand_writereg(ctrl, offs, tmp);
266093db446aSBoris Brezillon 
266193db446aSBoris Brezillon 	return 0;
266293db446aSBoris Brezillon }
266393db446aSBoris Brezillon 
26644918b905SMiquel Raynal static int brcmnand_attach_chip(struct nand_chip *chip)
26654918b905SMiquel Raynal {
26664918b905SMiquel Raynal 	struct mtd_info *mtd = nand_to_mtd(chip);
26674918b905SMiquel Raynal 	struct brcmnand_host *host = nand_get_controller_data(chip);
26684918b905SMiquel Raynal 	int ret;
26694918b905SMiquel Raynal 
26704918b905SMiquel Raynal 	chip->options |= NAND_NO_SUBPAGE_WRITE;
26714918b905SMiquel Raynal 	/*
26724918b905SMiquel Raynal 	 * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
26734918b905SMiquel Raynal 	 * to/from, and have nand_base pass us a bounce buffer instead, as
26744918b905SMiquel Raynal 	 * needed.
26754918b905SMiquel Raynal 	 */
2676ce8148d7SMiquel Raynal 	chip->options |= NAND_USES_DMA;
26774918b905SMiquel Raynal 
26784918b905SMiquel Raynal 	if (chip->bbt_options & NAND_BBT_USE_FLASH)
26794918b905SMiquel Raynal 		chip->bbt_options |= NAND_BBT_NO_OOB;
26804918b905SMiquel Raynal 
26814918b905SMiquel Raynal 	if (brcmnand_setup_dev(host))
26824918b905SMiquel Raynal 		return -ENXIO;
26834918b905SMiquel Raynal 
26844918b905SMiquel Raynal 	chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
26854918b905SMiquel Raynal 
26864918b905SMiquel Raynal 	/* only use our internal HW threshold */
26874918b905SMiquel Raynal 	mtd->bitflip_threshold = 1;
26884918b905SMiquel Raynal 
26894918b905SMiquel Raynal 	ret = brcmstb_choose_ecc_layout(host);
26904918b905SMiquel Raynal 
26914918b905SMiquel Raynal 	return ret;
26924918b905SMiquel Raynal }
26934918b905SMiquel Raynal 
26944918b905SMiquel Raynal static const struct nand_controller_ops brcmnand_controller_ops = {
26954918b905SMiquel Raynal 	.attach_chip = brcmnand_attach_chip,
26964918b905SMiquel Raynal };
26974918b905SMiquel Raynal 
269893db446aSBoris Brezillon static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
269993db446aSBoris Brezillon {
270093db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
270193db446aSBoris Brezillon 	struct platform_device *pdev = host->pdev;
270293db446aSBoris Brezillon 	struct mtd_info *mtd;
270393db446aSBoris Brezillon 	struct nand_chip *chip;
270493db446aSBoris Brezillon 	int ret;
270593db446aSBoris Brezillon 	u16 cfg_offs;
270693db446aSBoris Brezillon 
270793db446aSBoris Brezillon 	ret = of_property_read_u32(dn, "reg", &host->cs);
270893db446aSBoris Brezillon 	if (ret) {
270993db446aSBoris Brezillon 		dev_err(&pdev->dev, "can't get chip-select\n");
271093db446aSBoris Brezillon 		return -ENXIO;
271193db446aSBoris Brezillon 	}
271293db446aSBoris Brezillon 
271393db446aSBoris Brezillon 	mtd = nand_to_mtd(&host->chip);
271493db446aSBoris Brezillon 	chip = &host->chip;
271593db446aSBoris Brezillon 
271693db446aSBoris Brezillon 	nand_set_flash_node(chip, dn);
271793db446aSBoris Brezillon 	nand_set_controller_data(chip, host);
271893db446aSBoris Brezillon 	mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
271993db446aSBoris Brezillon 				   host->cs);
272093db446aSBoris Brezillon 	if (!mtd->name)
272193db446aSBoris Brezillon 		return -ENOMEM;
272293db446aSBoris Brezillon 
272393db446aSBoris Brezillon 	mtd->owner = THIS_MODULE;
272493db446aSBoris Brezillon 	mtd->dev.parent = &pdev->dev;
272593db446aSBoris Brezillon 
2726bf6065c6SBoris Brezillon 	chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
2727bf6065c6SBoris Brezillon 	chip->legacy.cmdfunc = brcmnand_cmdfunc;
27288395b753SBoris Brezillon 	chip->legacy.waitfunc = brcmnand_waitfunc;
2729716bbbabSBoris Brezillon 	chip->legacy.read_byte = brcmnand_read_byte;
2730716bbbabSBoris Brezillon 	chip->legacy.read_buf = brcmnand_read_buf;
2731716bbbabSBoris Brezillon 	chip->legacy.write_buf = brcmnand_write_buf;
273293db446aSBoris Brezillon 
2733bace41f8SMiquel Raynal 	chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
273493db446aSBoris Brezillon 	chip->ecc.read_page = brcmnand_read_page;
273593db446aSBoris Brezillon 	chip->ecc.write_page = brcmnand_write_page;
273693db446aSBoris Brezillon 	chip->ecc.read_page_raw = brcmnand_read_page_raw;
273793db446aSBoris Brezillon 	chip->ecc.write_page_raw = brcmnand_write_page_raw;
273893db446aSBoris Brezillon 	chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
273993db446aSBoris Brezillon 	chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
274093db446aSBoris Brezillon 	chip->ecc.read_oob = brcmnand_read_oob;
274193db446aSBoris Brezillon 	chip->ecc.write_oob = brcmnand_write_oob;
274293db446aSBoris Brezillon 
274393db446aSBoris Brezillon 	chip->controller = &ctrl->controller;
274493db446aSBoris Brezillon 
274593db446aSBoris Brezillon 	/*
274693db446aSBoris Brezillon 	 * The bootloader might have configured 16bit mode but
274793db446aSBoris Brezillon 	 * NAND READID command only works in 8bit mode. We force
274893db446aSBoris Brezillon 	 * 8bit mode here to ensure that NAND READID commands works.
274993db446aSBoris Brezillon 	 */
275093db446aSBoris Brezillon 	cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
275193db446aSBoris Brezillon 	nand_writereg(ctrl, cfg_offs,
275293db446aSBoris Brezillon 		      nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
275393db446aSBoris Brezillon 
275400ad378fSBoris Brezillon 	ret = nand_scan(chip, 1);
275593db446aSBoris Brezillon 	if (ret)
275693db446aSBoris Brezillon 		return ret;
275793db446aSBoris Brezillon 
27585826b880SMiquel Raynal 	ret = mtd_device_register(mtd, NULL, 0);
27595826b880SMiquel Raynal 	if (ret)
27605826b880SMiquel Raynal 		nand_cleanup(chip);
27615826b880SMiquel Raynal 
27625826b880SMiquel Raynal 	return ret;
276393db446aSBoris Brezillon }
276493db446aSBoris Brezillon 
276593db446aSBoris Brezillon static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
276693db446aSBoris Brezillon 					    int restore)
276793db446aSBoris Brezillon {
276893db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = host->ctrl;
276993db446aSBoris Brezillon 	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
277093db446aSBoris Brezillon 	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
277193db446aSBoris Brezillon 			BRCMNAND_CS_CFG_EXT);
277293db446aSBoris Brezillon 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
277393db446aSBoris Brezillon 			BRCMNAND_CS_ACC_CONTROL);
277493db446aSBoris Brezillon 	u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
277593db446aSBoris Brezillon 	u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
277693db446aSBoris Brezillon 
277793db446aSBoris Brezillon 	if (restore) {
277893db446aSBoris Brezillon 		nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
277993db446aSBoris Brezillon 		if (cfg_offs != cfg_ext_offs)
278093db446aSBoris Brezillon 			nand_writereg(ctrl, cfg_ext_offs,
278193db446aSBoris Brezillon 				      host->hwcfg.config_ext);
278293db446aSBoris Brezillon 		nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
278393db446aSBoris Brezillon 		nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
278493db446aSBoris Brezillon 		nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
278593db446aSBoris Brezillon 	} else {
278693db446aSBoris Brezillon 		host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
278793db446aSBoris Brezillon 		if (cfg_offs != cfg_ext_offs)
278893db446aSBoris Brezillon 			host->hwcfg.config_ext =
278993db446aSBoris Brezillon 				nand_readreg(ctrl, cfg_ext_offs);
279093db446aSBoris Brezillon 		host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
279193db446aSBoris Brezillon 		host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
279293db446aSBoris Brezillon 		host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
279393db446aSBoris Brezillon 	}
279493db446aSBoris Brezillon }
279593db446aSBoris Brezillon 
279693db446aSBoris Brezillon static int brcmnand_suspend(struct device *dev)
279793db446aSBoris Brezillon {
279893db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
279993db446aSBoris Brezillon 	struct brcmnand_host *host;
280093db446aSBoris Brezillon 
280193db446aSBoris Brezillon 	list_for_each_entry(host, &ctrl->host_list, node)
280293db446aSBoris Brezillon 		brcmnand_save_restore_cs_config(host, 0);
280393db446aSBoris Brezillon 
280493db446aSBoris Brezillon 	ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
280593db446aSBoris Brezillon 	ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
280693db446aSBoris Brezillon 	ctrl->corr_stat_threshold =
280793db446aSBoris Brezillon 		brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
280893db446aSBoris Brezillon 
280993db446aSBoris Brezillon 	if (has_flash_dma(ctrl))
281093db446aSBoris Brezillon 		ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
2811a5d53ad2SKamal Dasu 	else if (has_edu(ctrl))
2812a5d53ad2SKamal Dasu 		ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG);
281393db446aSBoris Brezillon 
281493db446aSBoris Brezillon 	return 0;
281593db446aSBoris Brezillon }
281693db446aSBoris Brezillon 
281793db446aSBoris Brezillon static int brcmnand_resume(struct device *dev)
281893db446aSBoris Brezillon {
281993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
282093db446aSBoris Brezillon 	struct brcmnand_host *host;
282193db446aSBoris Brezillon 
282293db446aSBoris Brezillon 	if (has_flash_dma(ctrl)) {
282393db446aSBoris Brezillon 		flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
282493db446aSBoris Brezillon 		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
282593db446aSBoris Brezillon 	}
282693db446aSBoris Brezillon 
2827f3a6a6c5SKamal Dasu 	if (has_edu(ctrl)) {
2828a5d53ad2SKamal Dasu 		ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG);
2829a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_CONFIG, ctrl->edu_config);
2830a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_CONFIG);
2831a5d53ad2SKamal Dasu 		brcmnand_edu_init(ctrl);
2832a5d53ad2SKamal Dasu 	}
2833a5d53ad2SKamal Dasu 
283493db446aSBoris Brezillon 	brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
283593db446aSBoris Brezillon 	brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
283693db446aSBoris Brezillon 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
283793db446aSBoris Brezillon 			ctrl->corr_stat_threshold);
283893db446aSBoris Brezillon 	if (ctrl->soc) {
283993db446aSBoris Brezillon 		/* Clear/re-enable interrupt */
284093db446aSBoris Brezillon 		ctrl->soc->ctlrdy_ack(ctrl->soc);
284193db446aSBoris Brezillon 		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
284293db446aSBoris Brezillon 	}
284393db446aSBoris Brezillon 
284493db446aSBoris Brezillon 	list_for_each_entry(host, &ctrl->host_list, node) {
284593db446aSBoris Brezillon 		struct nand_chip *chip = &host->chip;
284693db446aSBoris Brezillon 
284793db446aSBoris Brezillon 		brcmnand_save_restore_cs_config(host, 1);
284893db446aSBoris Brezillon 
284993db446aSBoris Brezillon 		/* Reset the chip, required by some chips after power-up */
285093db446aSBoris Brezillon 		nand_reset_op(chip);
285193db446aSBoris Brezillon 	}
285293db446aSBoris Brezillon 
285393db446aSBoris Brezillon 	return 0;
285493db446aSBoris Brezillon }
285593db446aSBoris Brezillon 
285693db446aSBoris Brezillon const struct dev_pm_ops brcmnand_pm_ops = {
285793db446aSBoris Brezillon 	.suspend		= brcmnand_suspend,
285893db446aSBoris Brezillon 	.resume			= brcmnand_resume,
285993db446aSBoris Brezillon };
286093db446aSBoris Brezillon EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
286193db446aSBoris Brezillon 
286293db446aSBoris Brezillon static const struct of_device_id brcmnand_of_match[] = {
28637e7c7df5SÁlvaro Fernández Rojas 	{ .compatible = "brcm,brcmnand-v2.1" },
28647e7c7df5SÁlvaro Fernández Rojas 	{ .compatible = "brcm,brcmnand-v2.2" },
286593db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v4.0" },
286693db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v5.0" },
286793db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v6.0" },
286893db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v6.1" },
286993db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v6.2" },
287093db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v7.0" },
287193db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v7.1" },
287293db446aSBoris Brezillon 	{ .compatible = "brcm,brcmnand-v7.2" },
28730c06da57SKamal Dasu 	{ .compatible = "brcm,brcmnand-v7.3" },
287493db446aSBoris Brezillon 	{},
287593db446aSBoris Brezillon };
287693db446aSBoris Brezillon MODULE_DEVICE_TABLE(of, brcmnand_of_match);
287793db446aSBoris Brezillon 
287893db446aSBoris Brezillon /***********************************************************************
287993db446aSBoris Brezillon  * Platform driver setup (per controller)
288093db446aSBoris Brezillon  ***********************************************************************/
2881a5d53ad2SKamal Dasu static int brcmnand_edu_setup(struct platform_device *pdev)
2882a5d53ad2SKamal Dasu {
2883a5d53ad2SKamal Dasu 	struct device *dev = &pdev->dev;
2884a5d53ad2SKamal Dasu 	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
2885a5d53ad2SKamal Dasu 	struct resource *res;
2886a5d53ad2SKamal Dasu 	int ret;
2887a5d53ad2SKamal Dasu 
2888a5d53ad2SKamal Dasu 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-edu");
2889a5d53ad2SKamal Dasu 	if (res) {
2890a5d53ad2SKamal Dasu 		ctrl->edu_base = devm_ioremap_resource(dev, res);
2891a5d53ad2SKamal Dasu 		if (IS_ERR(ctrl->edu_base))
2892a5d53ad2SKamal Dasu 			return PTR_ERR(ctrl->edu_base);
2893a5d53ad2SKamal Dasu 
2894a5d53ad2SKamal Dasu 		ctrl->edu_offsets = edu_regs;
2895a5d53ad2SKamal Dasu 
2896a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_CONFIG, EDU_CONFIG_MODE_NAND |
2897a5d53ad2SKamal Dasu 			   EDU_CONFIG_SWAP_CFG);
2898a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_CONFIG);
2899a5d53ad2SKamal Dasu 
2900a5d53ad2SKamal Dasu 		/* initialize edu */
2901a5d53ad2SKamal Dasu 		brcmnand_edu_init(ctrl);
2902a5d53ad2SKamal Dasu 
2903a5d53ad2SKamal Dasu 		ctrl->edu_irq = platform_get_irq_optional(pdev, 1);
2904a5d53ad2SKamal Dasu 		if (ctrl->edu_irq < 0) {
2905a5d53ad2SKamal Dasu 			dev_warn(dev,
2906a5d53ad2SKamal Dasu 				 "FLASH EDU enabled, using ctlrdy irq\n");
2907a5d53ad2SKamal Dasu 		} else {
2908a5d53ad2SKamal Dasu 			ret = devm_request_irq(dev, ctrl->edu_irq,
2909a5d53ad2SKamal Dasu 					       brcmnand_edu_irq, 0,
2910a5d53ad2SKamal Dasu 					       "brcmnand-edu", ctrl);
2911a5d53ad2SKamal Dasu 			if (ret < 0) {
2912a5d53ad2SKamal Dasu 				dev_err(ctrl->dev, "can't allocate IRQ %d: error %d\n",
2913a5d53ad2SKamal Dasu 					ctrl->edu_irq, ret);
2914a5d53ad2SKamal Dasu 				return ret;
2915a5d53ad2SKamal Dasu 			}
2916a5d53ad2SKamal Dasu 
2917a5d53ad2SKamal Dasu 			dev_info(dev, "FLASH EDU enabled using irq %u\n",
2918a5d53ad2SKamal Dasu 				 ctrl->edu_irq);
2919a5d53ad2SKamal Dasu 		}
2920a5d53ad2SKamal Dasu 	}
2921a5d53ad2SKamal Dasu 
2922a5d53ad2SKamal Dasu 	return 0;
2923a5d53ad2SKamal Dasu }
292493db446aSBoris Brezillon 
292593db446aSBoris Brezillon int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
292693db446aSBoris Brezillon {
292793db446aSBoris Brezillon 	struct device *dev = &pdev->dev;
292893db446aSBoris Brezillon 	struct device_node *dn = dev->of_node, *child;
292993db446aSBoris Brezillon 	struct brcmnand_controller *ctrl;
293093db446aSBoris Brezillon 	struct resource *res;
293193db446aSBoris Brezillon 	int ret;
293293db446aSBoris Brezillon 
293393db446aSBoris Brezillon 	/* We only support device-tree instantiation */
293493db446aSBoris Brezillon 	if (!dn)
293593db446aSBoris Brezillon 		return -ENODEV;
293693db446aSBoris Brezillon 
293793db446aSBoris Brezillon 	if (!of_match_node(brcmnand_of_match, dn))
293893db446aSBoris Brezillon 		return -ENODEV;
293993db446aSBoris Brezillon 
294093db446aSBoris Brezillon 	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
294193db446aSBoris Brezillon 	if (!ctrl)
294293db446aSBoris Brezillon 		return -ENOMEM;
294393db446aSBoris Brezillon 
294493db446aSBoris Brezillon 	dev_set_drvdata(dev, ctrl);
294593db446aSBoris Brezillon 	ctrl->dev = dev;
294693db446aSBoris Brezillon 
294793db446aSBoris Brezillon 	init_completion(&ctrl->done);
294893db446aSBoris Brezillon 	init_completion(&ctrl->dma_done);
2949a5d53ad2SKamal Dasu 	init_completion(&ctrl->edu_done);
29507da45139SMiquel Raynal 	nand_controller_init(&ctrl->controller);
29514918b905SMiquel Raynal 	ctrl->controller.ops = &brcmnand_controller_ops;
295293db446aSBoris Brezillon 	INIT_LIST_HEAD(&ctrl->host_list);
295393db446aSBoris Brezillon 
295493db446aSBoris Brezillon 	/* NAND register range */
295593db446aSBoris Brezillon 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
295693db446aSBoris Brezillon 	ctrl->nand_base = devm_ioremap_resource(dev, res);
295793db446aSBoris Brezillon 	if (IS_ERR(ctrl->nand_base))
295893db446aSBoris Brezillon 		return PTR_ERR(ctrl->nand_base);
295993db446aSBoris Brezillon 
296093db446aSBoris Brezillon 	/* Enable clock before using NAND registers */
296193db446aSBoris Brezillon 	ctrl->clk = devm_clk_get(dev, "nand");
296293db446aSBoris Brezillon 	if (!IS_ERR(ctrl->clk)) {
296393db446aSBoris Brezillon 		ret = clk_prepare_enable(ctrl->clk);
296493db446aSBoris Brezillon 		if (ret)
296593db446aSBoris Brezillon 			return ret;
296693db446aSBoris Brezillon 	} else {
296793db446aSBoris Brezillon 		ret = PTR_ERR(ctrl->clk);
296893db446aSBoris Brezillon 		if (ret == -EPROBE_DEFER)
296993db446aSBoris Brezillon 			return ret;
297093db446aSBoris Brezillon 
297193db446aSBoris Brezillon 		ctrl->clk = NULL;
297293db446aSBoris Brezillon 	}
297393db446aSBoris Brezillon 
297493db446aSBoris Brezillon 	/* Initialize NAND revision */
297593db446aSBoris Brezillon 	ret = brcmnand_revision_init(ctrl);
297693db446aSBoris Brezillon 	if (ret)
297793db446aSBoris Brezillon 		goto err;
297893db446aSBoris Brezillon 
297993db446aSBoris Brezillon 	/*
298093db446aSBoris Brezillon 	 * Most chips have this cache at a fixed offset within 'nand' block.
298193db446aSBoris Brezillon 	 * Some must specify this region separately.
298293db446aSBoris Brezillon 	 */
298393db446aSBoris Brezillon 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
298493db446aSBoris Brezillon 	if (res) {
298593db446aSBoris Brezillon 		ctrl->nand_fc = devm_ioremap_resource(dev, res);
298693db446aSBoris Brezillon 		if (IS_ERR(ctrl->nand_fc)) {
298793db446aSBoris Brezillon 			ret = PTR_ERR(ctrl->nand_fc);
298893db446aSBoris Brezillon 			goto err;
298993db446aSBoris Brezillon 		}
299093db446aSBoris Brezillon 	} else {
299193db446aSBoris Brezillon 		ctrl->nand_fc = ctrl->nand_base +
299293db446aSBoris Brezillon 				ctrl->reg_offsets[BRCMNAND_FC_BASE];
299393db446aSBoris Brezillon 	}
299493db446aSBoris Brezillon 
299593db446aSBoris Brezillon 	/* FLASH_DMA */
299693db446aSBoris Brezillon 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
299793db446aSBoris Brezillon 	if (res) {
299893db446aSBoris Brezillon 		ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
299993db446aSBoris Brezillon 		if (IS_ERR(ctrl->flash_dma_base)) {
300093db446aSBoris Brezillon 			ret = PTR_ERR(ctrl->flash_dma_base);
300193db446aSBoris Brezillon 			goto err;
300293db446aSBoris Brezillon 		}
300393db446aSBoris Brezillon 
30040c06da57SKamal Dasu 		/* initialize the dma version */
30050c06da57SKamal Dasu 		brcmnand_flash_dma_revision_init(ctrl);
30060c06da57SKamal Dasu 
3007393947e5SFlorian Fainelli 		ret = -EIO;
3008393947e5SFlorian Fainelli 		if (ctrl->nand_version >= 0x0700)
3009393947e5SFlorian Fainelli 			ret = dma_set_mask_and_coherent(&pdev->dev,
3010393947e5SFlorian Fainelli 							DMA_BIT_MASK(40));
3011393947e5SFlorian Fainelli 		if (ret)
3012393947e5SFlorian Fainelli 			ret = dma_set_mask_and_coherent(&pdev->dev,
3013393947e5SFlorian Fainelli 							DMA_BIT_MASK(32));
3014393947e5SFlorian Fainelli 		if (ret)
3015393947e5SFlorian Fainelli 			goto err;
3016393947e5SFlorian Fainelli 
30170c06da57SKamal Dasu 		/* linked-list and stop on error */
30180c06da57SKamal Dasu 		flash_dma_writel(ctrl, FLASH_DMA_MODE, FLASH_DMA_MODE_MASK);
301993db446aSBoris Brezillon 		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
302093db446aSBoris Brezillon 
302193db446aSBoris Brezillon 		/* Allocate descriptor(s) */
302293db446aSBoris Brezillon 		ctrl->dma_desc = dmam_alloc_coherent(dev,
302393db446aSBoris Brezillon 						     sizeof(*ctrl->dma_desc),
302493db446aSBoris Brezillon 						     &ctrl->dma_pa, GFP_KERNEL);
302593db446aSBoris Brezillon 		if (!ctrl->dma_desc) {
302693db446aSBoris Brezillon 			ret = -ENOMEM;
302793db446aSBoris Brezillon 			goto err;
302893db446aSBoris Brezillon 		}
302993db446aSBoris Brezillon 
303093db446aSBoris Brezillon 		ctrl->dma_irq = platform_get_irq(pdev, 1);
303193db446aSBoris Brezillon 		if ((int)ctrl->dma_irq < 0) {
303293db446aSBoris Brezillon 			dev_err(dev, "missing FLASH_DMA IRQ\n");
303393db446aSBoris Brezillon 			ret = -ENODEV;
303493db446aSBoris Brezillon 			goto err;
303593db446aSBoris Brezillon 		}
303693db446aSBoris Brezillon 
303793db446aSBoris Brezillon 		ret = devm_request_irq(dev, ctrl->dma_irq,
303893db446aSBoris Brezillon 				brcmnand_dma_irq, 0, DRV_NAME,
303993db446aSBoris Brezillon 				ctrl);
304093db446aSBoris Brezillon 		if (ret < 0) {
304193db446aSBoris Brezillon 			dev_err(dev, "can't allocate IRQ %d: error %d\n",
304293db446aSBoris Brezillon 					ctrl->dma_irq, ret);
304393db446aSBoris Brezillon 			goto err;
304493db446aSBoris Brezillon 		}
304593db446aSBoris Brezillon 
304693db446aSBoris Brezillon 		dev_info(dev, "enabling FLASH_DMA\n");
3047a5d53ad2SKamal Dasu 		/* set flash dma transfer function to call */
3048a5d53ad2SKamal Dasu 		ctrl->dma_trans = brcmnand_dma_trans;
3049a5d53ad2SKamal Dasu 	} else	{
3050a5d53ad2SKamal Dasu 		ret = brcmnand_edu_setup(pdev);
3051a5d53ad2SKamal Dasu 		if (ret < 0)
3052a5d53ad2SKamal Dasu 			goto err;
3053a5d53ad2SKamal Dasu 
3054bee3ab8bSKamal Dasu 		if (has_edu(ctrl))
3055a5d53ad2SKamal Dasu 			/* set edu transfer function to call */
3056a5d53ad2SKamal Dasu 			ctrl->dma_trans = brcmnand_edu_trans;
305793db446aSBoris Brezillon 	}
305893db446aSBoris Brezillon 
305993db446aSBoris Brezillon 	/* Disable automatic device ID config, direct addressing */
306093db446aSBoris Brezillon 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
306193db446aSBoris Brezillon 			 CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
306293db446aSBoris Brezillon 	/* Disable XOR addressing */
306393db446aSBoris Brezillon 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
306493db446aSBoris Brezillon 
306593db446aSBoris Brezillon 	if (ctrl->features & BRCMNAND_HAS_WP) {
306693db446aSBoris Brezillon 		/* Permanently disable write protection */
306793db446aSBoris Brezillon 		if (wp_on == 2)
306893db446aSBoris Brezillon 			brcmnand_set_wp(ctrl, false);
306993db446aSBoris Brezillon 	} else {
307093db446aSBoris Brezillon 		wp_on = 0;
307193db446aSBoris Brezillon 	}
307293db446aSBoris Brezillon 
307393db446aSBoris Brezillon 	/* IRQ */
307493db446aSBoris Brezillon 	ctrl->irq = platform_get_irq(pdev, 0);
307593db446aSBoris Brezillon 	if ((int)ctrl->irq < 0) {
307693db446aSBoris Brezillon 		dev_err(dev, "no IRQ defined\n");
307793db446aSBoris Brezillon 		ret = -ENODEV;
307893db446aSBoris Brezillon 		goto err;
307993db446aSBoris Brezillon 	}
308093db446aSBoris Brezillon 
308193db446aSBoris Brezillon 	/*
308293db446aSBoris Brezillon 	 * Some SoCs integrate this controller (e.g., its interrupt bits) in
308393db446aSBoris Brezillon 	 * interesting ways
308493db446aSBoris Brezillon 	 */
308593db446aSBoris Brezillon 	if (soc) {
308693db446aSBoris Brezillon 		ctrl->soc = soc;
308793db446aSBoris Brezillon 
308893db446aSBoris Brezillon 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
308993db446aSBoris Brezillon 				       DRV_NAME, ctrl);
309093db446aSBoris Brezillon 
309193db446aSBoris Brezillon 		/* Enable interrupt */
309293db446aSBoris Brezillon 		ctrl->soc->ctlrdy_ack(ctrl->soc);
309393db446aSBoris Brezillon 		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
309493db446aSBoris Brezillon 	} else {
309593db446aSBoris Brezillon 		/* Use standard interrupt infrastructure */
309693db446aSBoris Brezillon 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
309793db446aSBoris Brezillon 				       DRV_NAME, ctrl);
309893db446aSBoris Brezillon 	}
309993db446aSBoris Brezillon 	if (ret < 0) {
310093db446aSBoris Brezillon 		dev_err(dev, "can't allocate IRQ %d: error %d\n",
310193db446aSBoris Brezillon 			ctrl->irq, ret);
310293db446aSBoris Brezillon 		goto err;
310393db446aSBoris Brezillon 	}
310493db446aSBoris Brezillon 
310593db446aSBoris Brezillon 	for_each_available_child_of_node(dn, child) {
310693db446aSBoris Brezillon 		if (of_device_is_compatible(child, "brcm,nandcs")) {
310793db446aSBoris Brezillon 			struct brcmnand_host *host;
310893db446aSBoris Brezillon 
310993db446aSBoris Brezillon 			host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
311093db446aSBoris Brezillon 			if (!host) {
311193db446aSBoris Brezillon 				of_node_put(child);
311293db446aSBoris Brezillon 				ret = -ENOMEM;
311393db446aSBoris Brezillon 				goto err;
311493db446aSBoris Brezillon 			}
311593db446aSBoris Brezillon 			host->pdev = pdev;
311693db446aSBoris Brezillon 			host->ctrl = ctrl;
311793db446aSBoris Brezillon 
311893db446aSBoris Brezillon 			ret = brcmnand_init_cs(host, child);
311993db446aSBoris Brezillon 			if (ret) {
312093db446aSBoris Brezillon 				devm_kfree(dev, host);
312193db446aSBoris Brezillon 				continue; /* Try all chip-selects */
312293db446aSBoris Brezillon 			}
312393db446aSBoris Brezillon 
312493db446aSBoris Brezillon 			list_add_tail(&host->node, &ctrl->host_list);
312593db446aSBoris Brezillon 		}
312693db446aSBoris Brezillon 	}
312793db446aSBoris Brezillon 
312893db446aSBoris Brezillon 	/* No chip-selects could initialize properly */
312993db446aSBoris Brezillon 	if (list_empty(&ctrl->host_list)) {
313093db446aSBoris Brezillon 		ret = -ENODEV;
313193db446aSBoris Brezillon 		goto err;
313293db446aSBoris Brezillon 	}
313393db446aSBoris Brezillon 
313493db446aSBoris Brezillon 	return 0;
313593db446aSBoris Brezillon 
313693db446aSBoris Brezillon err:
313793db446aSBoris Brezillon 	clk_disable_unprepare(ctrl->clk);
313893db446aSBoris Brezillon 	return ret;
313993db446aSBoris Brezillon 
314093db446aSBoris Brezillon }
314193db446aSBoris Brezillon EXPORT_SYMBOL_GPL(brcmnand_probe);
314293db446aSBoris Brezillon 
314393db446aSBoris Brezillon int brcmnand_remove(struct platform_device *pdev)
314493db446aSBoris Brezillon {
314593db446aSBoris Brezillon 	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
314693db446aSBoris Brezillon 	struct brcmnand_host *host;
3147937d039dSMiquel Raynal 	struct nand_chip *chip;
3148937d039dSMiquel Raynal 	int ret;
314993db446aSBoris Brezillon 
3150937d039dSMiquel Raynal 	list_for_each_entry(host, &ctrl->host_list, node) {
3151937d039dSMiquel Raynal 		chip = &host->chip;
3152937d039dSMiquel Raynal 		ret = mtd_device_unregister(nand_to_mtd(chip));
3153937d039dSMiquel Raynal 		WARN_ON(ret);
3154937d039dSMiquel Raynal 		nand_cleanup(chip);
3155937d039dSMiquel Raynal 	}
315693db446aSBoris Brezillon 
315793db446aSBoris Brezillon 	clk_disable_unprepare(ctrl->clk);
315893db446aSBoris Brezillon 
315993db446aSBoris Brezillon 	dev_set_drvdata(&pdev->dev, NULL);
316093db446aSBoris Brezillon 
316193db446aSBoris Brezillon 	return 0;
316293db446aSBoris Brezillon }
316393db446aSBoris Brezillon EXPORT_SYMBOL_GPL(brcmnand_remove);
316493db446aSBoris Brezillon 
316593db446aSBoris Brezillon MODULE_LICENSE("GPL v2");
316693db446aSBoris Brezillon MODULE_AUTHOR("Kevin Cernekee");
316793db446aSBoris Brezillon MODULE_AUTHOR("Brian Norris");
316893db446aSBoris Brezillon MODULE_DESCRIPTION("NAND driver for Broadcom chips");
316993db446aSBoris Brezillon MODULE_ALIAS("platform:brcmnand");
3170