xref: /linux/drivers/mtd/nand/raw/brcmnand/brcmnand.c (revision bace41f80f65dc4ba13c892bac783e7e81847379)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
227c5b17cSBrian Norris /*
327c5b17cSBrian Norris  * Copyright © 2010-2015 Broadcom Corporation
427c5b17cSBrian Norris  */
527c5b17cSBrian Norris 
65c05bc00SSimon Arlott #include <linux/clk.h>
727c5b17cSBrian Norris #include <linux/module.h>
827c5b17cSBrian Norris #include <linux/init.h>
927c5b17cSBrian Norris #include <linux/delay.h>
1027c5b17cSBrian Norris #include <linux/device.h>
1127c5b17cSBrian Norris #include <linux/platform_device.h>
1227c5b17cSBrian Norris #include <linux/err.h>
1327c5b17cSBrian Norris #include <linux/completion.h>
1427c5b17cSBrian Norris #include <linux/interrupt.h>
1527c5b17cSBrian Norris #include <linux/spinlock.h>
1627c5b17cSBrian Norris #include <linux/dma-mapping.h>
1727c5b17cSBrian Norris #include <linux/ioport.h>
1827c5b17cSBrian Norris #include <linux/bug.h>
1927c5b17cSBrian Norris #include <linux/kernel.h>
2027c5b17cSBrian Norris #include <linux/bitops.h>
2127c5b17cSBrian Norris #include <linux/mm.h>
2227c5b17cSBrian Norris #include <linux/mtd/mtd.h>
23d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
2427c5b17cSBrian Norris #include <linux/mtd/partitions.h>
2527c5b17cSBrian Norris #include <linux/of.h>
2627c5b17cSBrian Norris #include <linux/of_platform.h>
2727c5b17cSBrian Norris #include <linux/slab.h>
2827c5b17cSBrian Norris #include <linux/list.h>
2927c5b17cSBrian Norris #include <linux/log2.h>
3027c5b17cSBrian Norris 
3127c5b17cSBrian Norris #include "brcmnand.h"
3227c5b17cSBrian Norris 
3327c5b17cSBrian Norris /*
3427c5b17cSBrian Norris  * This flag controls if WP stays on between erase/write commands to mitigate
3527c5b17cSBrian Norris  * flash corruption due to power glitches. Values:
3627c5b17cSBrian Norris  * 0: NAND_WP is not used or not available
3727c5b17cSBrian Norris  * 1: NAND_WP is set by default, cleared for erase/write operations
3827c5b17cSBrian Norris  * 2: NAND_WP is always cleared
3927c5b17cSBrian Norris  */
4027c5b17cSBrian Norris static int wp_on = 1;
4127c5b17cSBrian Norris module_param(wp_on, int, 0444);
4227c5b17cSBrian Norris 
4327c5b17cSBrian Norris /***********************************************************************
4427c5b17cSBrian Norris  * Definitions
4527c5b17cSBrian Norris  ***********************************************************************/
4627c5b17cSBrian Norris 
4727c5b17cSBrian Norris #define DRV_NAME			"brcmnand"
4827c5b17cSBrian Norris 
4927c5b17cSBrian Norris #define CMD_NULL			0x00
5027c5b17cSBrian Norris #define CMD_PAGE_READ			0x01
5127c5b17cSBrian Norris #define CMD_SPARE_AREA_READ		0x02
5227c5b17cSBrian Norris #define CMD_STATUS_READ			0x03
5327c5b17cSBrian Norris #define CMD_PROGRAM_PAGE		0x04
5427c5b17cSBrian Norris #define CMD_PROGRAM_SPARE_AREA		0x05
5527c5b17cSBrian Norris #define CMD_COPY_BACK			0x06
5627c5b17cSBrian Norris #define CMD_DEVICE_ID_READ		0x07
5727c5b17cSBrian Norris #define CMD_BLOCK_ERASE			0x08
5827c5b17cSBrian Norris #define CMD_FLASH_RESET			0x09
5927c5b17cSBrian Norris #define CMD_BLOCKS_LOCK			0x0a
6027c5b17cSBrian Norris #define CMD_BLOCKS_LOCK_DOWN		0x0b
6127c5b17cSBrian Norris #define CMD_BLOCKS_UNLOCK		0x0c
6227c5b17cSBrian Norris #define CMD_READ_BLOCKS_LOCK_STATUS	0x0d
6327c5b17cSBrian Norris #define CMD_PARAMETER_READ		0x0e
6427c5b17cSBrian Norris #define CMD_PARAMETER_CHANGE_COL	0x0f
6527c5b17cSBrian Norris #define CMD_LOW_LEVEL_OP		0x10
6627c5b17cSBrian Norris 
6727c5b17cSBrian Norris struct brcm_nand_dma_desc {
6827c5b17cSBrian Norris 	u32 next_desc;
6927c5b17cSBrian Norris 	u32 next_desc_ext;
7027c5b17cSBrian Norris 	u32 cmd_irq;
7127c5b17cSBrian Norris 	u32 dram_addr;
7227c5b17cSBrian Norris 	u32 dram_addr_ext;
7327c5b17cSBrian Norris 	u32 tfr_len;
7427c5b17cSBrian Norris 	u32 total_len;
7527c5b17cSBrian Norris 	u32 flash_addr;
7627c5b17cSBrian Norris 	u32 flash_addr_ext;
7727c5b17cSBrian Norris 	u32 cs;
7827c5b17cSBrian Norris 	u32 pad2[5];
7927c5b17cSBrian Norris 	u32 status_valid;
8027c5b17cSBrian Norris } __packed;
8127c5b17cSBrian Norris 
8227c5b17cSBrian Norris /* Bitfields for brcm_nand_dma_desc::status_valid */
8327c5b17cSBrian Norris #define FLASH_DMA_ECC_ERROR	(1 << 8)
8427c5b17cSBrian Norris #define FLASH_DMA_CORR_ERROR	(1 << 9)
8527c5b17cSBrian Norris 
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 
9227c5b17cSBrian Norris /* 512B flash cache in the NAND controller HW */
9327c5b17cSBrian Norris #define FC_SHIFT		9U
9427c5b17cSBrian Norris #define FC_BYTES		512U
9527c5b17cSBrian Norris #define FC_WORDS		(FC_BYTES >> 2)
9627c5b17cSBrian Norris 
9727c5b17cSBrian Norris #define BRCMNAND_MIN_PAGESIZE	512
9827c5b17cSBrian Norris #define BRCMNAND_MIN_BLOCKSIZE	(8 * 1024)
9927c5b17cSBrian Norris #define BRCMNAND_MIN_DEVSIZE	(4ULL * 1024 * 1024)
10027c5b17cSBrian Norris 
1019d2ee0a6SKamal Dasu #define NAND_CTRL_RDY			(INTFC_CTLR_READY | INTFC_FLASH_READY)
1029d2ee0a6SKamal Dasu #define NAND_POLL_STATUS_TIMEOUT_MS	100
1039d2ee0a6SKamal Dasu 
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 
20027c5b17cSBrian Norris /* Controller feature flags */
20127c5b17cSBrian Norris enum {
20227c5b17cSBrian Norris 	BRCMNAND_HAS_1K_SECTORS			= BIT(0),
20327c5b17cSBrian Norris 	BRCMNAND_HAS_PREFETCH			= BIT(1),
20427c5b17cSBrian Norris 	BRCMNAND_HAS_CACHE_MODE			= BIT(2),
20527c5b17cSBrian Norris 	BRCMNAND_HAS_WP				= BIT(3),
20627c5b17cSBrian Norris };
20727c5b17cSBrian Norris 
208a5d53ad2SKamal Dasu struct brcmnand_host;
209a5d53ad2SKamal Dasu 
21027c5b17cSBrian Norris struct brcmnand_controller {
21127c5b17cSBrian Norris 	struct device		*dev;
2127da45139SMiquel Raynal 	struct nand_controller	controller;
21327c5b17cSBrian Norris 	void __iomem		*nand_base;
21427c5b17cSBrian Norris 	void __iomem		*nand_fc; /* flash cache */
21527c5b17cSBrian Norris 	void __iomem		*flash_dma_base;
21627c5b17cSBrian Norris 	unsigned int		irq;
21727c5b17cSBrian Norris 	unsigned int		dma_irq;
21827c5b17cSBrian Norris 	int			nand_version;
21927c5b17cSBrian Norris 
220c26211d3SBrian Norris 	/* Some SoCs provide custom interrupt status register(s) */
221c26211d3SBrian Norris 	struct brcmnand_soc	*soc;
222c26211d3SBrian Norris 
2235c05bc00SSimon Arlott 	/* Some SoCs have a gateable clock for the controller */
2245c05bc00SSimon Arlott 	struct clk		*clk;
2255c05bc00SSimon Arlott 
22627c5b17cSBrian Norris 	int			cmd_pending;
22727c5b17cSBrian Norris 	bool			dma_pending;
228a5d53ad2SKamal Dasu 	bool                    edu_pending;
22927c5b17cSBrian Norris 	struct completion	done;
23027c5b17cSBrian Norris 	struct completion	dma_done;
231a5d53ad2SKamal Dasu 	struct completion       edu_done;
23227c5b17cSBrian Norris 
23327c5b17cSBrian Norris 	/* List of NAND hosts (one for each chip-select) */
23427c5b17cSBrian Norris 	struct list_head host_list;
23527c5b17cSBrian Norris 
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;
24827c5b17cSBrian Norris 	struct brcm_nand_dma_desc *dma_desc;
24927c5b17cSBrian Norris 	dma_addr_t		dma_pa;
25027c5b17cSBrian Norris 
251a5d53ad2SKamal Dasu 	int (*dma_trans)(struct brcmnand_host *host, u64 addr, u32 *buf,
252a5d53ad2SKamal Dasu 			 u32 len, u8 dma_cmd);
253a5d53ad2SKamal Dasu 
25427c5b17cSBrian Norris 	/* in-memory cache of the FLASH_CACHE, used only for some commands */
255d618baf9SBrian Norris 	u8			flash_cache[FC_BYTES];
25627c5b17cSBrian Norris 
25727c5b17cSBrian Norris 	/* Controller revision details */
25827c5b17cSBrian Norris 	const u16		*reg_offsets;
25927c5b17cSBrian Norris 	unsigned int		reg_spacing; /* between CS1, CS2, ... regs */
26027c5b17cSBrian Norris 	const u8		*cs_offsets; /* within each chip-select */
26127c5b17cSBrian Norris 	const u8		*cs0_offsets; /* within CS0, if different */
26227c5b17cSBrian Norris 	unsigned int		max_block_size;
26327c5b17cSBrian Norris 	const unsigned int	*block_sizes;
26427c5b17cSBrian Norris 	unsigned int		max_page_size;
26527c5b17cSBrian Norris 	const unsigned int	*page_sizes;
2667e7c7df5SÁlvaro Fernández Rojas 	unsigned int		page_size_shift;
26727c5b17cSBrian Norris 	unsigned int		max_oob;
26827c5b17cSBrian Norris 	u32			features;
26927c5b17cSBrian Norris 
27027c5b17cSBrian Norris 	/* for low-power standby/resume only */
27127c5b17cSBrian Norris 	u32			nand_cs_nand_select;
27227c5b17cSBrian Norris 	u32			nand_cs_nand_xor;
27327c5b17cSBrian Norris 	u32			corr_stat_threshold;
27427c5b17cSBrian Norris 	u32			flash_dma_mode;
275a5d53ad2SKamal Dasu 	u32                     flash_edu_mode;
276c1ac2dc3SKamal Dasu 	bool			pio_poll_mode;
27727c5b17cSBrian Norris };
27827c5b17cSBrian Norris 
27927c5b17cSBrian Norris struct brcmnand_cfg {
28027c5b17cSBrian Norris 	u64			device_size;
28127c5b17cSBrian Norris 	unsigned int		block_size;
28227c5b17cSBrian Norris 	unsigned int		page_size;
28327c5b17cSBrian Norris 	unsigned int		spare_area_size;
28427c5b17cSBrian Norris 	unsigned int		device_width;
28527c5b17cSBrian Norris 	unsigned int		col_adr_bytes;
28627c5b17cSBrian Norris 	unsigned int		blk_adr_bytes;
28727c5b17cSBrian Norris 	unsigned int		ful_adr_bytes;
28827c5b17cSBrian Norris 	unsigned int		sector_size_1k;
28927c5b17cSBrian Norris 	unsigned int		ecc_level;
29027c5b17cSBrian Norris 	/* use for low-power standby/resume only */
29127c5b17cSBrian Norris 	u32			acc_control;
29227c5b17cSBrian Norris 	u32			config;
29327c5b17cSBrian Norris 	u32			config_ext;
29427c5b17cSBrian Norris 	u32			timing_1;
29527c5b17cSBrian Norris 	u32			timing_2;
29627c5b17cSBrian Norris };
29727c5b17cSBrian Norris 
29827c5b17cSBrian Norris struct brcmnand_host {
29927c5b17cSBrian Norris 	struct list_head	node;
30027c5b17cSBrian Norris 
30127c5b17cSBrian Norris 	struct nand_chip	chip;
30227c5b17cSBrian Norris 	struct platform_device	*pdev;
30327c5b17cSBrian Norris 	int			cs;
30427c5b17cSBrian Norris 
30527c5b17cSBrian Norris 	unsigned int		last_cmd;
30627c5b17cSBrian Norris 	unsigned int		last_byte;
30727c5b17cSBrian Norris 	u64			last_addr;
30827c5b17cSBrian Norris 	struct brcmnand_cfg	hwcfg;
30927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl;
31027c5b17cSBrian Norris };
31127c5b17cSBrian Norris 
31227c5b17cSBrian Norris enum brcmnand_reg {
31327c5b17cSBrian Norris 	BRCMNAND_CMD_START = 0,
31427c5b17cSBrian Norris 	BRCMNAND_CMD_EXT_ADDRESS,
31527c5b17cSBrian Norris 	BRCMNAND_CMD_ADDRESS,
31627c5b17cSBrian Norris 	BRCMNAND_INTFC_STATUS,
31727c5b17cSBrian Norris 	BRCMNAND_CS_SELECT,
31827c5b17cSBrian Norris 	BRCMNAND_CS_XOR,
31927c5b17cSBrian Norris 	BRCMNAND_LL_OP,
32027c5b17cSBrian Norris 	BRCMNAND_CS0_BASE,
32127c5b17cSBrian Norris 	BRCMNAND_CS1_BASE,		/* CS1 regs, if non-contiguous */
32227c5b17cSBrian Norris 	BRCMNAND_CORR_THRESHOLD,
32327c5b17cSBrian Norris 	BRCMNAND_CORR_THRESHOLD_EXT,
32427c5b17cSBrian Norris 	BRCMNAND_UNCORR_COUNT,
32527c5b17cSBrian Norris 	BRCMNAND_CORR_COUNT,
32627c5b17cSBrian Norris 	BRCMNAND_CORR_EXT_ADDR,
32727c5b17cSBrian Norris 	BRCMNAND_CORR_ADDR,
32827c5b17cSBrian Norris 	BRCMNAND_UNCORR_EXT_ADDR,
32927c5b17cSBrian Norris 	BRCMNAND_UNCORR_ADDR,
33027c5b17cSBrian Norris 	BRCMNAND_SEMAPHORE,
33127c5b17cSBrian Norris 	BRCMNAND_ID,
33227c5b17cSBrian Norris 	BRCMNAND_ID_EXT,
33327c5b17cSBrian Norris 	BRCMNAND_LL_RDATA,
33427c5b17cSBrian Norris 	BRCMNAND_OOB_READ_BASE,
33527c5b17cSBrian Norris 	BRCMNAND_OOB_READ_10_BASE,	/* offset 0x10, if non-contiguous */
33627c5b17cSBrian Norris 	BRCMNAND_OOB_WRITE_BASE,
33727c5b17cSBrian Norris 	BRCMNAND_OOB_WRITE_10_BASE,	/* offset 0x10, if non-contiguous */
33827c5b17cSBrian Norris 	BRCMNAND_FC_BASE,
33927c5b17cSBrian Norris };
34027c5b17cSBrian Norris 
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[] = {
37327c5b17cSBrian Norris 	[BRCMNAND_CMD_START]		=  0x04,
37427c5b17cSBrian Norris 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
37527c5b17cSBrian Norris 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
37627c5b17cSBrian Norris 	[BRCMNAND_INTFC_STATUS]		=  0x6c,
37727c5b17cSBrian Norris 	[BRCMNAND_CS_SELECT]		=  0x14,
37827c5b17cSBrian Norris 	[BRCMNAND_CS_XOR]		=  0x18,
37927c5b17cSBrian Norris 	[BRCMNAND_LL_OP]		= 0x178,
38027c5b17cSBrian Norris 	[BRCMNAND_CS0_BASE]		=  0x40,
38127c5b17cSBrian Norris 	[BRCMNAND_CS1_BASE]		=  0xd0,
38227c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
38327c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
38427c5b17cSBrian Norris 	[BRCMNAND_UNCORR_COUNT]		=     0,
38527c5b17cSBrian Norris 	[BRCMNAND_CORR_COUNT]		=     0,
38627c5b17cSBrian Norris 	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
38727c5b17cSBrian Norris 	[BRCMNAND_CORR_ADDR]		=  0x74,
38827c5b17cSBrian Norris 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
38927c5b17cSBrian Norris 	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
39027c5b17cSBrian Norris 	[BRCMNAND_SEMAPHORE]		=  0x58,
39127c5b17cSBrian Norris 	[BRCMNAND_ID]			=  0x60,
39227c5b17cSBrian Norris 	[BRCMNAND_ID_EXT]		=  0x64,
39327c5b17cSBrian Norris 	[BRCMNAND_LL_RDATA]		= 0x17c,
39427c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
39527c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
39627c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
39727c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
39827c5b17cSBrian Norris 	[BRCMNAND_FC_BASE]		= 0x200,
39927c5b17cSBrian Norris };
40027c5b17cSBrian Norris 
40127c5b17cSBrian Norris /* BRCMNAND v5.0 */
40227c5b17cSBrian Norris static const u16 brcmnand_regs_v50[] = {
40327c5b17cSBrian Norris 	[BRCMNAND_CMD_START]		=  0x04,
40427c5b17cSBrian Norris 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
40527c5b17cSBrian Norris 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
40627c5b17cSBrian Norris 	[BRCMNAND_INTFC_STATUS]		=  0x6c,
40727c5b17cSBrian Norris 	[BRCMNAND_CS_SELECT]		=  0x14,
40827c5b17cSBrian Norris 	[BRCMNAND_CS_XOR]		=  0x18,
40927c5b17cSBrian Norris 	[BRCMNAND_LL_OP]		= 0x178,
41027c5b17cSBrian Norris 	[BRCMNAND_CS0_BASE]		=  0x40,
41127c5b17cSBrian Norris 	[BRCMNAND_CS1_BASE]		=  0xd0,
41227c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
41327c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
41427c5b17cSBrian Norris 	[BRCMNAND_UNCORR_COUNT]		=     0,
41527c5b17cSBrian Norris 	[BRCMNAND_CORR_COUNT]		=     0,
41627c5b17cSBrian Norris 	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
41727c5b17cSBrian Norris 	[BRCMNAND_CORR_ADDR]		=  0x74,
41827c5b17cSBrian Norris 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
41927c5b17cSBrian Norris 	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
42027c5b17cSBrian Norris 	[BRCMNAND_SEMAPHORE]		=  0x58,
42127c5b17cSBrian Norris 	[BRCMNAND_ID]			=  0x60,
42227c5b17cSBrian Norris 	[BRCMNAND_ID_EXT]		=  0x64,
42327c5b17cSBrian Norris 	[BRCMNAND_LL_RDATA]		= 0x17c,
42427c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
42527c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
42627c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
42727c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_10_BASE]	= 0x140,
42827c5b17cSBrian Norris 	[BRCMNAND_FC_BASE]		= 0x200,
42927c5b17cSBrian Norris };
43027c5b17cSBrian Norris 
43127c5b17cSBrian Norris /* BRCMNAND v6.0 - v7.1 */
43227c5b17cSBrian Norris static const u16 brcmnand_regs_v60[] = {
43327c5b17cSBrian Norris 	[BRCMNAND_CMD_START]		=  0x04,
43427c5b17cSBrian Norris 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
43527c5b17cSBrian Norris 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
43627c5b17cSBrian Norris 	[BRCMNAND_INTFC_STATUS]		=  0x14,
43727c5b17cSBrian Norris 	[BRCMNAND_CS_SELECT]		=  0x18,
43827c5b17cSBrian Norris 	[BRCMNAND_CS_XOR]		=  0x1c,
43927c5b17cSBrian Norris 	[BRCMNAND_LL_OP]		=  0x20,
44027c5b17cSBrian Norris 	[BRCMNAND_CS0_BASE]		=  0x50,
44127c5b17cSBrian Norris 	[BRCMNAND_CS1_BASE]		=     0,
44227c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD]	=  0xc0,
44327c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xc4,
44427c5b17cSBrian Norris 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
44527c5b17cSBrian Norris 	[BRCMNAND_CORR_COUNT]		= 0x100,
44627c5b17cSBrian Norris 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
44727c5b17cSBrian Norris 	[BRCMNAND_CORR_ADDR]		= 0x110,
44827c5b17cSBrian Norris 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
44927c5b17cSBrian Norris 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
45027c5b17cSBrian Norris 	[BRCMNAND_SEMAPHORE]		= 0x150,
45127c5b17cSBrian Norris 	[BRCMNAND_ID]			= 0x194,
45227c5b17cSBrian Norris 	[BRCMNAND_ID_EXT]		= 0x198,
45327c5b17cSBrian Norris 	[BRCMNAND_LL_RDATA]		= 0x19c,
45427c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
45527c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
45627c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
45727c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
45827c5b17cSBrian Norris 	[BRCMNAND_FC_BASE]		= 0x400,
45927c5b17cSBrian Norris };
46027c5b17cSBrian Norris 
461d267aefcSFlorian Fainelli /* BRCMNAND v7.1 */
462d267aefcSFlorian Fainelli static const u16 brcmnand_regs_v71[] = {
463d267aefcSFlorian Fainelli 	[BRCMNAND_CMD_START]		=  0x04,
464d267aefcSFlorian Fainelli 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
465d267aefcSFlorian Fainelli 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
466d267aefcSFlorian Fainelli 	[BRCMNAND_INTFC_STATUS]		=  0x14,
467d267aefcSFlorian Fainelli 	[BRCMNAND_CS_SELECT]		=  0x18,
468d267aefcSFlorian Fainelli 	[BRCMNAND_CS_XOR]		=  0x1c,
469d267aefcSFlorian Fainelli 	[BRCMNAND_LL_OP]		=  0x20,
470d267aefcSFlorian Fainelli 	[BRCMNAND_CS0_BASE]		=  0x50,
471d267aefcSFlorian Fainelli 	[BRCMNAND_CS1_BASE]		=     0,
472d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD]	=  0xdc,
473d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xe0,
474d267aefcSFlorian Fainelli 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
475d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_COUNT]		= 0x100,
476d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
477d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_ADDR]		= 0x110,
478d267aefcSFlorian Fainelli 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
479d267aefcSFlorian Fainelli 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
480d267aefcSFlorian Fainelli 	[BRCMNAND_SEMAPHORE]		= 0x150,
481d267aefcSFlorian Fainelli 	[BRCMNAND_ID]			= 0x194,
482d267aefcSFlorian Fainelli 	[BRCMNAND_ID_EXT]		= 0x198,
483d267aefcSFlorian Fainelli 	[BRCMNAND_LL_RDATA]		= 0x19c,
484d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
485d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
486d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
487d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
488d267aefcSFlorian Fainelli 	[BRCMNAND_FC_BASE]		= 0x400,
489d267aefcSFlorian Fainelli };
490d267aefcSFlorian Fainelli 
491decba6d4SFlorian Fainelli /* BRCMNAND v7.2 */
492decba6d4SFlorian Fainelli static const u16 brcmnand_regs_v72[] = {
493decba6d4SFlorian Fainelli 	[BRCMNAND_CMD_START]		=  0x04,
494decba6d4SFlorian Fainelli 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
495decba6d4SFlorian Fainelli 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
496decba6d4SFlorian Fainelli 	[BRCMNAND_INTFC_STATUS]		=  0x14,
497decba6d4SFlorian Fainelli 	[BRCMNAND_CS_SELECT]		=  0x18,
498decba6d4SFlorian Fainelli 	[BRCMNAND_CS_XOR]		=  0x1c,
499decba6d4SFlorian Fainelli 	[BRCMNAND_LL_OP]		=  0x20,
500decba6d4SFlorian Fainelli 	[BRCMNAND_CS0_BASE]		=  0x50,
501decba6d4SFlorian Fainelli 	[BRCMNAND_CS1_BASE]		=     0,
502decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD]	=  0xdc,
503decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xe0,
504decba6d4SFlorian Fainelli 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
505decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_COUNT]		= 0x100,
506decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
507decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_ADDR]		= 0x110,
508decba6d4SFlorian Fainelli 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
509decba6d4SFlorian Fainelli 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
510decba6d4SFlorian Fainelli 	[BRCMNAND_SEMAPHORE]		= 0x150,
511decba6d4SFlorian Fainelli 	[BRCMNAND_ID]			= 0x194,
512decba6d4SFlorian Fainelli 	[BRCMNAND_ID_EXT]		= 0x198,
513decba6d4SFlorian Fainelli 	[BRCMNAND_LL_RDATA]		= 0x19c,
514decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
515decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
516decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_WRITE_BASE]	= 0x400,
517decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
518decba6d4SFlorian Fainelli 	[BRCMNAND_FC_BASE]		= 0x600,
519decba6d4SFlorian Fainelli };
520decba6d4SFlorian Fainelli 
52127c5b17cSBrian Norris enum brcmnand_cs_reg {
52227c5b17cSBrian Norris 	BRCMNAND_CS_CFG_EXT = 0,
52327c5b17cSBrian Norris 	BRCMNAND_CS_CFG,
52427c5b17cSBrian Norris 	BRCMNAND_CS_ACC_CONTROL,
52527c5b17cSBrian Norris 	BRCMNAND_CS_TIMING1,
52627c5b17cSBrian Norris 	BRCMNAND_CS_TIMING2,
52727c5b17cSBrian Norris };
52827c5b17cSBrian Norris 
52927c5b17cSBrian Norris /* Per chip-select offsets for v7.1 */
53027c5b17cSBrian Norris static const u8 brcmnand_cs_offsets_v71[] = {
53127c5b17cSBrian Norris 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
53227c5b17cSBrian Norris 	[BRCMNAND_CS_CFG_EXT]		= 0x04,
53327c5b17cSBrian Norris 	[BRCMNAND_CS_CFG]		= 0x08,
53427c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING1]		= 0x0c,
53527c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING2]		= 0x10,
53627c5b17cSBrian Norris };
53727c5b17cSBrian Norris 
53827c5b17cSBrian Norris /* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
53927c5b17cSBrian Norris static const u8 brcmnand_cs_offsets[] = {
54027c5b17cSBrian Norris 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
54127c5b17cSBrian Norris 	[BRCMNAND_CS_CFG_EXT]		= 0x04,
54227c5b17cSBrian Norris 	[BRCMNAND_CS_CFG]		= 0x04,
54327c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING1]		= 0x08,
54427c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING2]		= 0x0c,
54527c5b17cSBrian Norris };
54627c5b17cSBrian Norris 
54727c5b17cSBrian Norris /* Per chip-select offset for <= v5.0 on CS0 only */
54827c5b17cSBrian Norris static const u8 brcmnand_cs_offsets_cs0[] = {
54927c5b17cSBrian Norris 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
55027c5b17cSBrian Norris 	[BRCMNAND_CS_CFG_EXT]		= 0x08,
55127c5b17cSBrian Norris 	[BRCMNAND_CS_CFG]		= 0x08,
55227c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING1]		= 0x10,
55327c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING2]		= 0x14,
55427c5b17cSBrian Norris };
55527c5b17cSBrian Norris 
5563f06d2a9SBrian Norris /*
5573f06d2a9SBrian Norris  * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
5583f06d2a9SBrian Norris  * one config register, but once the bitfields overflowed, newer controllers
5593f06d2a9SBrian Norris  * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
5603f06d2a9SBrian Norris  */
5613f06d2a9SBrian Norris enum {
5623f06d2a9SBrian Norris 	CFG_BLK_ADR_BYTES_SHIFT		= 8,
5633f06d2a9SBrian Norris 	CFG_COL_ADR_BYTES_SHIFT		= 12,
5643f06d2a9SBrian Norris 	CFG_FUL_ADR_BYTES_SHIFT		= 16,
5653f06d2a9SBrian Norris 	CFG_BUS_WIDTH_SHIFT		= 23,
5663f06d2a9SBrian Norris 	CFG_BUS_WIDTH			= BIT(CFG_BUS_WIDTH_SHIFT),
5673f06d2a9SBrian Norris 	CFG_DEVICE_SIZE_SHIFT		= 24,
5683f06d2a9SBrian Norris 
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 
5723f06d2a9SBrian Norris 	/* Only for pre-v7.1 (with no CFG_EXT register) */
5733f06d2a9SBrian Norris 	CFG_PAGE_SIZE_SHIFT		= 20,
5743f06d2a9SBrian Norris 	CFG_BLK_SIZE_SHIFT		= 28,
5753f06d2a9SBrian Norris 
5763f06d2a9SBrian Norris 	/* Only for v7.1+ (with CFG_EXT register) */
5773f06d2a9SBrian Norris 	CFG_EXT_PAGE_SIZE_SHIFT		= 0,
5783f06d2a9SBrian Norris 	CFG_EXT_BLK_SIZE_SHIFT		= 4,
5793f06d2a9SBrian Norris };
5803f06d2a9SBrian Norris 
58127c5b17cSBrian Norris /* BRCMNAND_INTFC_STATUS */
58227c5b17cSBrian Norris enum {
58327c5b17cSBrian Norris 	INTFC_FLASH_STATUS		= GENMASK(7, 0),
58427c5b17cSBrian Norris 
58527c5b17cSBrian Norris 	INTFC_ERASED			= BIT(27),
58627c5b17cSBrian Norris 	INTFC_OOB_VALID			= BIT(28),
58727c5b17cSBrian Norris 	INTFC_CACHE_VALID		= BIT(29),
58827c5b17cSBrian Norris 	INTFC_FLASH_READY		= BIT(30),
58927c5b17cSBrian Norris 	INTFC_CTLR_READY		= BIT(31),
59027c5b17cSBrian Norris };
59127c5b17cSBrian Norris 
59227c5b17cSBrian Norris static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
59327c5b17cSBrian Norris {
59427c5b17cSBrian Norris 	return brcmnand_readl(ctrl->nand_base + offs);
59527c5b17cSBrian Norris }
59627c5b17cSBrian Norris 
59727c5b17cSBrian Norris static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
59827c5b17cSBrian Norris 				 u32 val)
59927c5b17cSBrian Norris {
60027c5b17cSBrian Norris 	brcmnand_writel(val, ctrl->nand_base + offs);
60127c5b17cSBrian Norris }
60227c5b17cSBrian Norris 
60327c5b17cSBrian Norris static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
60427c5b17cSBrian Norris {
60527c5b17cSBrian Norris 	static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
60627c5b17cSBrian Norris 	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 };
61227c5b17cSBrian Norris 
61327c5b17cSBrian Norris 	ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
61427c5b17cSBrian Norris 
6157e7c7df5SÁlvaro Fernández Rojas 	/* Only support v2.1+ */
6167e7c7df5SÁlvaro Fernández Rojas 	if (ctrl->nand_version < 0x0201) {
61727c5b17cSBrian Norris 		dev_err(ctrl->dev, "version %#x not supported\n",
61827c5b17cSBrian Norris 			ctrl->nand_version);
61927c5b17cSBrian Norris 		return -ENODEV;
62027c5b17cSBrian Norris 	}
62127c5b17cSBrian Norris 
62227c5b17cSBrian Norris 	/* Register offsets */
623decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
624decba6d4SFlorian Fainelli 		ctrl->reg_offsets = brcmnand_regs_v72;
6250c06da57SKamal Dasu 	else if (ctrl->nand_version == 0x0701)
626d267aefcSFlorian Fainelli 		ctrl->reg_offsets = brcmnand_regs_v71;
627d267aefcSFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
62827c5b17cSBrian Norris 		ctrl->reg_offsets = brcmnand_regs_v60;
62927c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
63027c5b17cSBrian Norris 		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;
63527c5b17cSBrian Norris 
63627c5b17cSBrian Norris 	/* Chip-select stride */
63727c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0701)
63827c5b17cSBrian Norris 		ctrl->reg_spacing = 0x14;
63927c5b17cSBrian Norris 	else
64027c5b17cSBrian Norris 		ctrl->reg_spacing = 0x10;
64127c5b17cSBrian Norris 
64227c5b17cSBrian Norris 	/* Per chip-select registers */
64327c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0701) {
64427c5b17cSBrian Norris 		ctrl->cs_offsets = brcmnand_cs_offsets_v71;
64527c5b17cSBrian Norris 	} else {
64627c5b17cSBrian Norris 		ctrl->cs_offsets = brcmnand_cs_offsets;
64727c5b17cSBrian Norris 
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)
65127c5b17cSBrian Norris 			ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
65227c5b17cSBrian Norris 	}
65327c5b17cSBrian Norris 
65427c5b17cSBrian Norris 	/* Page / block sizes */
65527c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0701) {
65627c5b17cSBrian Norris 		/* >= v7.1 use nice power-of-2 values! */
65727c5b17cSBrian Norris 		ctrl->max_page_size = 16 * 1024;
65827c5b17cSBrian Norris 		ctrl->max_block_size = 2 * 1024 * 1024;
65927c5b17cSBrian Norris 	} 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 
67227c5b17cSBrian Norris 		if (ctrl->nand_version >= 0x0600)
67327c5b17cSBrian Norris 			ctrl->block_sizes = block_sizes_v6;
6747e7c7df5SÁlvaro Fernández Rojas 		else if (ctrl->nand_version >= 0x0400)
67527c5b17cSBrian Norris 			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;
68027c5b17cSBrian Norris 
68127c5b17cSBrian Norris 		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
68527c5b17cSBrian Norris 				ctrl->max_page_size = 4096;
68627c5b17cSBrian Norris 			ctrl->max_block_size = 512 * 1024;
68727c5b17cSBrian Norris 		}
68827c5b17cSBrian Norris 	}
68927c5b17cSBrian Norris 
69027c5b17cSBrian Norris 	/* Maximum spare area sector size (per 512B) */
6910c06da57SKamal Dasu 	if (ctrl->nand_version == 0x0702)
692decba6d4SFlorian Fainelli 		ctrl->max_oob = 128;
693decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
69427c5b17cSBrian Norris 		ctrl->max_oob = 64;
69527c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
69627c5b17cSBrian Norris 		ctrl->max_oob = 32;
69727c5b17cSBrian Norris 	else
69827c5b17cSBrian Norris 		ctrl->max_oob = 16;
69927c5b17cSBrian Norris 
70027c5b17cSBrian Norris 	/* v6.0 and newer (except v6.1) have prefetch support */
70127c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
70227c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_PREFETCH;
70327c5b17cSBrian Norris 
70427c5b17cSBrian Norris 	/*
70527c5b17cSBrian Norris 	 * v6.x has cache mode, but it's implemented differently. Ignore it for
70627c5b17cSBrian Norris 	 * now.
70727c5b17cSBrian Norris 	 */
70827c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0700)
70927c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
71027c5b17cSBrian Norris 
71127c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0500)
71227c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
71327c5b17cSBrian Norris 
71427c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0700)
71527c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_WP;
71627c5b17cSBrian Norris 	else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
71727c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_WP;
71827c5b17cSBrian Norris 
71927c5b17cSBrian Norris 	return 0;
72027c5b17cSBrian Norris }
72127c5b17cSBrian Norris 
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 
73327c5b17cSBrian Norris static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
73427c5b17cSBrian Norris 		enum brcmnand_reg reg)
73527c5b17cSBrian Norris {
73627c5b17cSBrian Norris 	u16 offs = ctrl->reg_offsets[reg];
73727c5b17cSBrian Norris 
73827c5b17cSBrian Norris 	if (offs)
73927c5b17cSBrian Norris 		return nand_readreg(ctrl, offs);
74027c5b17cSBrian Norris 	else
74127c5b17cSBrian Norris 		return 0;
74227c5b17cSBrian Norris }
74327c5b17cSBrian Norris 
74427c5b17cSBrian Norris static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
74527c5b17cSBrian Norris 				      enum brcmnand_reg reg, u32 val)
74627c5b17cSBrian Norris {
74727c5b17cSBrian Norris 	u16 offs = ctrl->reg_offsets[reg];
74827c5b17cSBrian Norris 
74927c5b17cSBrian Norris 	if (offs)
75027c5b17cSBrian Norris 		nand_writereg(ctrl, offs, val);
75127c5b17cSBrian Norris }
75227c5b17cSBrian Norris 
75327c5b17cSBrian Norris static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
75427c5b17cSBrian Norris 				    enum brcmnand_reg reg, u32 mask, unsigned
75527c5b17cSBrian Norris 				    int shift, u32 val)
75627c5b17cSBrian Norris {
75727c5b17cSBrian Norris 	u32 tmp = brcmnand_read_reg(ctrl, reg);
75827c5b17cSBrian Norris 
75927c5b17cSBrian Norris 	tmp &= ~mask;
76027c5b17cSBrian Norris 	tmp |= val << shift;
76127c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, reg, tmp);
76227c5b17cSBrian Norris }
76327c5b17cSBrian Norris 
76427c5b17cSBrian Norris static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
76527c5b17cSBrian Norris {
76627c5b17cSBrian Norris 	return __raw_readl(ctrl->nand_fc + word * 4);
76727c5b17cSBrian Norris }
76827c5b17cSBrian Norris 
76927c5b17cSBrian Norris static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
77027c5b17cSBrian Norris 				     int word, u32 val)
77127c5b17cSBrian Norris {
77227c5b17cSBrian Norris 	__raw_writel(val, ctrl->nand_fc + word * 4);
77327c5b17cSBrian Norris }
77427c5b17cSBrian Norris 
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 
83927c5b17cSBrian Norris static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
84027c5b17cSBrian Norris 				     enum brcmnand_cs_reg reg)
84127c5b17cSBrian Norris {
84227c5b17cSBrian Norris 	u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
84327c5b17cSBrian Norris 	u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
84427c5b17cSBrian Norris 	u8 cs_offs;
84527c5b17cSBrian Norris 
84627c5b17cSBrian Norris 	if (cs == 0 && ctrl->cs0_offsets)
84727c5b17cSBrian Norris 		cs_offs = ctrl->cs0_offsets[reg];
84827c5b17cSBrian Norris 	else
84927c5b17cSBrian Norris 		cs_offs = ctrl->cs_offsets[reg];
85027c5b17cSBrian Norris 
85127c5b17cSBrian Norris 	if (cs && offs_cs1)
85227c5b17cSBrian Norris 		return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
85327c5b17cSBrian Norris 
85427c5b17cSBrian Norris 	return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
85527c5b17cSBrian Norris }
85627c5b17cSBrian Norris 
85727c5b17cSBrian Norris static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
85827c5b17cSBrian Norris {
85927c5b17cSBrian Norris 	if (ctrl->nand_version < 0x0600)
86027c5b17cSBrian Norris 		return 1;
86127c5b17cSBrian Norris 	return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
86227c5b17cSBrian Norris }
86327c5b17cSBrian Norris 
86427c5b17cSBrian Norris static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
86527c5b17cSBrian Norris {
86627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
86727c5b17cSBrian Norris 	unsigned int shift = 0, bits;
86827c5b17cSBrian Norris 	enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
86927c5b17cSBrian Norris 	int cs = host->cs;
87027c5b17cSBrian Norris 
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)
875decba6d4SFlorian Fainelli 		bits = 7;
876decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
87727c5b17cSBrian Norris 		bits = 6;
87827c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
87927c5b17cSBrian Norris 		bits = 5;
88027c5b17cSBrian Norris 	else
88127c5b17cSBrian Norris 		bits = 4;
88227c5b17cSBrian Norris 
883decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702) {
884decba6d4SFlorian Fainelli 		if (cs >= 4)
885decba6d4SFlorian Fainelli 			reg = BRCMNAND_CORR_THRESHOLD_EXT;
886decba6d4SFlorian Fainelli 		shift = (cs % 4) * bits;
887decba6d4SFlorian Fainelli 	} else if (ctrl->nand_version >= 0x0600) {
88827c5b17cSBrian Norris 		if (cs >= 5)
88927c5b17cSBrian Norris 			reg = BRCMNAND_CORR_THRESHOLD_EXT;
89027c5b17cSBrian Norris 		shift = (cs % 5) * bits;
89127c5b17cSBrian Norris 	}
89227c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
89327c5b17cSBrian Norris }
89427c5b17cSBrian Norris 
89527c5b17cSBrian Norris static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
89627c5b17cSBrian Norris {
897269ecf03SFlorian Fainelli 	if (ctrl->nand_version < 0x0602)
89827c5b17cSBrian Norris 		return 24;
89927c5b17cSBrian Norris 	return 0;
90027c5b17cSBrian Norris }
90127c5b17cSBrian Norris 
90227c5b17cSBrian Norris /***********************************************************************
90327c5b17cSBrian Norris  * NAND ACC CONTROL bitfield
90427c5b17cSBrian Norris  *
90527c5b17cSBrian Norris  * Some bits have remained constant throughout hardware revision, while
90627c5b17cSBrian Norris  * others have shifted around.
90727c5b17cSBrian Norris  ***********************************************************************/
90827c5b17cSBrian Norris 
90927c5b17cSBrian Norris /* Constant for all versions (where supported) */
91027c5b17cSBrian Norris enum {
91127c5b17cSBrian Norris 	/* See BRCMNAND_HAS_CACHE_MODE */
91227c5b17cSBrian Norris 	ACC_CONTROL_CACHE_MODE				= BIT(22),
91327c5b17cSBrian Norris 
91427c5b17cSBrian Norris 	/* See BRCMNAND_HAS_PREFETCH */
91527c5b17cSBrian Norris 	ACC_CONTROL_PREFETCH				= BIT(23),
91627c5b17cSBrian Norris 
91727c5b17cSBrian Norris 	ACC_CONTROL_PAGE_HIT				= BIT(24),
91827c5b17cSBrian Norris 	ACC_CONTROL_WR_PREEMPT				= BIT(25),
91927c5b17cSBrian Norris 	ACC_CONTROL_PARTIAL_PAGE			= BIT(26),
92027c5b17cSBrian Norris 	ACC_CONTROL_RD_ERASED				= BIT(27),
92127c5b17cSBrian Norris 	ACC_CONTROL_FAST_PGM_RDIN			= BIT(28),
92227c5b17cSBrian Norris 	ACC_CONTROL_WR_ECC				= BIT(30),
92327c5b17cSBrian Norris 	ACC_CONTROL_RD_ECC				= BIT(31),
92427c5b17cSBrian Norris };
92527c5b17cSBrian Norris 
92627c5b17cSBrian Norris static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
92727c5b17cSBrian Norris {
9280c06da57SKamal Dasu 	if (ctrl->nand_version == 0x0702)
929decba6d4SFlorian Fainelli 		return GENMASK(7, 0);
930decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
93127c5b17cSBrian Norris 		return GENMASK(6, 0);
9327e7c7df5SÁlvaro Fernández Rojas 	else if (ctrl->nand_version >= 0x0303)
93327c5b17cSBrian Norris 		return GENMASK(5, 0);
9347e7c7df5SÁlvaro Fernández Rojas 	else
9357e7c7df5SÁlvaro Fernández Rojas 		return GENMASK(4, 0);
93627c5b17cSBrian Norris }
93727c5b17cSBrian Norris 
93827c5b17cSBrian Norris #define NAND_ACC_CONTROL_ECC_SHIFT	16
939decba6d4SFlorian Fainelli #define NAND_ACC_CONTROL_ECC_EXT_SHIFT	13
94027c5b17cSBrian Norris 
94127c5b17cSBrian Norris static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
94227c5b17cSBrian Norris {
94327c5b17cSBrian Norris 	u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
94427c5b17cSBrian Norris 
945decba6d4SFlorian Fainelli 	mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
946decba6d4SFlorian Fainelli 
947decba6d4SFlorian Fainelli 	/* v7.2 includes additional ECC levels */
948decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
949decba6d4SFlorian Fainelli 		mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
950decba6d4SFlorian Fainelli 
951decba6d4SFlorian Fainelli 	return mask;
95227c5b17cSBrian Norris }
95327c5b17cSBrian Norris 
95427c5b17cSBrian Norris static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
95527c5b17cSBrian Norris {
95627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
95727c5b17cSBrian Norris 	u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
95827c5b17cSBrian Norris 	u32 acc_control = nand_readreg(ctrl, offs);
95927c5b17cSBrian Norris 	u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
96027c5b17cSBrian Norris 
96127c5b17cSBrian Norris 	if (en) {
96227c5b17cSBrian Norris 		acc_control |= ecc_flags; /* enable RD/WR ECC */
96327c5b17cSBrian Norris 		acc_control |= host->hwcfg.ecc_level
96427c5b17cSBrian Norris 			       << NAND_ACC_CONTROL_ECC_SHIFT;
96527c5b17cSBrian Norris 	} else {
96627c5b17cSBrian Norris 		acc_control &= ~ecc_flags; /* disable RD/WR ECC */
96727c5b17cSBrian Norris 		acc_control &= ~brcmnand_ecc_level_mask(ctrl);
96827c5b17cSBrian Norris 	}
96927c5b17cSBrian Norris 
97027c5b17cSBrian Norris 	nand_writereg(ctrl, offs, acc_control);
97127c5b17cSBrian Norris }
97227c5b17cSBrian Norris 
97327c5b17cSBrian Norris static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
97427c5b17cSBrian Norris {
975decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
976decba6d4SFlorian Fainelli 		return 9;
977decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
97827c5b17cSBrian Norris 		return 7;
97927c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
98027c5b17cSBrian Norris 		return 6;
98127c5b17cSBrian Norris 	else
98227c5b17cSBrian Norris 		return -1;
98327c5b17cSBrian Norris }
98427c5b17cSBrian Norris 
98527c5b17cSBrian Norris static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
98627c5b17cSBrian Norris {
98727c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
98827c5b17cSBrian Norris 	int shift = brcmnand_sector_1k_shift(ctrl);
98927c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
99027c5b17cSBrian Norris 						  BRCMNAND_CS_ACC_CONTROL);
99127c5b17cSBrian Norris 
99227c5b17cSBrian Norris 	if (shift < 0)
99327c5b17cSBrian Norris 		return 0;
99427c5b17cSBrian Norris 
99527c5b17cSBrian Norris 	return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
99627c5b17cSBrian Norris }
99727c5b17cSBrian Norris 
99827c5b17cSBrian Norris static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
99927c5b17cSBrian Norris {
100027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
100127c5b17cSBrian Norris 	int shift = brcmnand_sector_1k_shift(ctrl);
100227c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
100327c5b17cSBrian Norris 						  BRCMNAND_CS_ACC_CONTROL);
100427c5b17cSBrian Norris 	u32 tmp;
100527c5b17cSBrian Norris 
100627c5b17cSBrian Norris 	if (shift < 0)
100727c5b17cSBrian Norris 		return;
100827c5b17cSBrian Norris 
100927c5b17cSBrian Norris 	tmp = nand_readreg(ctrl, acc_control_offs);
101027c5b17cSBrian Norris 	tmp &= ~(1 << shift);
101127c5b17cSBrian Norris 	tmp |= (!!val) << shift;
101227c5b17cSBrian Norris 	nand_writereg(ctrl, acc_control_offs, tmp);
101327c5b17cSBrian Norris }
101427c5b17cSBrian Norris 
101527c5b17cSBrian Norris /***********************************************************************
101627c5b17cSBrian Norris  * CS_NAND_SELECT
101727c5b17cSBrian Norris  ***********************************************************************/
101827c5b17cSBrian Norris 
101927c5b17cSBrian Norris enum {
102027c5b17cSBrian Norris 	CS_SELECT_NAND_WP			= BIT(29),
102127c5b17cSBrian Norris 	CS_SELECT_AUTO_DEVICE_ID_CFG		= BIT(30),
102227c5b17cSBrian Norris };
102327c5b17cSBrian Norris 
10249d2ee0a6SKamal Dasu static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
10259d2ee0a6SKamal Dasu 				    u32 mask, u32 expected_val,
10269d2ee0a6SKamal Dasu 				    unsigned long timeout_ms)
10279d2ee0a6SKamal Dasu {
10289d2ee0a6SKamal Dasu 	unsigned long limit;
10299d2ee0a6SKamal Dasu 	u32 val;
10309d2ee0a6SKamal Dasu 
10319d2ee0a6SKamal Dasu 	if (!timeout_ms)
10329d2ee0a6SKamal Dasu 		timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
10339d2ee0a6SKamal Dasu 
10349d2ee0a6SKamal Dasu 	limit = jiffies + msecs_to_jiffies(timeout_ms);
10359d2ee0a6SKamal Dasu 	do {
10369d2ee0a6SKamal Dasu 		val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
10379d2ee0a6SKamal Dasu 		if ((val & mask) == expected_val)
10389d2ee0a6SKamal Dasu 			return 0;
10399d2ee0a6SKamal Dasu 
10409d2ee0a6SKamal Dasu 		cpu_relax();
10419d2ee0a6SKamal Dasu 	} while (time_after(limit, jiffies));
10429d2ee0a6SKamal Dasu 
10439d2ee0a6SKamal Dasu 	dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
10449d2ee0a6SKamal Dasu 		 expected_val, val & mask);
10459d2ee0a6SKamal Dasu 
10469d2ee0a6SKamal Dasu 	return -ETIMEDOUT;
10479d2ee0a6SKamal Dasu }
10489d2ee0a6SKamal Dasu 
104927c5b17cSBrian Norris static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
105027c5b17cSBrian Norris {
105127c5b17cSBrian Norris 	u32 val = en ? CS_SELECT_NAND_WP : 0;
105227c5b17cSBrian Norris 
105327c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
105427c5b17cSBrian Norris }
105527c5b17cSBrian Norris 
105627c5b17cSBrian Norris /***********************************************************************
105727c5b17cSBrian Norris  * Flash DMA
105827c5b17cSBrian Norris  ***********************************************************************/
105927c5b17cSBrian Norris 
106027c5b17cSBrian Norris static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
106127c5b17cSBrian Norris {
106227c5b17cSBrian Norris 	return ctrl->flash_dma_base;
106327c5b17cSBrian Norris }
106427c5b17cSBrian Norris 
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 
108927c5b17cSBrian Norris static inline bool flash_dma_buf_ok(const void *buf)
109027c5b17cSBrian Norris {
109127c5b17cSBrian Norris 	return buf && !is_vmalloc_addr(buf) &&
109227c5b17cSBrian Norris 		likely(IS_ALIGNED((uintptr_t)buf, 4));
109327c5b17cSBrian Norris }
109427c5b17cSBrian Norris 
10950c06da57SKamal Dasu static inline void flash_dma_writel(struct brcmnand_controller *ctrl,
10960c06da57SKamal Dasu 				    enum flash_dma_reg dma_reg, u32 val)
109727c5b17cSBrian Norris {
10980c06da57SKamal Dasu 	u16 offs = ctrl->flash_dma_offsets[dma_reg];
10990c06da57SKamal Dasu 
110027c5b17cSBrian Norris 	brcmnand_writel(val, ctrl->flash_dma_base + offs);
110127c5b17cSBrian Norris }
110227c5b17cSBrian Norris 
11030c06da57SKamal Dasu static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl,
11040c06da57SKamal Dasu 				  enum flash_dma_reg dma_reg)
110527c5b17cSBrian Norris {
11060c06da57SKamal Dasu 	u16 offs = ctrl->flash_dma_offsets[dma_reg];
11070c06da57SKamal Dasu 
110827c5b17cSBrian Norris 	return brcmnand_readl(ctrl->flash_dma_base + offs);
110927c5b17cSBrian Norris }
111027c5b17cSBrian Norris 
111127c5b17cSBrian Norris /* Low-level operation types: command, address, write, or read */
111227c5b17cSBrian Norris enum brcmnand_llop_type {
111327c5b17cSBrian Norris 	LL_OP_CMD,
111427c5b17cSBrian Norris 	LL_OP_ADDR,
111527c5b17cSBrian Norris 	LL_OP_WR,
111627c5b17cSBrian Norris 	LL_OP_RD,
111727c5b17cSBrian Norris };
111827c5b17cSBrian Norris 
111927c5b17cSBrian Norris /***********************************************************************
112027c5b17cSBrian Norris  * Internal support functions
112127c5b17cSBrian Norris  ***********************************************************************/
112227c5b17cSBrian Norris 
1123decba6d4SFlorian Fainelli static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
1124decba6d4SFlorian Fainelli 				  struct brcmnand_cfg *cfg)
112527c5b17cSBrian Norris {
1126decba6d4SFlorian Fainelli 	if (ctrl->nand_version <= 0x0701)
112727c5b17cSBrian Norris 		return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
112827c5b17cSBrian Norris 			cfg->ecc_level == 15;
1129decba6d4SFlorian Fainelli 	else
1130decba6d4SFlorian Fainelli 		return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
1131decba6d4SFlorian Fainelli 			cfg->ecc_level == 15) ||
1132decba6d4SFlorian Fainelli 			(cfg->spare_area_size == 28 && cfg->ecc_level == 16));
113327c5b17cSBrian Norris }
113427c5b17cSBrian Norris 
113527c5b17cSBrian Norris /*
1136ef5eeea6SBoris Brezillon  * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
1137ef5eeea6SBoris Brezillon  * the layout/configuration.
1138ef5eeea6SBoris Brezillon  * Returns -ERRCODE on failure.
113927c5b17cSBrian Norris  */
1140ef5eeea6SBoris Brezillon static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
1141ef5eeea6SBoris Brezillon 					  struct mtd_oob_region *oobregion)
114227c5b17cSBrian Norris {
1143ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
1144ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
114527c5b17cSBrian Norris 	struct brcmnand_cfg *cfg = &host->hwcfg;
1146ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
1147ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
114827c5b17cSBrian Norris 
1149ef5eeea6SBoris Brezillon 	if (section >= sectors)
1150ef5eeea6SBoris Brezillon 		return -ERANGE;
115127c5b17cSBrian Norris 
1152ef5eeea6SBoris Brezillon 	oobregion->offset = (section * sas) + 6;
1153ef5eeea6SBoris Brezillon 	oobregion->length = 3;
115427c5b17cSBrian Norris 
1155ef5eeea6SBoris Brezillon 	return 0;
1156ef5eeea6SBoris Brezillon }
1157ef5eeea6SBoris Brezillon 
1158ef5eeea6SBoris Brezillon static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
1159ef5eeea6SBoris Brezillon 					   struct mtd_oob_region *oobregion)
1160ef5eeea6SBoris Brezillon {
1161ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
1162ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
1163ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
1164ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
1165ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
1166d00358d7SÁlvaro Fernández Rojas 	u32 next;
1167ef5eeea6SBoris Brezillon 
1168d00358d7SÁlvaro Fernández Rojas 	if (section > sectors)
1169ef5eeea6SBoris Brezillon 		return -ERANGE;
1170ef5eeea6SBoris Brezillon 
1171d00358d7SÁlvaro Fernández Rojas 	next = (section * sas);
1172d00358d7SÁlvaro Fernández Rojas 	if (section < sectors)
1173d00358d7SÁlvaro Fernández Rojas 		next += 6;
1174ef5eeea6SBoris Brezillon 
1175d00358d7SÁlvaro Fernández Rojas 	if (section) {
1176d00358d7SÁlvaro Fernández Rojas 		oobregion->offset = ((section - 1) * sas) + 9;
117727c5b17cSBrian Norris 	} 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--;
118527c5b17cSBrian Norris 		}
118627c5b17cSBrian Norris 	}
118727c5b17cSBrian Norris 
1188d00358d7SÁlvaro Fernández Rojas 	oobregion->length = next - oobregion->offset;
1189f5b8aa78SBoris BREZILLON 
1190ef5eeea6SBoris Brezillon 	return 0;
1191ef5eeea6SBoris Brezillon }
1192ef5eeea6SBoris Brezillon 
1193ef5eeea6SBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
1194ef5eeea6SBoris Brezillon 	.ecc = brcmnand_hamming_ooblayout_ecc,
1195ef5eeea6SBoris Brezillon 	.free = brcmnand_hamming_ooblayout_free,
1196ef5eeea6SBoris Brezillon };
1197ef5eeea6SBoris Brezillon 
1198ef5eeea6SBoris Brezillon static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
1199ef5eeea6SBoris Brezillon 				      struct mtd_oob_region *oobregion)
1200ef5eeea6SBoris Brezillon {
1201ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
1202ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
1203ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
1204ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
1205ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
1206ef5eeea6SBoris Brezillon 
1207ef5eeea6SBoris Brezillon 	if (section >= sectors)
1208ef5eeea6SBoris Brezillon 		return -ERANGE;
1209ef5eeea6SBoris Brezillon 
1210917cc594SKamal Dasu 	oobregion->offset = ((section + 1) * sas) - chip->ecc.bytes;
1211ef5eeea6SBoris Brezillon 	oobregion->length = chip->ecc.bytes;
1212ef5eeea6SBoris Brezillon 
1213ef5eeea6SBoris Brezillon 	return 0;
1214ef5eeea6SBoris Brezillon }
1215ef5eeea6SBoris Brezillon 
1216ef5eeea6SBoris Brezillon static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
1217ef5eeea6SBoris Brezillon 					  struct mtd_oob_region *oobregion)
1218ef5eeea6SBoris Brezillon {
1219ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
1220ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
1221ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
1222ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
1223ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
1224ef5eeea6SBoris Brezillon 
1225ef5eeea6SBoris Brezillon 	if (section >= sectors)
1226ef5eeea6SBoris Brezillon 		return -ERANGE;
1227ef5eeea6SBoris Brezillon 
1228ef5eeea6SBoris Brezillon 	if (sas <= chip->ecc.bytes)
1229ef5eeea6SBoris Brezillon 		return 0;
1230ef5eeea6SBoris Brezillon 
1231ef5eeea6SBoris Brezillon 	oobregion->offset = section * sas;
1232ef5eeea6SBoris Brezillon 	oobregion->length = sas - chip->ecc.bytes;
1233ef5eeea6SBoris Brezillon 
1234ef5eeea6SBoris Brezillon 	if (!section) {
1235ef5eeea6SBoris Brezillon 		oobregion->offset++;
1236ef5eeea6SBoris Brezillon 		oobregion->length--;
1237ef5eeea6SBoris Brezillon 	}
1238ef5eeea6SBoris Brezillon 
1239ef5eeea6SBoris Brezillon 	return 0;
1240ef5eeea6SBoris Brezillon }
1241ef5eeea6SBoris Brezillon 
1242ef5eeea6SBoris Brezillon static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
1243ef5eeea6SBoris Brezillon 					  struct mtd_oob_region *oobregion)
1244ef5eeea6SBoris Brezillon {
1245ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
1246ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
1247ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
1248ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
1249ef5eeea6SBoris Brezillon 
1250ef5eeea6SBoris Brezillon 	if (section > 1 || sas - chip->ecc.bytes < 6 ||
1251ef5eeea6SBoris Brezillon 	    (section && sas - chip->ecc.bytes == 6))
1252ef5eeea6SBoris Brezillon 		return -ERANGE;
1253ef5eeea6SBoris Brezillon 
1254ef5eeea6SBoris Brezillon 	if (!section) {
1255ef5eeea6SBoris Brezillon 		oobregion->offset = 0;
1256ef5eeea6SBoris Brezillon 		oobregion->length = 5;
1257ef5eeea6SBoris Brezillon 	} else {
1258ef5eeea6SBoris Brezillon 		oobregion->offset = 6;
1259ef5eeea6SBoris Brezillon 		oobregion->length = sas - chip->ecc.bytes - 6;
1260ef5eeea6SBoris Brezillon 	}
1261ef5eeea6SBoris Brezillon 
1262ef5eeea6SBoris Brezillon 	return 0;
1263ef5eeea6SBoris Brezillon }
1264ef5eeea6SBoris Brezillon 
1265ef5eeea6SBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
1266ef5eeea6SBoris Brezillon 	.ecc = brcmnand_bch_ooblayout_ecc,
1267ef5eeea6SBoris Brezillon 	.free = brcmnand_bch_ooblayout_free_lp,
1268ef5eeea6SBoris Brezillon };
1269ef5eeea6SBoris Brezillon 
1270ef5eeea6SBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
1271ef5eeea6SBoris Brezillon 	.ecc = brcmnand_bch_ooblayout_ecc,
1272ef5eeea6SBoris Brezillon 	.free = brcmnand_bch_ooblayout_free_sp,
1273ef5eeea6SBoris Brezillon };
1274ef5eeea6SBoris Brezillon 
1275ef5eeea6SBoris Brezillon static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
1276ef5eeea6SBoris Brezillon {
1277ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *p = &host->hwcfg;
1278ef5eeea6SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
1279ef5eeea6SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &host->chip.ecc;
1280ef5eeea6SBoris Brezillon 	unsigned int ecc_level = p->ecc_level;
1281ef5eeea6SBoris Brezillon 	int sas = p->spare_area_size << p->sector_size_1k;
1282ef5eeea6SBoris Brezillon 	int sectors = p->page_size / (512 << p->sector_size_1k);
1283ef5eeea6SBoris Brezillon 
1284ef5eeea6SBoris Brezillon 	if (p->sector_size_1k)
1285ef5eeea6SBoris Brezillon 		ecc_level <<= 1;
1286ef5eeea6SBoris Brezillon 
1287decba6d4SFlorian Fainelli 	if (is_hamming_ecc(host->ctrl, p)) {
1288ef5eeea6SBoris Brezillon 		ecc->bytes = 3 * sectors;
1289ef5eeea6SBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
1290ef5eeea6SBoris Brezillon 		return 0;
129127c5b17cSBrian Norris 	}
129227c5b17cSBrian Norris 
129327c5b17cSBrian Norris 	/*
129427c5b17cSBrian Norris 	 * CONTROLLER_VERSION:
129527c5b17cSBrian Norris 	 *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
129627c5b17cSBrian Norris 	 *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
129727c5b17cSBrian Norris 	 * But we will just be conservative.
129827c5b17cSBrian Norris 	 */
1299ef5eeea6SBoris Brezillon 	ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
1300ef5eeea6SBoris Brezillon 	if (p->page_size == 512)
1301ef5eeea6SBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
1302ef5eeea6SBoris Brezillon 	else
1303ef5eeea6SBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
1304ef5eeea6SBoris Brezillon 
1305ef5eeea6SBoris Brezillon 	if (ecc->bytes >= sas) {
130627c5b17cSBrian Norris 		dev_err(&host->pdev->dev,
130727c5b17cSBrian Norris 			"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
1308ef5eeea6SBoris Brezillon 			ecc->bytes, sas);
1309ef5eeea6SBoris Brezillon 		return -EINVAL;
131027c5b17cSBrian Norris 	}
131127c5b17cSBrian Norris 
1312ef5eeea6SBoris Brezillon 	return 0;
131327c5b17cSBrian Norris }
131427c5b17cSBrian Norris 
131527c5b17cSBrian Norris static void brcmnand_wp(struct mtd_info *mtd, int wp)
131627c5b17cSBrian Norris {
13174bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
1318d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
131927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
132027c5b17cSBrian Norris 
132127c5b17cSBrian Norris 	if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
132227c5b17cSBrian Norris 		static int old_wp = -1;
13239d2ee0a6SKamal Dasu 		int ret;
132427c5b17cSBrian Norris 
132527c5b17cSBrian Norris 		if (old_wp != wp) {
132627c5b17cSBrian Norris 			dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
132727c5b17cSBrian Norris 			old_wp = wp;
132827c5b17cSBrian Norris 		}
13299d2ee0a6SKamal Dasu 
13309d2ee0a6SKamal Dasu 		/*
13319d2ee0a6SKamal Dasu 		 * make sure ctrl/flash ready before and after
13329d2ee0a6SKamal Dasu 		 * changing state of #WP pin
13339d2ee0a6SKamal Dasu 		 */
13349d2ee0a6SKamal Dasu 		ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
13359d2ee0a6SKamal Dasu 					       NAND_STATUS_READY,
13369d2ee0a6SKamal Dasu 					       NAND_CTRL_RDY |
13379d2ee0a6SKamal Dasu 					       NAND_STATUS_READY, 0);
13389d2ee0a6SKamal Dasu 		if (ret)
13399d2ee0a6SKamal Dasu 			return;
13409d2ee0a6SKamal Dasu 
134127c5b17cSBrian Norris 		brcmnand_set_wp(ctrl, wp);
134297d90da8SBoris Brezillon 		nand_status_op(chip, NULL);
13439d2ee0a6SKamal Dasu 		/* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
13449d2ee0a6SKamal Dasu 		ret = bcmnand_ctrl_poll_status(ctrl,
13459d2ee0a6SKamal Dasu 					       NAND_CTRL_RDY |
13469d2ee0a6SKamal Dasu 					       NAND_STATUS_READY |
13479d2ee0a6SKamal Dasu 					       NAND_STATUS_WP,
13489d2ee0a6SKamal Dasu 					       NAND_CTRL_RDY |
13499d2ee0a6SKamal Dasu 					       NAND_STATUS_READY |
13509d2ee0a6SKamal Dasu 					       (wp ? 0 : NAND_STATUS_WP), 0);
13519d2ee0a6SKamal Dasu 
13529d2ee0a6SKamal Dasu 		if (ret)
13539d2ee0a6SKamal Dasu 			dev_err_ratelimited(&host->pdev->dev,
13549d2ee0a6SKamal Dasu 					    "nand #WP expected %s\n",
13559d2ee0a6SKamal Dasu 					    wp ? "on" : "off");
135627c5b17cSBrian Norris 	}
135727c5b17cSBrian Norris }
135827c5b17cSBrian Norris 
135927c5b17cSBrian Norris /* Helper functions for reading and writing OOB registers */
136027c5b17cSBrian Norris static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
136127c5b17cSBrian Norris {
136227c5b17cSBrian Norris 	u16 offset0, offset10, reg_offs;
136327c5b17cSBrian Norris 
136427c5b17cSBrian Norris 	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
136527c5b17cSBrian Norris 	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
136627c5b17cSBrian Norris 
136727c5b17cSBrian Norris 	if (offs >= ctrl->max_oob)
136827c5b17cSBrian Norris 		return 0x77;
136927c5b17cSBrian Norris 
137027c5b17cSBrian Norris 	if (offs >= 16 && offset10)
137127c5b17cSBrian Norris 		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
137227c5b17cSBrian Norris 	else
137327c5b17cSBrian Norris 		reg_offs = offset0 + (offs & ~0x03);
137427c5b17cSBrian Norris 
137527c5b17cSBrian Norris 	return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
137627c5b17cSBrian Norris }
137727c5b17cSBrian Norris 
137827c5b17cSBrian Norris static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
137927c5b17cSBrian Norris 				 u32 data)
138027c5b17cSBrian Norris {
138127c5b17cSBrian Norris 	u16 offset0, offset10, reg_offs;
138227c5b17cSBrian Norris 
138327c5b17cSBrian Norris 	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
138427c5b17cSBrian Norris 	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
138527c5b17cSBrian Norris 
138627c5b17cSBrian Norris 	if (offs >= ctrl->max_oob)
138727c5b17cSBrian Norris 		return;
138827c5b17cSBrian Norris 
138927c5b17cSBrian Norris 	if (offs >= 16 && offset10)
139027c5b17cSBrian Norris 		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
139127c5b17cSBrian Norris 	else
139227c5b17cSBrian Norris 		reg_offs = offset0 + (offs & ~0x03);
139327c5b17cSBrian Norris 
139427c5b17cSBrian Norris 	nand_writereg(ctrl, reg_offs, data);
139527c5b17cSBrian Norris }
139627c5b17cSBrian Norris 
139727c5b17cSBrian Norris /*
139827c5b17cSBrian Norris  * read_oob_from_regs - read data from OOB registers
139927c5b17cSBrian Norris  * @ctrl: NAND controller
140027c5b17cSBrian Norris  * @i: sub-page sector index
140127c5b17cSBrian Norris  * @oob: buffer to read to
140227c5b17cSBrian Norris  * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
140327c5b17cSBrian Norris  * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
140427c5b17cSBrian Norris  */
140527c5b17cSBrian Norris static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
140627c5b17cSBrian Norris 			      int sas, int sector_1k)
140727c5b17cSBrian Norris {
140827c5b17cSBrian Norris 	int tbytes = sas << sector_1k;
140927c5b17cSBrian Norris 	int j;
141027c5b17cSBrian Norris 
141127c5b17cSBrian Norris 	/* Adjust OOB values for 1K sector size */
141227c5b17cSBrian Norris 	if (sector_1k && (i & 0x01))
141327c5b17cSBrian Norris 		tbytes = max(0, tbytes - (int)ctrl->max_oob);
141427c5b17cSBrian Norris 	tbytes = min_t(int, tbytes, ctrl->max_oob);
141527c5b17cSBrian Norris 
141627c5b17cSBrian Norris 	for (j = 0; j < tbytes; j++)
141727c5b17cSBrian Norris 		oob[j] = oob_reg_read(ctrl, j);
141827c5b17cSBrian Norris 	return tbytes;
141927c5b17cSBrian Norris }
142027c5b17cSBrian Norris 
142127c5b17cSBrian Norris /*
142227c5b17cSBrian Norris  * write_oob_to_regs - write data to OOB registers
142327c5b17cSBrian Norris  * @i: sub-page sector index
142427c5b17cSBrian Norris  * @oob: buffer to write from
142527c5b17cSBrian Norris  * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
142627c5b17cSBrian Norris  * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
142727c5b17cSBrian Norris  */
142827c5b17cSBrian Norris static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
142927c5b17cSBrian Norris 			     const u8 *oob, int sas, int sector_1k)
143027c5b17cSBrian Norris {
143127c5b17cSBrian Norris 	int tbytes = sas << sector_1k;
143227c5b17cSBrian Norris 	int j;
143327c5b17cSBrian Norris 
143427c5b17cSBrian Norris 	/* Adjust OOB values for 1K sector size */
143527c5b17cSBrian Norris 	if (sector_1k && (i & 0x01))
143627c5b17cSBrian Norris 		tbytes = max(0, tbytes - (int)ctrl->max_oob);
143727c5b17cSBrian Norris 	tbytes = min_t(int, tbytes, ctrl->max_oob);
143827c5b17cSBrian Norris 
143927c5b17cSBrian Norris 	for (j = 0; j < tbytes; j += 4)
144027c5b17cSBrian Norris 		oob_reg_write(ctrl, j,
144127c5b17cSBrian Norris 				(oob[j + 0] << 24) |
144227c5b17cSBrian Norris 				(oob[j + 1] << 16) |
144327c5b17cSBrian Norris 				(oob[j + 2] <<  8) |
144427c5b17cSBrian Norris 				(oob[j + 3] <<  0));
144527c5b17cSBrian Norris 	return tbytes;
144627c5b17cSBrian Norris }
144727c5b17cSBrian Norris 
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 
149427c5b17cSBrian Norris static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
149527c5b17cSBrian Norris {
149627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = data;
149727c5b17cSBrian Norris 
149827c5b17cSBrian Norris 	/* Discard all NAND_CTLRDY interrupts during DMA */
149927c5b17cSBrian Norris 	if (ctrl->dma_pending)
150027c5b17cSBrian Norris 		return IRQ_HANDLED;
150127c5b17cSBrian Norris 
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 
151227c5b17cSBrian Norris 	complete(&ctrl->done);
151327c5b17cSBrian Norris 	return IRQ_HANDLED;
151427c5b17cSBrian Norris }
151527c5b17cSBrian Norris 
1516c26211d3SBrian Norris /* Handle SoC-specific interrupt hardware */
1517c26211d3SBrian Norris static irqreturn_t brcmnand_irq(int irq, void *data)
1518c26211d3SBrian Norris {
1519c26211d3SBrian Norris 	struct brcmnand_controller *ctrl = data;
1520c26211d3SBrian Norris 
1521c26211d3SBrian Norris 	if (ctrl->soc->ctlrdy_ack(ctrl->soc))
1522c26211d3SBrian Norris 		return brcmnand_ctlrdy_irq(irq, data);
1523c26211d3SBrian Norris 
1524c26211d3SBrian Norris 	return IRQ_NONE;
1525c26211d3SBrian Norris }
1526c26211d3SBrian Norris 
152727c5b17cSBrian Norris static irqreturn_t brcmnand_dma_irq(int irq, void *data)
152827c5b17cSBrian Norris {
152927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = data;
153027c5b17cSBrian Norris 
153127c5b17cSBrian Norris 	complete(&ctrl->dma_done);
153227c5b17cSBrian Norris 
153327c5b17cSBrian Norris 	return IRQ_HANDLED;
153427c5b17cSBrian Norris }
153527c5b17cSBrian Norris 
153627c5b17cSBrian Norris static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
153727c5b17cSBrian Norris {
153827c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
15399d2ee0a6SKamal Dasu 	int ret;
15403c7c1e45SKamal Dasu 	u64 cmd_addr;
154127c5b17cSBrian Norris 
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 
154627c5b17cSBrian Norris 	BUG_ON(ctrl->cmd_pending != 0);
154727c5b17cSBrian Norris 	ctrl->cmd_pending = cmd;
154827c5b17cSBrian Norris 
15499d2ee0a6SKamal Dasu 	ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
15509d2ee0a6SKamal Dasu 	WARN_ON(ret);
155127c5b17cSBrian Norris 
155227c5b17cSBrian Norris 	mb(); /* flush previous writes */
155327c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
155427c5b17cSBrian Norris 			   cmd << brcmnand_cmd_shift(ctrl));
155527c5b17cSBrian Norris }
155627c5b17cSBrian Norris 
155727c5b17cSBrian Norris /***********************************************************************
155827c5b17cSBrian Norris  * NAND MTD API: read/program/erase
155927c5b17cSBrian Norris  ***********************************************************************/
156027c5b17cSBrian Norris 
15610f808c16SBoris Brezillon static void brcmnand_cmd_ctrl(struct nand_chip *chip, int dat,
156227c5b17cSBrian Norris 			      unsigned int ctrl)
156327c5b17cSBrian Norris {
156427c5b17cSBrian Norris 	/* intentionally left blank */
156527c5b17cSBrian Norris }
156627c5b17cSBrian Norris 
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)
159327c5b17cSBrian Norris {
1594d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
159527c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
1596c1ac2dc3SKamal Dasu 	bool err = false;
159727c5b17cSBrian Norris 
159827c5b17cSBrian Norris 	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) {
160327c5b17cSBrian Norris 		u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
160427c5b17cSBrian Norris 					>> brcmnand_cmd_shift(ctrl);
160527c5b17cSBrian Norris 
160627c5b17cSBrian Norris 		dev_err_ratelimited(ctrl->dev,
160727c5b17cSBrian Norris 			"timeout waiting for command %#02x\n", cmd);
160827c5b17cSBrian Norris 		dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
160927c5b17cSBrian Norris 			brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
161027c5b17cSBrian Norris 	}
161127c5b17cSBrian Norris 	ctrl->cmd_pending = 0;
161227c5b17cSBrian Norris 	return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
161327c5b17cSBrian Norris 				 INTFC_FLASH_STATUS;
161427c5b17cSBrian Norris }
161527c5b17cSBrian Norris 
161627c5b17cSBrian Norris enum {
161727c5b17cSBrian Norris 	LLOP_RE				= BIT(16),
161827c5b17cSBrian Norris 	LLOP_WE				= BIT(17),
161927c5b17cSBrian Norris 	LLOP_ALE			= BIT(18),
162027c5b17cSBrian Norris 	LLOP_CLE			= BIT(19),
162127c5b17cSBrian Norris 	LLOP_RETURN_IDLE		= BIT(31),
162227c5b17cSBrian Norris 
162327c5b17cSBrian Norris 	LLOP_DATA_MASK			= GENMASK(15, 0),
162427c5b17cSBrian Norris };
162527c5b17cSBrian Norris 
162627c5b17cSBrian Norris static int brcmnand_low_level_op(struct brcmnand_host *host,
162727c5b17cSBrian Norris 				 enum brcmnand_llop_type type, u32 data,
162827c5b17cSBrian Norris 				 bool last_op)
162927c5b17cSBrian Norris {
163027c5b17cSBrian Norris 	struct nand_chip *chip = &host->chip;
163127c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
163227c5b17cSBrian Norris 	u32 tmp;
163327c5b17cSBrian Norris 
163427c5b17cSBrian Norris 	tmp = data & LLOP_DATA_MASK;
163527c5b17cSBrian Norris 	switch (type) {
163627c5b17cSBrian Norris 	case LL_OP_CMD:
163727c5b17cSBrian Norris 		tmp |= LLOP_WE | LLOP_CLE;
163827c5b17cSBrian Norris 		break;
163927c5b17cSBrian Norris 	case LL_OP_ADDR:
164027c5b17cSBrian Norris 		/* WE | ALE */
164127c5b17cSBrian Norris 		tmp |= LLOP_WE | LLOP_ALE;
164227c5b17cSBrian Norris 		break;
164327c5b17cSBrian Norris 	case LL_OP_WR:
164427c5b17cSBrian Norris 		/* WE */
164527c5b17cSBrian Norris 		tmp |= LLOP_WE;
164627c5b17cSBrian Norris 		break;
164727c5b17cSBrian Norris 	case LL_OP_RD:
164827c5b17cSBrian Norris 		/* RE */
164927c5b17cSBrian Norris 		tmp |= LLOP_RE;
165027c5b17cSBrian Norris 		break;
165127c5b17cSBrian Norris 	}
165227c5b17cSBrian Norris 	if (last_op)
165327c5b17cSBrian Norris 		/* RETURN_IDLE */
165427c5b17cSBrian Norris 		tmp |= LLOP_RETURN_IDLE;
165527c5b17cSBrian Norris 
165627c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
165727c5b17cSBrian Norris 
165827c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
165927c5b17cSBrian Norris 	(void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
166027c5b17cSBrian Norris 
166127c5b17cSBrian Norris 	brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
1662f1d46942SBoris Brezillon 	return brcmnand_waitfunc(chip);
166327c5b17cSBrian Norris }
166427c5b17cSBrian Norris 
16655295cf2eSBoris Brezillon static void brcmnand_cmdfunc(struct nand_chip *chip, unsigned command,
166627c5b17cSBrian Norris 			     int column, int page_addr)
166727c5b17cSBrian Norris {
16685295cf2eSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1669d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
167027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
167127c5b17cSBrian Norris 	u64 addr = (u64)page_addr << chip->page_shift;
167227c5b17cSBrian Norris 	int native_cmd = 0;
167327c5b17cSBrian Norris 
167427c5b17cSBrian Norris 	if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
167527c5b17cSBrian Norris 			command == NAND_CMD_RNDOUT)
167627c5b17cSBrian Norris 		addr = (u64)column;
167727c5b17cSBrian Norris 	/* Avoid propagating a negative, don't-care address */
167827c5b17cSBrian Norris 	else if (page_addr < 0)
167927c5b17cSBrian Norris 		addr = 0;
168027c5b17cSBrian Norris 
168127c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
168227c5b17cSBrian Norris 		(unsigned long long)addr);
168327c5b17cSBrian Norris 
168427c5b17cSBrian Norris 	host->last_cmd = command;
168527c5b17cSBrian Norris 	host->last_byte = 0;
168627c5b17cSBrian Norris 	host->last_addr = addr;
168727c5b17cSBrian Norris 
168827c5b17cSBrian Norris 	switch (command) {
168927c5b17cSBrian Norris 	case NAND_CMD_RESET:
169027c5b17cSBrian Norris 		native_cmd = CMD_FLASH_RESET;
169127c5b17cSBrian Norris 		break;
169227c5b17cSBrian Norris 	case NAND_CMD_STATUS:
169327c5b17cSBrian Norris 		native_cmd = CMD_STATUS_READ;
169427c5b17cSBrian Norris 		break;
169527c5b17cSBrian Norris 	case NAND_CMD_READID:
169627c5b17cSBrian Norris 		native_cmd = CMD_DEVICE_ID_READ;
169727c5b17cSBrian Norris 		break;
169827c5b17cSBrian Norris 	case NAND_CMD_READOOB:
169927c5b17cSBrian Norris 		native_cmd = CMD_SPARE_AREA_READ;
170027c5b17cSBrian Norris 		break;
170127c5b17cSBrian Norris 	case NAND_CMD_ERASE1:
170227c5b17cSBrian Norris 		native_cmd = CMD_BLOCK_ERASE;
170327c5b17cSBrian Norris 		brcmnand_wp(mtd, 0);
170427c5b17cSBrian Norris 		break;
170527c5b17cSBrian Norris 	case NAND_CMD_PARAM:
170627c5b17cSBrian Norris 		native_cmd = CMD_PARAMETER_READ;
170727c5b17cSBrian Norris 		break;
170827c5b17cSBrian Norris 	case NAND_CMD_SET_FEATURES:
170927c5b17cSBrian Norris 	case NAND_CMD_GET_FEATURES:
171027c5b17cSBrian Norris 		brcmnand_low_level_op(host, LL_OP_CMD, command, false);
171127c5b17cSBrian Norris 		brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
171227c5b17cSBrian Norris 		break;
171327c5b17cSBrian Norris 	case NAND_CMD_RNDOUT:
171427c5b17cSBrian Norris 		native_cmd = CMD_PARAMETER_CHANGE_COL;
171527c5b17cSBrian Norris 		addr &= ~((u64)(FC_BYTES - 1));
171627c5b17cSBrian Norris 		/*
171727c5b17cSBrian Norris 		 * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
171827c5b17cSBrian Norris 		 * NB: hwcfg.sector_size_1k may not be initialized yet
171927c5b17cSBrian Norris 		 */
172027c5b17cSBrian Norris 		if (brcmnand_get_sector_size_1k(host)) {
172127c5b17cSBrian Norris 			host->hwcfg.sector_size_1k =
172227c5b17cSBrian Norris 				brcmnand_get_sector_size_1k(host);
172327c5b17cSBrian Norris 			brcmnand_set_sector_size_1k(host, 0);
172427c5b17cSBrian Norris 		}
172527c5b17cSBrian Norris 		break;
172627c5b17cSBrian Norris 	}
172727c5b17cSBrian Norris 
172827c5b17cSBrian Norris 	if (!native_cmd)
172927c5b17cSBrian Norris 		return;
173027c5b17cSBrian Norris 
17313c7c1e45SKamal Dasu 	brcmnand_set_cmd_addr(mtd, addr);
173227c5b17cSBrian Norris 	brcmnand_send_cmd(host, native_cmd);
1733f1d46942SBoris Brezillon 	brcmnand_waitfunc(chip);
173427c5b17cSBrian Norris 
173527c5b17cSBrian Norris 	if (native_cmd == CMD_PARAMETER_READ ||
173627c5b17cSBrian Norris 			native_cmd == CMD_PARAMETER_CHANGE_COL) {
1737d618baf9SBrian Norris 		/* Copy flash cache word-wise */
1738d618baf9SBrian Norris 		u32 *flash_cache = (u32 *)ctrl->flash_cache;
173927c5b17cSBrian Norris 		int i;
1740c26211d3SBrian Norris 
1741eab7fdc7SRay Jui 		brcmnand_soc_data_bus_prepare(ctrl->soc, true);
1742c26211d3SBrian Norris 
174327c5b17cSBrian Norris 		/*
174427c5b17cSBrian Norris 		 * Must cache the FLASH_CACHE now, since changes in
174527c5b17cSBrian Norris 		 * SECTOR_SIZE_1K may invalidate it
174627c5b17cSBrian Norris 		 */
174727c5b17cSBrian Norris 		for (i = 0; i < FC_WORDS; i++)
1748d618baf9SBrian Norris 			/*
1749d618baf9SBrian Norris 			 * Flash cache is big endian for parameter pages, at
1750d618baf9SBrian Norris 			 * least on STB SoCs
1751d618baf9SBrian Norris 			 */
1752d618baf9SBrian Norris 			flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
1753c26211d3SBrian Norris 
1754eab7fdc7SRay Jui 		brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
1755c26211d3SBrian Norris 
175627c5b17cSBrian Norris 		/* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
175727c5b17cSBrian Norris 		if (host->hwcfg.sector_size_1k)
175827c5b17cSBrian Norris 			brcmnand_set_sector_size_1k(host,
175927c5b17cSBrian Norris 						    host->hwcfg.sector_size_1k);
176027c5b17cSBrian Norris 	}
176127c5b17cSBrian Norris 
176227c5b17cSBrian Norris 	/* Re-enable protection is necessary only after erase */
176327c5b17cSBrian Norris 	if (command == NAND_CMD_ERASE1)
176427c5b17cSBrian Norris 		brcmnand_wp(mtd, 1);
176527c5b17cSBrian Norris }
176627c5b17cSBrian Norris 
17677e534323SBoris Brezillon static uint8_t brcmnand_read_byte(struct nand_chip *chip)
176827c5b17cSBrian Norris {
1769d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
177027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
177127c5b17cSBrian Norris 	uint8_t ret = 0;
177227c5b17cSBrian Norris 	int addr, offs;
177327c5b17cSBrian Norris 
177427c5b17cSBrian Norris 	switch (host->last_cmd) {
177527c5b17cSBrian Norris 	case NAND_CMD_READID:
177627c5b17cSBrian Norris 		if (host->last_byte < 4)
177727c5b17cSBrian Norris 			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
177827c5b17cSBrian Norris 				(24 - (host->last_byte << 3));
177927c5b17cSBrian Norris 		else if (host->last_byte < 8)
178027c5b17cSBrian Norris 			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
178127c5b17cSBrian Norris 				(56 - (host->last_byte << 3));
178227c5b17cSBrian Norris 		break;
178327c5b17cSBrian Norris 
178427c5b17cSBrian Norris 	case NAND_CMD_READOOB:
178527c5b17cSBrian Norris 		ret = oob_reg_read(ctrl, host->last_byte);
178627c5b17cSBrian Norris 		break;
178727c5b17cSBrian Norris 
178827c5b17cSBrian Norris 	case NAND_CMD_STATUS:
178927c5b17cSBrian Norris 		ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
179027c5b17cSBrian Norris 					INTFC_FLASH_STATUS;
179127c5b17cSBrian Norris 		if (wp_on) /* hide WP status */
179227c5b17cSBrian Norris 			ret |= NAND_STATUS_WP;
179327c5b17cSBrian Norris 		break;
179427c5b17cSBrian Norris 
179527c5b17cSBrian Norris 	case NAND_CMD_PARAM:
179627c5b17cSBrian Norris 	case NAND_CMD_RNDOUT:
179727c5b17cSBrian Norris 		addr = host->last_addr + host->last_byte;
179827c5b17cSBrian Norris 		offs = addr & (FC_BYTES - 1);
179927c5b17cSBrian Norris 
180027c5b17cSBrian Norris 		/* At FC_BYTES boundary, switch to next column */
180127c5b17cSBrian Norris 		if (host->last_byte > 0 && offs == 0)
180297d90da8SBoris Brezillon 			nand_change_read_column_op(chip, addr, NULL, 0, false);
180327c5b17cSBrian Norris 
1804d618baf9SBrian Norris 		ret = ctrl->flash_cache[offs];
180527c5b17cSBrian Norris 		break;
180627c5b17cSBrian Norris 	case NAND_CMD_GET_FEATURES:
180727c5b17cSBrian Norris 		if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
180827c5b17cSBrian Norris 			ret = 0;
180927c5b17cSBrian Norris 		} else {
181027c5b17cSBrian Norris 			bool last = host->last_byte ==
181127c5b17cSBrian Norris 				ONFI_SUBFEATURE_PARAM_LEN - 1;
181227c5b17cSBrian Norris 			brcmnand_low_level_op(host, LL_OP_RD, 0, last);
181327c5b17cSBrian Norris 			ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
181427c5b17cSBrian Norris 		}
181527c5b17cSBrian Norris 	}
181627c5b17cSBrian Norris 
181727c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
181827c5b17cSBrian Norris 	host->last_byte++;
181927c5b17cSBrian Norris 
182027c5b17cSBrian Norris 	return ret;
182127c5b17cSBrian Norris }
182227c5b17cSBrian Norris 
18237e534323SBoris Brezillon static void brcmnand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
182427c5b17cSBrian Norris {
182527c5b17cSBrian Norris 	int i;
182627c5b17cSBrian Norris 
182727c5b17cSBrian Norris 	for (i = 0; i < len; i++, buf++)
18287e534323SBoris Brezillon 		*buf = brcmnand_read_byte(chip);
182927c5b17cSBrian Norris }
183027c5b17cSBrian Norris 
1831c0739d85SBoris Brezillon static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf,
183227c5b17cSBrian Norris 			       int len)
183327c5b17cSBrian Norris {
183427c5b17cSBrian Norris 	int i;
1835d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
183627c5b17cSBrian Norris 
183727c5b17cSBrian Norris 	switch (host->last_cmd) {
183827c5b17cSBrian Norris 	case NAND_CMD_SET_FEATURES:
183927c5b17cSBrian Norris 		for (i = 0; i < len; i++)
184027c5b17cSBrian Norris 			brcmnand_low_level_op(host, LL_OP_WR, buf[i],
184127c5b17cSBrian Norris 						  (i + 1) == len);
184227c5b17cSBrian Norris 		break;
184327c5b17cSBrian Norris 	default:
184427c5b17cSBrian Norris 		BUG();
184527c5b17cSBrian Norris 		break;
184627c5b17cSBrian Norris 	}
184727c5b17cSBrian Norris }
184827c5b17cSBrian Norris 
184927c5b17cSBrian Norris /**
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 
1940a5d53ad2SKamal Dasu /**
194127c5b17cSBrian Norris  * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
194227c5b17cSBrian Norris  * following ahead of time:
194327c5b17cSBrian Norris  *  - Is this descriptor the beginning or end of a linked list?
194427c5b17cSBrian Norris  *  - What is the (DMA) address of the next descriptor in the linked list?
194527c5b17cSBrian Norris  */
194627c5b17cSBrian Norris static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
194727c5b17cSBrian Norris 				  struct brcm_nand_dma_desc *desc, u64 addr,
194827c5b17cSBrian Norris 				  dma_addr_t buf, u32 len, u8 dma_cmd,
194927c5b17cSBrian Norris 				  bool begin, bool end,
195027c5b17cSBrian Norris 				  dma_addr_t next_desc)
195127c5b17cSBrian Norris {
195227c5b17cSBrian Norris 	memset(desc, 0, sizeof(*desc));
195327c5b17cSBrian Norris 	/* Descriptors are written in native byte order (wordwise) */
195427c5b17cSBrian Norris 	desc->next_desc = lower_32_bits(next_desc);
195527c5b17cSBrian Norris 	desc->next_desc_ext = upper_32_bits(next_desc);
195627c5b17cSBrian Norris 	desc->cmd_irq = (dma_cmd << 24) |
195727c5b17cSBrian Norris 		(end ? (0x03 << 8) : 0) | /* IRQ | STOP */
195827c5b17cSBrian Norris 		(!!begin) | ((!!end) << 1); /* head, tail */
195927c5b17cSBrian Norris #ifdef CONFIG_CPU_BIG_ENDIAN
196027c5b17cSBrian Norris 	desc->cmd_irq |= 0x01 << 12;
196127c5b17cSBrian Norris #endif
196227c5b17cSBrian Norris 	desc->dram_addr = lower_32_bits(buf);
196327c5b17cSBrian Norris 	desc->dram_addr_ext = upper_32_bits(buf);
196427c5b17cSBrian Norris 	desc->tfr_len = len;
196527c5b17cSBrian Norris 	desc->total_len = len;
196627c5b17cSBrian Norris 	desc->flash_addr = lower_32_bits(addr);
196727c5b17cSBrian Norris 	desc->flash_addr_ext = upper_32_bits(addr);
196827c5b17cSBrian Norris 	desc->cs = host->cs;
196927c5b17cSBrian Norris 	desc->status_valid = 0x01;
197027c5b17cSBrian Norris 	return 0;
197127c5b17cSBrian Norris }
197227c5b17cSBrian Norris 
197327c5b17cSBrian Norris /**
197427c5b17cSBrian Norris  * Kick the FLASH_DMA engine, with a given DMA descriptor
197527c5b17cSBrian Norris  */
197627c5b17cSBrian Norris static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
197727c5b17cSBrian Norris {
197827c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
197927c5b17cSBrian Norris 	unsigned long timeo = msecs_to_jiffies(100);
198027c5b17cSBrian Norris 
198127c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
198227c5b17cSBrian Norris 	(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));
198627c5b17cSBrian Norris 		(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
198783156c1cSKamal Dasu 	}
198827c5b17cSBrian Norris 
198927c5b17cSBrian Norris 	/* Start FLASH_DMA engine */
199027c5b17cSBrian Norris 	ctrl->dma_pending = true;
199127c5b17cSBrian Norris 	mb(); /* flush previous writes */
199227c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
199327c5b17cSBrian Norris 
199427c5b17cSBrian Norris 	if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
199527c5b17cSBrian Norris 		dev_err(ctrl->dev,
199627c5b17cSBrian Norris 				"timeout waiting for DMA; status %#x, error status %#x\n",
199727c5b17cSBrian Norris 				flash_dma_readl(ctrl, FLASH_DMA_STATUS),
199827c5b17cSBrian Norris 				flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
199927c5b17cSBrian Norris 	}
200027c5b17cSBrian Norris 	ctrl->dma_pending = false;
200127c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
200227c5b17cSBrian Norris }
200327c5b17cSBrian Norris 
200427c5b17cSBrian Norris static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
200527c5b17cSBrian Norris 			      u32 len, u8 dma_cmd)
200627c5b17cSBrian Norris {
200727c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
200827c5b17cSBrian Norris 	dma_addr_t buf_pa;
200927c5b17cSBrian Norris 	int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
201027c5b17cSBrian Norris 
201127c5b17cSBrian Norris 	buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
201227c5b17cSBrian Norris 	if (dma_mapping_error(ctrl->dev, buf_pa)) {
201327c5b17cSBrian Norris 		dev_err(ctrl->dev, "unable to map buffer for DMA\n");
201427c5b17cSBrian Norris 		return -ENOMEM;
201527c5b17cSBrian Norris 	}
201627c5b17cSBrian Norris 
201727c5b17cSBrian Norris 	brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
201827c5b17cSBrian Norris 				   dma_cmd, true, true, 0);
201927c5b17cSBrian Norris 
202027c5b17cSBrian Norris 	brcmnand_dma_run(host, ctrl->dma_pa);
202127c5b17cSBrian Norris 
202227c5b17cSBrian Norris 	dma_unmap_single(ctrl->dev, buf_pa, len, dir);
202327c5b17cSBrian Norris 
202427c5b17cSBrian Norris 	if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
202527c5b17cSBrian Norris 		return -EBADMSG;
202627c5b17cSBrian Norris 	else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
202727c5b17cSBrian Norris 		return -EUCLEAN;
202827c5b17cSBrian Norris 
202927c5b17cSBrian Norris 	return 0;
203027c5b17cSBrian Norris }
203127c5b17cSBrian Norris 
203227c5b17cSBrian Norris /*
203327c5b17cSBrian Norris  * Assumes proper CS is already set
203427c5b17cSBrian Norris  */
203527c5b17cSBrian Norris static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
203627c5b17cSBrian Norris 				u64 addr, unsigned int trans, u32 *buf,
203727c5b17cSBrian Norris 				u8 *oob, u64 *err_addr)
203827c5b17cSBrian Norris {
2039d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
204027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
204127c5b17cSBrian Norris 	int i, j, ret = 0;
204227c5b17cSBrian Norris 
20433c7c1e45SKamal Dasu 	brcmnand_clear_ecc_addr(ctrl);
204427c5b17cSBrian Norris 
204527c5b17cSBrian Norris 	for (i = 0; i < trans; i++, addr += FC_BYTES) {
20463c7c1e45SKamal Dasu 		brcmnand_set_cmd_addr(mtd, addr);
204727c5b17cSBrian Norris 		/* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
204827c5b17cSBrian Norris 		brcmnand_send_cmd(host, CMD_PAGE_READ);
2049f1d46942SBoris Brezillon 		brcmnand_waitfunc(chip);
205027c5b17cSBrian Norris 
2051c26211d3SBrian Norris 		if (likely(buf)) {
2052eab7fdc7SRay Jui 			brcmnand_soc_data_bus_prepare(ctrl->soc, false);
2053c26211d3SBrian Norris 
205427c5b17cSBrian Norris 			for (j = 0; j < FC_WORDS; j++, buf++)
205527c5b17cSBrian Norris 				*buf = brcmnand_read_fc(ctrl, j);
205627c5b17cSBrian Norris 
2057eab7fdc7SRay Jui 			brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
2058c26211d3SBrian Norris 		}
2059c26211d3SBrian Norris 
206027c5b17cSBrian Norris 		if (oob)
206127c5b17cSBrian Norris 			oob += read_oob_from_regs(ctrl, i, oob,
206227c5b17cSBrian Norris 					mtd->oobsize / trans,
206327c5b17cSBrian Norris 					host->hwcfg.sector_size_1k);
206427c5b17cSBrian Norris 
206527c5b17cSBrian Norris 		if (!ret) {
20663c7c1e45SKamal Dasu 			*err_addr = brcmnand_get_uncorrecc_addr(ctrl);
20673c7c1e45SKamal Dasu 
206827c5b17cSBrian Norris 			if (*err_addr)
206927c5b17cSBrian Norris 				ret = -EBADMSG;
207027c5b17cSBrian Norris 		}
207127c5b17cSBrian Norris 
207227c5b17cSBrian Norris 		if (!ret) {
20733c7c1e45SKamal Dasu 			*err_addr = brcmnand_get_correcc_addr(ctrl);
20743c7c1e45SKamal Dasu 
207527c5b17cSBrian Norris 			if (*err_addr)
207627c5b17cSBrian Norris 				ret = -EUCLEAN;
207727c5b17cSBrian Norris 		}
207827c5b17cSBrian Norris 	}
207927c5b17cSBrian Norris 
208027c5b17cSBrian Norris 	return ret;
208127c5b17cSBrian Norris }
208227c5b17cSBrian Norris 
208302b88eeaSKamal Dasu /*
208402b88eeaSKamal Dasu  * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
208502b88eeaSKamal Dasu  * error
208602b88eeaSKamal Dasu  *
208702b88eeaSKamal Dasu  * Because the HW ECC signals an ECC error if an erase paged has even a single
208802b88eeaSKamal Dasu  * bitflip, we must check each ECC error to see if it is actually an erased
208902b88eeaSKamal Dasu  * page with bitflips, not a truly corrupted page.
209002b88eeaSKamal Dasu  *
209102b88eeaSKamal Dasu  * On a real error, return a negative error code (-EBADMSG for ECC error), and
209202b88eeaSKamal Dasu  * buf will contain raw data.
209302b88eeaSKamal Dasu  * Otherwise, buf gets filled with 0xffs and return the maximum number of
209402b88eeaSKamal Dasu  * bitflips-per-ECC-sector to the caller.
209502b88eeaSKamal Dasu  *
209602b88eeaSKamal Dasu  */
209702b88eeaSKamal Dasu static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
209802b88eeaSKamal Dasu 		  struct nand_chip *chip, void *buf, u64 addr)
209902b88eeaSKamal Dasu {
2100dcb351c0SÁlvaro Fernández Rojas 	struct mtd_oob_region ecc;
2101dcb351c0SÁlvaro Fernández Rojas 	int i;
210202b88eeaSKamal Dasu 	int bitflips = 0;
210302b88eeaSKamal Dasu 	int page = addr >> chip->page_shift;
210402b88eeaSKamal Dasu 	int ret;
2105dcb351c0SÁlvaro Fernández Rojas 	void *ecc_bytes;
21067f852cc1SClaire Lin 	void *ecc_chunk;
210702b88eeaSKamal Dasu 
2108eeab7174SBoris Brezillon 	if (!buf)
2109eeab7174SBoris Brezillon 		buf = nand_get_data_buf(chip);
211002b88eeaSKamal Dasu 
211102b88eeaSKamal Dasu 	/* read without ecc for verification */
2112b9761687SBoris Brezillon 	ret = chip->ecc.read_page_raw(chip, buf, true, page);
211302b88eeaSKamal Dasu 	if (ret)
211402b88eeaSKamal Dasu 		return ret;
211502b88eeaSKamal Dasu 
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,
212502b88eeaSKamal Dasu 						  chip->ecc.strength);
212602b88eeaSKamal Dasu 		if (ret < 0)
212702b88eeaSKamal Dasu 			return ret;
212802b88eeaSKamal Dasu 
212902b88eeaSKamal Dasu 		bitflips = max(bitflips, ret);
213002b88eeaSKamal Dasu 	}
213102b88eeaSKamal Dasu 
213202b88eeaSKamal Dasu 	return bitflips;
213302b88eeaSKamal Dasu }
213402b88eeaSKamal Dasu 
213527c5b17cSBrian Norris static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
213627c5b17cSBrian Norris 			 u64 addr, unsigned int trans, u32 *buf, u8 *oob)
213727c5b17cSBrian Norris {
2138d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
213927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
214027c5b17cSBrian Norris 	u64 err_addr = 0;
214127c5b17cSBrian Norris 	int err;
2142bc265323SKamal Dasu 	bool retry = true;
21434551e78aSKamal Dasu 	bool edu_err = false;
214427c5b17cSBrian Norris 
214527c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
214627c5b17cSBrian Norris 
2147bc265323SKamal Dasu try_dmaread:
21483c7c1e45SKamal Dasu 	brcmnand_clear_ecc_addr(ctrl);
214927c5b17cSBrian Norris 
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,
215327c5b17cSBrian Norris 				      CMD_PAGE_READ);
2154a5d53ad2SKamal Dasu 
215527c5b17cSBrian Norris 		if (err) {
215627c5b17cSBrian Norris 			if (mtd_is_bitflip_or_eccerr(err))
215727c5b17cSBrian Norris 				err_addr = addr;
215827c5b17cSBrian Norris 			else
215927c5b17cSBrian Norris 				return -EIO;
216027c5b17cSBrian Norris 		}
21614551e78aSKamal Dasu 
21624551e78aSKamal Dasu 		if (has_edu(ctrl) && err_addr)
21634551e78aSKamal Dasu 			edu_err = true;
21644551e78aSKamal Dasu 
216527c5b17cSBrian Norris 	} else {
216627c5b17cSBrian Norris 		if (oob)
216727c5b17cSBrian Norris 			memset(oob, 0x99, mtd->oobsize);
216827c5b17cSBrian Norris 
216927c5b17cSBrian Norris 		err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
217027c5b17cSBrian Norris 					       oob, &err_addr);
217127c5b17cSBrian Norris 	}
217227c5b17cSBrian Norris 
217327c5b17cSBrian Norris 	if (mtd_is_eccerr(err)) {
217402b88eeaSKamal Dasu 		/*
2175bc265323SKamal Dasu 		 * On controller version and 7.0, 7.1 , DMA read after a
2176bc265323SKamal Dasu 		 * prior PIO read that reported uncorrectable error,
2177bc265323SKamal Dasu 		 * the DMA engine captures this error following DMA read
2178bc265323SKamal Dasu 		 * cleared only on subsequent DMA read, so just retry once
2179bc265323SKamal Dasu 		 * to clear a possible false error reported for current DMA
2180bc265323SKamal Dasu 		 * read
2181bc265323SKamal Dasu 		 */
2182bc265323SKamal Dasu 		if ((ctrl->nand_version == 0x0700) ||
2183bc265323SKamal Dasu 		    (ctrl->nand_version == 0x0701)) {
2184bc265323SKamal Dasu 			if (retry) {
2185bc265323SKamal Dasu 				retry = false;
2186bc265323SKamal Dasu 				goto try_dmaread;
2187bc265323SKamal Dasu 			}
2188bc265323SKamal Dasu 		}
2189bc265323SKamal Dasu 
2190bc265323SKamal Dasu 		/*
219102b88eeaSKamal Dasu 		 * Controller version 7.2 has hw encoder to detect erased page
219202b88eeaSKamal Dasu 		 * bitflips, apply sw verification for older controllers only
219302b88eeaSKamal Dasu 		 */
219402b88eeaSKamal Dasu 		if (ctrl->nand_version < 0x0702) {
219502b88eeaSKamal Dasu 			err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
219602b88eeaSKamal Dasu 							      addr);
219702b88eeaSKamal Dasu 			/* erased page bitflips corrected */
2198e44b9a9cSAlbert Hsieh 			if (err >= 0)
219902b88eeaSKamal Dasu 				return err;
220002b88eeaSKamal Dasu 		}
220102b88eeaSKamal Dasu 
220227c5b17cSBrian Norris 		dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
220327c5b17cSBrian Norris 			(unsigned long long)err_addr);
220427c5b17cSBrian Norris 		mtd->ecc_stats.failed++;
220527c5b17cSBrian Norris 		/* NAND layer expects zero on ECC errors */
220627c5b17cSBrian Norris 		return 0;
220727c5b17cSBrian Norris 	}
220827c5b17cSBrian Norris 
220927c5b17cSBrian Norris 	if (mtd_is_bitflip(err)) {
221027c5b17cSBrian Norris 		unsigned int corrected = brcmnand_count_corrected(ctrl);
221127c5b17cSBrian Norris 
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 
221727c5b17cSBrian Norris 		dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
221827c5b17cSBrian Norris 			(unsigned long long)err_addr);
221927c5b17cSBrian Norris 		mtd->ecc_stats.corrected += corrected;
222027c5b17cSBrian Norris 		/* Always exceed the software-imposed threshold */
222127c5b17cSBrian Norris 		return max(mtd->bitflip_threshold, corrected);
222227c5b17cSBrian Norris 	}
222327c5b17cSBrian Norris 
222427c5b17cSBrian Norris 	return 0;
222527c5b17cSBrian Norris }
222627c5b17cSBrian Norris 
2227b9761687SBoris Brezillon static int brcmnand_read_page(struct nand_chip *chip, uint8_t *buf,
2228b9761687SBoris Brezillon 			      int oob_required, int page)
222927c5b17cSBrian Norris {
2230b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2231d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
223227c5b17cSBrian Norris 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
223327c5b17cSBrian Norris 
223425f815f6SBoris Brezillon 	nand_read_page_op(chip, page, 0, NULL, 0);
223525f815f6SBoris Brezillon 
223627c5b17cSBrian Norris 	return brcmnand_read(mtd, chip, host->last_addr,
223727c5b17cSBrian Norris 			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
223827c5b17cSBrian Norris }
223927c5b17cSBrian Norris 
2240b9761687SBoris Brezillon static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
2241b9761687SBoris Brezillon 				  int oob_required, int page)
224227c5b17cSBrian Norris {
2243d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
2244b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
224527c5b17cSBrian Norris 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
224627c5b17cSBrian Norris 	int ret;
224727c5b17cSBrian Norris 
224825f815f6SBoris Brezillon 	nand_read_page_op(chip, page, 0, NULL, 0);
224925f815f6SBoris Brezillon 
225027c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
225127c5b17cSBrian Norris 	ret = brcmnand_read(mtd, chip, host->last_addr,
225227c5b17cSBrian Norris 			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
225327c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
225427c5b17cSBrian Norris 	return ret;
225527c5b17cSBrian Norris }
225627c5b17cSBrian Norris 
2257b9761687SBoris Brezillon static int brcmnand_read_oob(struct nand_chip *chip, int page)
225827c5b17cSBrian Norris {
2259b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2260b9761687SBoris Brezillon 
226127c5b17cSBrian Norris 	return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
226227c5b17cSBrian Norris 			mtd->writesize >> FC_SHIFT,
226327c5b17cSBrian Norris 			NULL, (u8 *)chip->oob_poi);
226427c5b17cSBrian Norris }
226527c5b17cSBrian Norris 
2266b9761687SBoris Brezillon static int brcmnand_read_oob_raw(struct nand_chip *chip, int page)
226727c5b17cSBrian Norris {
2268b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2269d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
227027c5b17cSBrian Norris 
227127c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
227227c5b17cSBrian Norris 	brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
227327c5b17cSBrian Norris 		mtd->writesize >> FC_SHIFT,
227427c5b17cSBrian Norris 		NULL, (u8 *)chip->oob_poi);
227527c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
227627c5b17cSBrian Norris 	return 0;
227727c5b17cSBrian Norris }
227827c5b17cSBrian Norris 
227927c5b17cSBrian Norris static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
228027c5b17cSBrian Norris 			  u64 addr, const u32 *buf, u8 *oob)
228127c5b17cSBrian Norris {
2282d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
228327c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
228427c5b17cSBrian Norris 	unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
228527c5b17cSBrian Norris 	int status, ret = 0;
228627c5b17cSBrian Norris 
228727c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
228827c5b17cSBrian Norris 
22893f08b8baSAnup Patel 	if (unlikely((unsigned long)buf & 0x03)) {
229027c5b17cSBrian Norris 		dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
22913f08b8baSAnup Patel 		buf = (u32 *)((unsigned long)buf & ~0x03);
229227c5b17cSBrian Norris 	}
229327c5b17cSBrian Norris 
229427c5b17cSBrian Norris 	brcmnand_wp(mtd, 0);
229527c5b17cSBrian Norris 
229627c5b17cSBrian Norris 	for (i = 0; i < ctrl->max_oob; i += 4)
229727c5b17cSBrian Norris 		oob_reg_write(ctrl, i, 0xffffffff);
229827c5b17cSBrian Norris 
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 
230327c5b17cSBrian Norris 			ret = -EIO;
2304a5d53ad2SKamal Dasu 
230527c5b17cSBrian Norris 		goto out;
230627c5b17cSBrian Norris 	}
230727c5b17cSBrian Norris 
230827c5b17cSBrian Norris 	for (i = 0; i < trans; i++, addr += FC_BYTES) {
230927c5b17cSBrian Norris 		/* full address MUST be set before populating FC */
23103c7c1e45SKamal Dasu 		brcmnand_set_cmd_addr(mtd, addr);
231127c5b17cSBrian Norris 
2312c26211d3SBrian Norris 		if (buf) {
2313eab7fdc7SRay Jui 			brcmnand_soc_data_bus_prepare(ctrl->soc, false);
2314c26211d3SBrian Norris 
231527c5b17cSBrian Norris 			for (j = 0; j < FC_WORDS; j++, buf++)
231627c5b17cSBrian Norris 				brcmnand_write_fc(ctrl, j, *buf);
2317c26211d3SBrian Norris 
2318eab7fdc7SRay Jui 			brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
2319c26211d3SBrian Norris 		} else if (oob) {
232027c5b17cSBrian Norris 			for (j = 0; j < FC_WORDS; j++)
232127c5b17cSBrian Norris 				brcmnand_write_fc(ctrl, j, 0xffffffff);
2322c26211d3SBrian Norris 		}
232327c5b17cSBrian Norris 
232427c5b17cSBrian Norris 		if (oob) {
232527c5b17cSBrian Norris 			oob += write_oob_to_regs(ctrl, i, oob,
232627c5b17cSBrian Norris 					mtd->oobsize / trans,
232727c5b17cSBrian Norris 					host->hwcfg.sector_size_1k);
232827c5b17cSBrian Norris 		}
232927c5b17cSBrian Norris 
233027c5b17cSBrian Norris 		/* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
233127c5b17cSBrian Norris 		brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
2332f1d46942SBoris Brezillon 		status = brcmnand_waitfunc(chip);
233327c5b17cSBrian Norris 
233427c5b17cSBrian Norris 		if (status & NAND_STATUS_FAIL) {
233527c5b17cSBrian Norris 			dev_info(ctrl->dev, "program failed at %llx\n",
233627c5b17cSBrian Norris 				(unsigned long long)addr);
233727c5b17cSBrian Norris 			ret = -EIO;
233827c5b17cSBrian Norris 			goto out;
233927c5b17cSBrian Norris 		}
234027c5b17cSBrian Norris 	}
234127c5b17cSBrian Norris out:
234227c5b17cSBrian Norris 	brcmnand_wp(mtd, 1);
234327c5b17cSBrian Norris 	return ret;
234427c5b17cSBrian Norris }
234527c5b17cSBrian Norris 
2346767eb6fbSBoris Brezillon static int brcmnand_write_page(struct nand_chip *chip, const uint8_t *buf,
2347767eb6fbSBoris Brezillon 			       int oob_required, int page)
234827c5b17cSBrian Norris {
2349767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2350d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
235127c5b17cSBrian Norris 	void *oob = oob_required ? chip->oob_poi : NULL;
235227c5b17cSBrian Norris 
235325f815f6SBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
235427c5b17cSBrian Norris 	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
235525f815f6SBoris Brezillon 
235625f815f6SBoris Brezillon 	return nand_prog_page_end_op(chip);
235727c5b17cSBrian Norris }
235827c5b17cSBrian Norris 
2359767eb6fbSBoris Brezillon static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
236045aaeff9SBoris BREZILLON 				   int oob_required, int page)
236127c5b17cSBrian Norris {
2362767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2363d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
236427c5b17cSBrian Norris 	void *oob = oob_required ? chip->oob_poi : NULL;
236527c5b17cSBrian Norris 
236625f815f6SBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
236727c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
236827c5b17cSBrian Norris 	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
236927c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
237025f815f6SBoris Brezillon 
237125f815f6SBoris Brezillon 	return nand_prog_page_end_op(chip);
237227c5b17cSBrian Norris }
237327c5b17cSBrian Norris 
2374767eb6fbSBoris Brezillon static int brcmnand_write_oob(struct nand_chip *chip, int page)
237527c5b17cSBrian Norris {
2376767eb6fbSBoris Brezillon 	return brcmnand_write(nand_to_mtd(chip), chip,
2377767eb6fbSBoris Brezillon 			      (u64)page << chip->page_shift, NULL,
2378767eb6fbSBoris Brezillon 			      chip->oob_poi);
237927c5b17cSBrian Norris }
238027c5b17cSBrian Norris 
2381767eb6fbSBoris Brezillon static int brcmnand_write_oob_raw(struct nand_chip *chip, int page)
238227c5b17cSBrian Norris {
2383767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
2384d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
238527c5b17cSBrian Norris 	int ret;
238627c5b17cSBrian Norris 
238727c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
238827c5b17cSBrian Norris 	ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
238927c5b17cSBrian Norris 				 (u8 *)chip->oob_poi);
239027c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
239127c5b17cSBrian Norris 
239227c5b17cSBrian Norris 	return ret;
239327c5b17cSBrian Norris }
239427c5b17cSBrian Norris 
239527c5b17cSBrian Norris /***********************************************************************
239627c5b17cSBrian Norris  * Per-CS setup (1 NAND device)
239727c5b17cSBrian Norris  ***********************************************************************/
239827c5b17cSBrian Norris 
239927c5b17cSBrian Norris static int brcmnand_set_cfg(struct brcmnand_host *host,
240027c5b17cSBrian Norris 			    struct brcmnand_cfg *cfg)
240127c5b17cSBrian Norris {
240227c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
240327c5b17cSBrian Norris 	struct nand_chip *chip = &host->chip;
240427c5b17cSBrian Norris 	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
240527c5b17cSBrian Norris 	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
240627c5b17cSBrian Norris 			BRCMNAND_CS_CFG_EXT);
240727c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
240827c5b17cSBrian Norris 			BRCMNAND_CS_ACC_CONTROL);
240927c5b17cSBrian Norris 	u8 block_size = 0, page_size = 0, device_size = 0;
241027c5b17cSBrian Norris 	u32 tmp;
241127c5b17cSBrian Norris 
241227c5b17cSBrian Norris 	if (ctrl->block_sizes) {
241327c5b17cSBrian Norris 		int i, found;
241427c5b17cSBrian Norris 
241527c5b17cSBrian Norris 		for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
241627c5b17cSBrian Norris 			if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
241727c5b17cSBrian Norris 				block_size = i;
241827c5b17cSBrian Norris 				found = 1;
241927c5b17cSBrian Norris 			}
242027c5b17cSBrian Norris 		if (!found) {
242127c5b17cSBrian Norris 			dev_warn(ctrl->dev, "invalid block size %u\n",
242227c5b17cSBrian Norris 					cfg->block_size);
242327c5b17cSBrian Norris 			return -EINVAL;
242427c5b17cSBrian Norris 		}
242527c5b17cSBrian Norris 	} else {
242627c5b17cSBrian Norris 		block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
242727c5b17cSBrian Norris 	}
242827c5b17cSBrian Norris 
242927c5b17cSBrian Norris 	if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
243027c5b17cSBrian Norris 				cfg->block_size > ctrl->max_block_size)) {
243127c5b17cSBrian Norris 		dev_warn(ctrl->dev, "invalid block size %u\n",
243227c5b17cSBrian Norris 				cfg->block_size);
243327c5b17cSBrian Norris 		block_size = 0;
243427c5b17cSBrian Norris 	}
243527c5b17cSBrian Norris 
243627c5b17cSBrian Norris 	if (ctrl->page_sizes) {
243727c5b17cSBrian Norris 		int i, found;
243827c5b17cSBrian Norris 
243927c5b17cSBrian Norris 		for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
244027c5b17cSBrian Norris 			if (ctrl->page_sizes[i] == cfg->page_size) {
244127c5b17cSBrian Norris 				page_size = i;
244227c5b17cSBrian Norris 				found = 1;
244327c5b17cSBrian Norris 			}
244427c5b17cSBrian Norris 		if (!found) {
244527c5b17cSBrian Norris 			dev_warn(ctrl->dev, "invalid page size %u\n",
244627c5b17cSBrian Norris 					cfg->page_size);
244727c5b17cSBrian Norris 			return -EINVAL;
244827c5b17cSBrian Norris 		}
244927c5b17cSBrian Norris 	} else {
245027c5b17cSBrian Norris 		page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
245127c5b17cSBrian Norris 	}
245227c5b17cSBrian Norris 
245327c5b17cSBrian Norris 	if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
245427c5b17cSBrian Norris 				cfg->page_size > ctrl->max_page_size)) {
245527c5b17cSBrian Norris 		dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
245627c5b17cSBrian Norris 		return -EINVAL;
245727c5b17cSBrian Norris 	}
245827c5b17cSBrian Norris 
245927c5b17cSBrian Norris 	if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
246027c5b17cSBrian Norris 		dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
246127c5b17cSBrian Norris 			(unsigned long long)cfg->device_size);
246227c5b17cSBrian Norris 		return -EINVAL;
246327c5b17cSBrian Norris 	}
246427c5b17cSBrian Norris 	device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
246527c5b17cSBrian Norris 
24663f06d2a9SBrian Norris 	tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) |
24673f06d2a9SBrian Norris 		(cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) |
24683f06d2a9SBrian Norris 		(cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) |
24693f06d2a9SBrian Norris 		(!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) |
24703f06d2a9SBrian Norris 		(device_size << CFG_DEVICE_SIZE_SHIFT);
247127c5b17cSBrian Norris 	if (cfg_offs == cfg_ext_offs) {
24727e7c7df5SÁlvaro Fernández Rojas 		tmp |= (page_size << ctrl->page_size_shift) |
24733f06d2a9SBrian Norris 		       (block_size << CFG_BLK_SIZE_SHIFT);
247427c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_offs, tmp);
247527c5b17cSBrian Norris 	} else {
247627c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_offs, tmp);
24773f06d2a9SBrian Norris 		tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) |
24783f06d2a9SBrian Norris 		      (block_size << CFG_EXT_BLK_SIZE_SHIFT);
247927c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_ext_offs, tmp);
248027c5b17cSBrian Norris 	}
248127c5b17cSBrian Norris 
248227c5b17cSBrian Norris 	tmp = nand_readreg(ctrl, acc_control_offs);
248327c5b17cSBrian Norris 	tmp &= ~brcmnand_ecc_level_mask(ctrl);
248427c5b17cSBrian Norris 	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;
248727c5b17cSBrian Norris 		tmp |= cfg->spare_area_size;
24887e7c7df5SÁlvaro Fernández Rojas 	}
248927c5b17cSBrian Norris 	nand_writereg(ctrl, acc_control_offs, tmp);
249027c5b17cSBrian Norris 
249127c5b17cSBrian Norris 	brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
249227c5b17cSBrian Norris 
249327c5b17cSBrian Norris 	/* threshold = ceil(BCH-level * 0.75) */
249427c5b17cSBrian Norris 	brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
249527c5b17cSBrian Norris 
249627c5b17cSBrian Norris 	return 0;
249727c5b17cSBrian Norris }
249827c5b17cSBrian Norris 
2499decba6d4SFlorian Fainelli static void brcmnand_print_cfg(struct brcmnand_host *host,
2500decba6d4SFlorian Fainelli 			       char *buf, struct brcmnand_cfg *cfg)
250127c5b17cSBrian Norris {
250227c5b17cSBrian Norris 	buf += sprintf(buf,
250327c5b17cSBrian Norris 		"%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
250427c5b17cSBrian Norris 		(unsigned long long)cfg->device_size >> 20,
250527c5b17cSBrian Norris 		cfg->block_size >> 10,
250627c5b17cSBrian Norris 		cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
250727c5b17cSBrian Norris 		cfg->page_size >= 1024 ? "KiB" : "B",
250827c5b17cSBrian Norris 		cfg->spare_area_size, cfg->device_width);
250927c5b17cSBrian Norris 
251027c5b17cSBrian Norris 	/* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
2511decba6d4SFlorian Fainelli 	if (is_hamming_ecc(host->ctrl, cfg))
251227c5b17cSBrian Norris 		sprintf(buf, ", Hamming ECC");
251327c5b17cSBrian Norris 	else if (cfg->sector_size_1k)
251427c5b17cSBrian Norris 		sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
251527c5b17cSBrian Norris 	else
251680204124SHauke Mehrtens 		sprintf(buf, ", BCH-%u", cfg->ecc_level);
251727c5b17cSBrian Norris }
251827c5b17cSBrian Norris 
251927c5b17cSBrian Norris /*
252027c5b17cSBrian Norris  * Minimum number of bytes to address a page. Calculated as:
252127c5b17cSBrian Norris  *     roundup(log2(size / page-size) / 8)
252227c5b17cSBrian Norris  *
252327c5b17cSBrian Norris  * NB: the following does not "round up" for non-power-of-2 'size'; but this is
252427c5b17cSBrian Norris  *     OK because many other things will break if 'size' is irregular...
252527c5b17cSBrian Norris  */
252627c5b17cSBrian Norris static inline int get_blk_adr_bytes(u64 size, u32 writesize)
252727c5b17cSBrian Norris {
252827c5b17cSBrian Norris 	return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
252927c5b17cSBrian Norris }
253027c5b17cSBrian Norris 
253127c5b17cSBrian Norris static int brcmnand_setup_dev(struct brcmnand_host *host)
253227c5b17cSBrian Norris {
2533f1c4c999SBoris BREZILLON 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
253427c5b17cSBrian Norris 	struct nand_chip *chip = &host->chip;
253527c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
253627c5b17cSBrian Norris 	struct brcmnand_cfg *cfg = &host->hwcfg;
253727c5b17cSBrian Norris 	char msg[128];
253827c5b17cSBrian Norris 	u32 offs, tmp, oob_sector;
253927c5b17cSBrian Norris 	int ret;
254027c5b17cSBrian Norris 
254127c5b17cSBrian Norris 	memset(cfg, 0, sizeof(*cfg));
254227c5b17cSBrian Norris 
254344ec23c9SBoris BREZILLON 	ret = of_property_read_u32(nand_get_flash_node(chip),
254461528d88SMarek Vasut 				   "brcm,nand-oob-sector-size",
254527c5b17cSBrian Norris 				   &oob_sector);
254627c5b17cSBrian Norris 	if (ret) {
254727c5b17cSBrian Norris 		/* Use detected size */
254827c5b17cSBrian Norris 		cfg->spare_area_size = mtd->oobsize /
254927c5b17cSBrian Norris 					(mtd->writesize >> FC_SHIFT);
255027c5b17cSBrian Norris 	} else {
255127c5b17cSBrian Norris 		cfg->spare_area_size = oob_sector;
255227c5b17cSBrian Norris 	}
255327c5b17cSBrian Norris 	if (cfg->spare_area_size > ctrl->max_oob)
255427c5b17cSBrian Norris 		cfg->spare_area_size = ctrl->max_oob;
255527c5b17cSBrian Norris 	/*
255627c5b17cSBrian Norris 	 * Set oobsize to be consistent with controller's spare_area_size, as
255727c5b17cSBrian Norris 	 * the rest is inaccessible.
255827c5b17cSBrian Norris 	 */
255927c5b17cSBrian Norris 	mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
256027c5b17cSBrian Norris 
256127c5b17cSBrian Norris 	cfg->device_size = mtd->size;
256227c5b17cSBrian Norris 	cfg->block_size = mtd->erasesize;
256327c5b17cSBrian Norris 	cfg->page_size = mtd->writesize;
256427c5b17cSBrian Norris 	cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
256527c5b17cSBrian Norris 	cfg->col_adr_bytes = 2;
256627c5b17cSBrian Norris 	cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
256727c5b17cSBrian Norris 
2568bace41f8SMiquel Raynal 	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) {
2569666b6568SBrian Norris 		dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
2570bace41f8SMiquel Raynal 			chip->ecc.engine_type);
2571666b6568SBrian Norris 		return -EINVAL;
2572666b6568SBrian Norris 	}
2573666b6568SBrian Norris 
2574e0a564aeSMiquel Raynal 	if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) {
2575666b6568SBrian Norris 		if (chip->ecc.strength == 1 && chip->ecc.size == 512)
2576666b6568SBrian Norris 			/* Default to Hamming for 1-bit ECC, if unspecified */
2577e0a564aeSMiquel Raynal 			chip->ecc.algo = NAND_ECC_ALGO_HAMMING;
2578666b6568SBrian Norris 		else
2579666b6568SBrian Norris 			/* Otherwise, BCH */
2580e0a564aeSMiquel Raynal 			chip->ecc.algo = NAND_ECC_ALGO_BCH;
2581666b6568SBrian Norris 	}
2582666b6568SBrian Norris 
2583e0a564aeSMiquel Raynal 	if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING &&
2584e0a564aeSMiquel Raynal 	    (chip->ecc.strength != 1 || chip->ecc.size != 512)) {
2585666b6568SBrian Norris 		dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
2586666b6568SBrian Norris 			chip->ecc.strength, chip->ecc.size);
2587666b6568SBrian Norris 		return -EINVAL;
2588666b6568SBrian Norris 	}
2589666b6568SBrian Norris 
2590bace41f8SMiquel Raynal 	if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_NONE &&
259178933218SKamal Dasu 	    (!chip->ecc.size || !chip->ecc.strength)) {
259278933218SKamal Dasu 		if (chip->base.eccreq.step_size && chip->base.eccreq.strength) {
259378933218SKamal Dasu 			/* use detected ECC parameters */
259478933218SKamal Dasu 			chip->ecc.size = chip->base.eccreq.step_size;
259578933218SKamal Dasu 			chip->ecc.strength = chip->base.eccreq.strength;
259678933218SKamal Dasu 			dev_info(ctrl->dev, "Using ECC step-size %d, strength %d\n",
259778933218SKamal Dasu 				chip->ecc.size, chip->ecc.strength);
259878933218SKamal Dasu 		}
259978933218SKamal Dasu 	}
260078933218SKamal Dasu 
260127c5b17cSBrian Norris 	switch (chip->ecc.size) {
260227c5b17cSBrian Norris 	case 512:
2603e0a564aeSMiquel Raynal 		if (chip->ecc.algo == NAND_ECC_ALGO_HAMMING)
260427c5b17cSBrian Norris 			cfg->ecc_level = 15;
260527c5b17cSBrian Norris 		else
260627c5b17cSBrian Norris 			cfg->ecc_level = chip->ecc.strength;
260727c5b17cSBrian Norris 		cfg->sector_size_1k = 0;
260827c5b17cSBrian Norris 		break;
260927c5b17cSBrian Norris 	case 1024:
261027c5b17cSBrian Norris 		if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
261127c5b17cSBrian Norris 			dev_err(ctrl->dev, "1KB sectors not supported\n");
261227c5b17cSBrian Norris 			return -EINVAL;
261327c5b17cSBrian Norris 		}
261427c5b17cSBrian Norris 		if (chip->ecc.strength & 0x1) {
261527c5b17cSBrian Norris 			dev_err(ctrl->dev,
261627c5b17cSBrian Norris 				"odd ECC not supported with 1KB sectors\n");
261727c5b17cSBrian Norris 			return -EINVAL;
261827c5b17cSBrian Norris 		}
261927c5b17cSBrian Norris 
262027c5b17cSBrian Norris 		cfg->ecc_level = chip->ecc.strength >> 1;
262127c5b17cSBrian Norris 		cfg->sector_size_1k = 1;
262227c5b17cSBrian Norris 		break;
262327c5b17cSBrian Norris 	default:
262427c5b17cSBrian Norris 		dev_err(ctrl->dev, "unsupported ECC size: %d\n",
262527c5b17cSBrian Norris 			chip->ecc.size);
262627c5b17cSBrian Norris 		return -EINVAL;
262727c5b17cSBrian Norris 	}
262827c5b17cSBrian Norris 
262927c5b17cSBrian Norris 	cfg->ful_adr_bytes = cfg->blk_adr_bytes;
263027c5b17cSBrian Norris 	if (mtd->writesize > 512)
263127c5b17cSBrian Norris 		cfg->ful_adr_bytes += cfg->col_adr_bytes;
263227c5b17cSBrian Norris 	else
263327c5b17cSBrian Norris 		cfg->ful_adr_bytes += 1;
263427c5b17cSBrian Norris 
263527c5b17cSBrian Norris 	ret = brcmnand_set_cfg(host, cfg);
263627c5b17cSBrian Norris 	if (ret)
263727c5b17cSBrian Norris 		return ret;
263827c5b17cSBrian Norris 
263927c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
264027c5b17cSBrian Norris 
2641decba6d4SFlorian Fainelli 	brcmnand_print_cfg(host, msg, cfg);
264227c5b17cSBrian Norris 	dev_info(ctrl->dev, "detected %s\n", msg);
264327c5b17cSBrian Norris 
264427c5b17cSBrian Norris 	/* Configure ACC_CONTROL */
264527c5b17cSBrian Norris 	offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
264627c5b17cSBrian Norris 	tmp = nand_readreg(ctrl, offs);
264727c5b17cSBrian Norris 	tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
264827c5b17cSBrian Norris 	tmp &= ~ACC_CONTROL_RD_ERASED;
2649decba6d4SFlorian Fainelli 
2650decba6d4SFlorian Fainelli 	/* We need to turn on Read from erased paged protected by ECC */
2651decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
2652decba6d4SFlorian Fainelli 		tmp |= ACC_CONTROL_RD_ERASED;
265327c5b17cSBrian Norris 	tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
2654f953f0f8SKamal Dasu 	if (ctrl->features & BRCMNAND_HAS_PREFETCH)
265527c5b17cSBrian Norris 		tmp &= ~ACC_CONTROL_PREFETCH;
2656f953f0f8SKamal Dasu 
265727c5b17cSBrian Norris 	nand_writereg(ctrl, offs, tmp);
265827c5b17cSBrian Norris 
265927c5b17cSBrian Norris 	return 0;
266027c5b17cSBrian Norris }
266127c5b17cSBrian Norris 
26624918b905SMiquel Raynal static int brcmnand_attach_chip(struct nand_chip *chip)
26634918b905SMiquel Raynal {
26644918b905SMiquel Raynal 	struct mtd_info *mtd = nand_to_mtd(chip);
26654918b905SMiquel Raynal 	struct brcmnand_host *host = nand_get_controller_data(chip);
26664918b905SMiquel Raynal 	int ret;
26674918b905SMiquel Raynal 
26684918b905SMiquel Raynal 	chip->options |= NAND_NO_SUBPAGE_WRITE;
26694918b905SMiquel Raynal 	/*
26704918b905SMiquel Raynal 	 * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
26714918b905SMiquel Raynal 	 * to/from, and have nand_base pass us a bounce buffer instead, as
26724918b905SMiquel Raynal 	 * needed.
26734918b905SMiquel Raynal 	 */
2674ce8148d7SMiquel Raynal 	chip->options |= NAND_USES_DMA;
26754918b905SMiquel Raynal 
26764918b905SMiquel Raynal 	if (chip->bbt_options & NAND_BBT_USE_FLASH)
26774918b905SMiquel Raynal 		chip->bbt_options |= NAND_BBT_NO_OOB;
26784918b905SMiquel Raynal 
26794918b905SMiquel Raynal 	if (brcmnand_setup_dev(host))
26804918b905SMiquel Raynal 		return -ENXIO;
26814918b905SMiquel Raynal 
26824918b905SMiquel Raynal 	chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
26834918b905SMiquel Raynal 
26844918b905SMiquel Raynal 	/* only use our internal HW threshold */
26854918b905SMiquel Raynal 	mtd->bitflip_threshold = 1;
26864918b905SMiquel Raynal 
26874918b905SMiquel Raynal 	ret = brcmstb_choose_ecc_layout(host);
26884918b905SMiquel Raynal 
26894918b905SMiquel Raynal 	return ret;
26904918b905SMiquel Raynal }
26914918b905SMiquel Raynal 
26924918b905SMiquel Raynal static const struct nand_controller_ops brcmnand_controller_ops = {
26934918b905SMiquel Raynal 	.attach_chip = brcmnand_attach_chip,
26944918b905SMiquel Raynal };
26954918b905SMiquel Raynal 
2696d121b66dSBrian Norris static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
269727c5b17cSBrian Norris {
269827c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
269927c5b17cSBrian Norris 	struct platform_device *pdev = host->pdev;
270027c5b17cSBrian Norris 	struct mtd_info *mtd;
270127c5b17cSBrian Norris 	struct nand_chip *chip;
27025e65d48bSBrian Norris 	int ret;
27034d1ea982SAnup Patel 	u16 cfg_offs;
270427c5b17cSBrian Norris 
270527c5b17cSBrian Norris 	ret = of_property_read_u32(dn, "reg", &host->cs);
270627c5b17cSBrian Norris 	if (ret) {
270727c5b17cSBrian Norris 		dev_err(&pdev->dev, "can't get chip-select\n");
270827c5b17cSBrian Norris 		return -ENXIO;
270927c5b17cSBrian Norris 	}
271027c5b17cSBrian Norris 
2711f1c4c999SBoris BREZILLON 	mtd = nand_to_mtd(&host->chip);
271227c5b17cSBrian Norris 	chip = &host->chip;
271327c5b17cSBrian Norris 
271463752199SBrian Norris 	nand_set_flash_node(chip, dn);
2715d699ed25SBoris BREZILLON 	nand_set_controller_data(chip, host);
271627c5b17cSBrian Norris 	mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
271727c5b17cSBrian Norris 				   host->cs);
2718039b4377SFabio Estevam 	if (!mtd->name)
2719039b4377SFabio Estevam 		return -ENOMEM;
2720039b4377SFabio Estevam 
272127c5b17cSBrian Norris 	mtd->owner = THIS_MODULE;
272227c5b17cSBrian Norris 	mtd->dev.parent = &pdev->dev;
272327c5b17cSBrian Norris 
2724bf6065c6SBoris Brezillon 	chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
2725bf6065c6SBoris Brezillon 	chip->legacy.cmdfunc = brcmnand_cmdfunc;
27268395b753SBoris Brezillon 	chip->legacy.waitfunc = brcmnand_waitfunc;
2727716bbbabSBoris Brezillon 	chip->legacy.read_byte = brcmnand_read_byte;
2728716bbbabSBoris Brezillon 	chip->legacy.read_buf = brcmnand_read_buf;
2729716bbbabSBoris Brezillon 	chip->legacy.write_buf = brcmnand_write_buf;
273027c5b17cSBrian Norris 
2731bace41f8SMiquel Raynal 	chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
273227c5b17cSBrian Norris 	chip->ecc.read_page = brcmnand_read_page;
273327c5b17cSBrian Norris 	chip->ecc.write_page = brcmnand_write_page;
273427c5b17cSBrian Norris 	chip->ecc.read_page_raw = brcmnand_read_page_raw;
273527c5b17cSBrian Norris 	chip->ecc.write_page_raw = brcmnand_write_page_raw;
273627c5b17cSBrian Norris 	chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
273727c5b17cSBrian Norris 	chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
273827c5b17cSBrian Norris 	chip->ecc.read_oob = brcmnand_read_oob;
273927c5b17cSBrian Norris 	chip->ecc.write_oob = brcmnand_write_oob;
274027c5b17cSBrian Norris 
274127c5b17cSBrian Norris 	chip->controller = &ctrl->controller;
274227c5b17cSBrian Norris 
27434d1ea982SAnup Patel 	/*
27444d1ea982SAnup Patel 	 * The bootloader might have configured 16bit mode but
27454d1ea982SAnup Patel 	 * NAND READID command only works in 8bit mode. We force
27464d1ea982SAnup Patel 	 * 8bit mode here to ensure that NAND READID commands works.
27474d1ea982SAnup Patel 	 */
27484d1ea982SAnup Patel 	cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
27494d1ea982SAnup Patel 	nand_writereg(ctrl, cfg_offs,
27504d1ea982SAnup Patel 		      nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
27514d1ea982SAnup Patel 
275200ad378fSBoris Brezillon 	ret = nand_scan(chip, 1);
2753c25cca03SMasahiro Yamada 	if (ret)
2754c25cca03SMasahiro Yamada 		return ret;
275527c5b17cSBrian Norris 
27565826b880SMiquel Raynal 	ret = mtd_device_register(mtd, NULL, 0);
27575826b880SMiquel Raynal 	if (ret)
27585826b880SMiquel Raynal 		nand_cleanup(chip);
27595826b880SMiquel Raynal 
27605826b880SMiquel Raynal 	return ret;
276127c5b17cSBrian Norris }
276227c5b17cSBrian Norris 
276327c5b17cSBrian Norris static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
276427c5b17cSBrian Norris 					    int restore)
276527c5b17cSBrian Norris {
276627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
276727c5b17cSBrian Norris 	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
276827c5b17cSBrian Norris 	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
276927c5b17cSBrian Norris 			BRCMNAND_CS_CFG_EXT);
277027c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
277127c5b17cSBrian Norris 			BRCMNAND_CS_ACC_CONTROL);
277227c5b17cSBrian Norris 	u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
277327c5b17cSBrian Norris 	u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
277427c5b17cSBrian Norris 
277527c5b17cSBrian Norris 	if (restore) {
277627c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
277727c5b17cSBrian Norris 		if (cfg_offs != cfg_ext_offs)
277827c5b17cSBrian Norris 			nand_writereg(ctrl, cfg_ext_offs,
277927c5b17cSBrian Norris 				      host->hwcfg.config_ext);
278027c5b17cSBrian Norris 		nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
278127c5b17cSBrian Norris 		nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
278227c5b17cSBrian Norris 		nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
278327c5b17cSBrian Norris 	} else {
278427c5b17cSBrian Norris 		host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
278527c5b17cSBrian Norris 		if (cfg_offs != cfg_ext_offs)
278627c5b17cSBrian Norris 			host->hwcfg.config_ext =
278727c5b17cSBrian Norris 				nand_readreg(ctrl, cfg_ext_offs);
278827c5b17cSBrian Norris 		host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
278927c5b17cSBrian Norris 		host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
279027c5b17cSBrian Norris 		host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
279127c5b17cSBrian Norris 	}
279227c5b17cSBrian Norris }
279327c5b17cSBrian Norris 
279427c5b17cSBrian Norris static int brcmnand_suspend(struct device *dev)
279527c5b17cSBrian Norris {
279627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
279727c5b17cSBrian Norris 	struct brcmnand_host *host;
279827c5b17cSBrian Norris 
279927c5b17cSBrian Norris 	list_for_each_entry(host, &ctrl->host_list, node)
280027c5b17cSBrian Norris 		brcmnand_save_restore_cs_config(host, 0);
280127c5b17cSBrian Norris 
280227c5b17cSBrian Norris 	ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
280327c5b17cSBrian Norris 	ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
280427c5b17cSBrian Norris 	ctrl->corr_stat_threshold =
280527c5b17cSBrian Norris 		brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
280627c5b17cSBrian Norris 
280727c5b17cSBrian Norris 	if (has_flash_dma(ctrl))
280827c5b17cSBrian Norris 		ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
2809a5d53ad2SKamal Dasu 	else if (has_edu(ctrl))
2810a5d53ad2SKamal Dasu 		ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG);
281127c5b17cSBrian Norris 
281227c5b17cSBrian Norris 	return 0;
281327c5b17cSBrian Norris }
281427c5b17cSBrian Norris 
281527c5b17cSBrian Norris static int brcmnand_resume(struct device *dev)
281627c5b17cSBrian Norris {
281727c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
281827c5b17cSBrian Norris 	struct brcmnand_host *host;
281927c5b17cSBrian Norris 
282027c5b17cSBrian Norris 	if (has_flash_dma(ctrl)) {
282127c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
282227c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
282327c5b17cSBrian Norris 	}
282427c5b17cSBrian Norris 
2825f3a6a6c5SKamal Dasu 	if (has_edu(ctrl)) {
2826a5d53ad2SKamal Dasu 		ctrl->edu_config = edu_readl(ctrl, EDU_CONFIG);
2827a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_CONFIG, ctrl->edu_config);
2828a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_CONFIG);
2829a5d53ad2SKamal Dasu 		brcmnand_edu_init(ctrl);
2830a5d53ad2SKamal Dasu 	}
2831a5d53ad2SKamal Dasu 
283227c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
283327c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
283427c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
283527c5b17cSBrian Norris 			ctrl->corr_stat_threshold);
2836c26211d3SBrian Norris 	if (ctrl->soc) {
2837c26211d3SBrian Norris 		/* Clear/re-enable interrupt */
2838c26211d3SBrian Norris 		ctrl->soc->ctlrdy_ack(ctrl->soc);
2839c26211d3SBrian Norris 		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
2840c26211d3SBrian Norris 	}
284127c5b17cSBrian Norris 
284227c5b17cSBrian Norris 	list_for_each_entry(host, &ctrl->host_list, node) {
2843f1c4c999SBoris BREZILLON 		struct nand_chip *chip = &host->chip;
284427c5b17cSBrian Norris 
284527c5b17cSBrian Norris 		brcmnand_save_restore_cs_config(host, 1);
284627c5b17cSBrian Norris 
284727c5b17cSBrian Norris 		/* Reset the chip, required by some chips after power-up */
284897d90da8SBoris Brezillon 		nand_reset_op(chip);
284927c5b17cSBrian Norris 	}
285027c5b17cSBrian Norris 
285127c5b17cSBrian Norris 	return 0;
285227c5b17cSBrian Norris }
285327c5b17cSBrian Norris 
285427c5b17cSBrian Norris const struct dev_pm_ops brcmnand_pm_ops = {
285527c5b17cSBrian Norris 	.suspend		= brcmnand_suspend,
285627c5b17cSBrian Norris 	.resume			= brcmnand_resume,
285727c5b17cSBrian Norris };
285827c5b17cSBrian Norris EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
285927c5b17cSBrian Norris 
286027c5b17cSBrian Norris static const struct of_device_id brcmnand_of_match[] = {
28617e7c7df5SÁlvaro Fernández Rojas 	{ .compatible = "brcm,brcmnand-v2.1" },
28627e7c7df5SÁlvaro Fernández Rojas 	{ .compatible = "brcm,brcmnand-v2.2" },
286327c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v4.0" },
286427c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v5.0" },
286527c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v6.0" },
286627c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v6.1" },
2867269ecf03SFlorian Fainelli 	{ .compatible = "brcm,brcmnand-v6.2" },
286827c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v7.0" },
286927c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v7.1" },
2870decba6d4SFlorian Fainelli 	{ .compatible = "brcm,brcmnand-v7.2" },
28710c06da57SKamal Dasu 	{ .compatible = "brcm,brcmnand-v7.3" },
287227c5b17cSBrian Norris 	{},
287327c5b17cSBrian Norris };
287427c5b17cSBrian Norris MODULE_DEVICE_TABLE(of, brcmnand_of_match);
287527c5b17cSBrian Norris 
287627c5b17cSBrian Norris /***********************************************************************
287727c5b17cSBrian Norris  * Platform driver setup (per controller)
287827c5b17cSBrian Norris  ***********************************************************************/
2879a5d53ad2SKamal Dasu static int brcmnand_edu_setup(struct platform_device *pdev)
2880a5d53ad2SKamal Dasu {
2881a5d53ad2SKamal Dasu 	struct device *dev = &pdev->dev;
2882a5d53ad2SKamal Dasu 	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
2883a5d53ad2SKamal Dasu 	struct resource *res;
2884a5d53ad2SKamal Dasu 	int ret;
2885a5d53ad2SKamal Dasu 
2886a5d53ad2SKamal Dasu 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-edu");
2887a5d53ad2SKamal Dasu 	if (res) {
2888a5d53ad2SKamal Dasu 		ctrl->edu_base = devm_ioremap_resource(dev, res);
2889a5d53ad2SKamal Dasu 		if (IS_ERR(ctrl->edu_base))
2890a5d53ad2SKamal Dasu 			return PTR_ERR(ctrl->edu_base);
2891a5d53ad2SKamal Dasu 
2892a5d53ad2SKamal Dasu 		ctrl->edu_offsets = edu_regs;
2893a5d53ad2SKamal Dasu 
2894a5d53ad2SKamal Dasu 		edu_writel(ctrl, EDU_CONFIG, EDU_CONFIG_MODE_NAND |
2895a5d53ad2SKamal Dasu 			   EDU_CONFIG_SWAP_CFG);
2896a5d53ad2SKamal Dasu 		edu_readl(ctrl, EDU_CONFIG);
2897a5d53ad2SKamal Dasu 
2898a5d53ad2SKamal Dasu 		/* initialize edu */
2899a5d53ad2SKamal Dasu 		brcmnand_edu_init(ctrl);
2900a5d53ad2SKamal Dasu 
2901a5d53ad2SKamal Dasu 		ctrl->edu_irq = platform_get_irq_optional(pdev, 1);
2902a5d53ad2SKamal Dasu 		if (ctrl->edu_irq < 0) {
2903a5d53ad2SKamal Dasu 			dev_warn(dev,
2904a5d53ad2SKamal Dasu 				 "FLASH EDU enabled, using ctlrdy irq\n");
2905a5d53ad2SKamal Dasu 		} else {
2906a5d53ad2SKamal Dasu 			ret = devm_request_irq(dev, ctrl->edu_irq,
2907a5d53ad2SKamal Dasu 					       brcmnand_edu_irq, 0,
2908a5d53ad2SKamal Dasu 					       "brcmnand-edu", ctrl);
2909a5d53ad2SKamal Dasu 			if (ret < 0) {
2910a5d53ad2SKamal Dasu 				dev_err(ctrl->dev, "can't allocate IRQ %d: error %d\n",
2911a5d53ad2SKamal Dasu 					ctrl->edu_irq, ret);
2912a5d53ad2SKamal Dasu 				return ret;
2913a5d53ad2SKamal Dasu 			}
2914a5d53ad2SKamal Dasu 
2915a5d53ad2SKamal Dasu 			dev_info(dev, "FLASH EDU enabled using irq %u\n",
2916a5d53ad2SKamal Dasu 				 ctrl->edu_irq);
2917a5d53ad2SKamal Dasu 		}
2918a5d53ad2SKamal Dasu 	}
2919a5d53ad2SKamal Dasu 
2920a5d53ad2SKamal Dasu 	return 0;
2921a5d53ad2SKamal Dasu }
292227c5b17cSBrian Norris 
292327c5b17cSBrian Norris int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
292427c5b17cSBrian Norris {
292527c5b17cSBrian Norris 	struct device *dev = &pdev->dev;
292627c5b17cSBrian Norris 	struct device_node *dn = dev->of_node, *child;
2927bcb83a19SHauke Mehrtens 	struct brcmnand_controller *ctrl;
292827c5b17cSBrian Norris 	struct resource *res;
292927c5b17cSBrian Norris 	int ret;
293027c5b17cSBrian Norris 
293127c5b17cSBrian Norris 	/* We only support device-tree instantiation */
293227c5b17cSBrian Norris 	if (!dn)
293327c5b17cSBrian Norris 		return -ENODEV;
293427c5b17cSBrian Norris 
293527c5b17cSBrian Norris 	if (!of_match_node(brcmnand_of_match, dn))
293627c5b17cSBrian Norris 		return -ENODEV;
293727c5b17cSBrian Norris 
293827c5b17cSBrian Norris 	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
293927c5b17cSBrian Norris 	if (!ctrl)
294027c5b17cSBrian Norris 		return -ENOMEM;
294127c5b17cSBrian Norris 
294227c5b17cSBrian Norris 	dev_set_drvdata(dev, ctrl);
294327c5b17cSBrian Norris 	ctrl->dev = dev;
294427c5b17cSBrian Norris 
294527c5b17cSBrian Norris 	init_completion(&ctrl->done);
294627c5b17cSBrian Norris 	init_completion(&ctrl->dma_done);
2947a5d53ad2SKamal Dasu 	init_completion(&ctrl->edu_done);
29487da45139SMiquel Raynal 	nand_controller_init(&ctrl->controller);
29494918b905SMiquel Raynal 	ctrl->controller.ops = &brcmnand_controller_ops;
295027c5b17cSBrian Norris 	INIT_LIST_HEAD(&ctrl->host_list);
295127c5b17cSBrian Norris 
295227c5b17cSBrian Norris 	/* NAND register range */
295327c5b17cSBrian Norris 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
295427c5b17cSBrian Norris 	ctrl->nand_base = devm_ioremap_resource(dev, res);
295527c5b17cSBrian Norris 	if (IS_ERR(ctrl->nand_base))
295627c5b17cSBrian Norris 		return PTR_ERR(ctrl->nand_base);
295727c5b17cSBrian Norris 
29585c05bc00SSimon Arlott 	/* Enable clock before using NAND registers */
29595c05bc00SSimon Arlott 	ctrl->clk = devm_clk_get(dev, "nand");
29605c05bc00SSimon Arlott 	if (!IS_ERR(ctrl->clk)) {
29615c05bc00SSimon Arlott 		ret = clk_prepare_enable(ctrl->clk);
29625c05bc00SSimon Arlott 		if (ret)
29635c05bc00SSimon Arlott 			return ret;
29645c05bc00SSimon Arlott 	} else {
29655c05bc00SSimon Arlott 		ret = PTR_ERR(ctrl->clk);
29665c05bc00SSimon Arlott 		if (ret == -EPROBE_DEFER)
29675c05bc00SSimon Arlott 			return ret;
29685c05bc00SSimon Arlott 
29695c05bc00SSimon Arlott 		ctrl->clk = NULL;
29705c05bc00SSimon Arlott 	}
29715c05bc00SSimon Arlott 
297227c5b17cSBrian Norris 	/* Initialize NAND revision */
297327c5b17cSBrian Norris 	ret = brcmnand_revision_init(ctrl);
297427c5b17cSBrian Norris 	if (ret)
29755c05bc00SSimon Arlott 		goto err;
297627c5b17cSBrian Norris 
297727c5b17cSBrian Norris 	/*
297827c5b17cSBrian Norris 	 * Most chips have this cache at a fixed offset within 'nand' block.
297927c5b17cSBrian Norris 	 * Some must specify this region separately.
298027c5b17cSBrian Norris 	 */
298127c5b17cSBrian Norris 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
298227c5b17cSBrian Norris 	if (res) {
298327c5b17cSBrian Norris 		ctrl->nand_fc = devm_ioremap_resource(dev, res);
29845c05bc00SSimon Arlott 		if (IS_ERR(ctrl->nand_fc)) {
29855c05bc00SSimon Arlott 			ret = PTR_ERR(ctrl->nand_fc);
29865c05bc00SSimon Arlott 			goto err;
29875c05bc00SSimon Arlott 		}
298827c5b17cSBrian Norris 	} else {
298927c5b17cSBrian Norris 		ctrl->nand_fc = ctrl->nand_base +
299027c5b17cSBrian Norris 				ctrl->reg_offsets[BRCMNAND_FC_BASE];
299127c5b17cSBrian Norris 	}
299227c5b17cSBrian Norris 
299327c5b17cSBrian Norris 	/* FLASH_DMA */
299427c5b17cSBrian Norris 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
299527c5b17cSBrian Norris 	if (res) {
299627c5b17cSBrian Norris 		ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
29975c05bc00SSimon Arlott 		if (IS_ERR(ctrl->flash_dma_base)) {
29985c05bc00SSimon Arlott 			ret = PTR_ERR(ctrl->flash_dma_base);
29995c05bc00SSimon Arlott 			goto err;
30005c05bc00SSimon Arlott 		}
300127c5b17cSBrian Norris 
30020c06da57SKamal Dasu 		/* initialize the dma version */
30030c06da57SKamal Dasu 		brcmnand_flash_dma_revision_init(ctrl);
30040c06da57SKamal Dasu 
3005393947e5SFlorian Fainelli 		ret = -EIO;
3006393947e5SFlorian Fainelli 		if (ctrl->nand_version >= 0x0700)
3007393947e5SFlorian Fainelli 			ret = dma_set_mask_and_coherent(&pdev->dev,
3008393947e5SFlorian Fainelli 							DMA_BIT_MASK(40));
3009393947e5SFlorian Fainelli 		if (ret)
3010393947e5SFlorian Fainelli 			ret = dma_set_mask_and_coherent(&pdev->dev,
3011393947e5SFlorian Fainelli 							DMA_BIT_MASK(32));
3012393947e5SFlorian Fainelli 		if (ret)
3013393947e5SFlorian Fainelli 			goto err;
3014393947e5SFlorian Fainelli 
30150c06da57SKamal Dasu 		/* linked-list and stop on error */
30160c06da57SKamal Dasu 		flash_dma_writel(ctrl, FLASH_DMA_MODE, FLASH_DMA_MODE_MASK);
301727c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
301827c5b17cSBrian Norris 
301927c5b17cSBrian Norris 		/* Allocate descriptor(s) */
302027c5b17cSBrian Norris 		ctrl->dma_desc = dmam_alloc_coherent(dev,
302127c5b17cSBrian Norris 						     sizeof(*ctrl->dma_desc),
302227c5b17cSBrian Norris 						     &ctrl->dma_pa, GFP_KERNEL);
30235c05bc00SSimon Arlott 		if (!ctrl->dma_desc) {
30245c05bc00SSimon Arlott 			ret = -ENOMEM;
30255c05bc00SSimon Arlott 			goto err;
30265c05bc00SSimon Arlott 		}
302727c5b17cSBrian Norris 
302827c5b17cSBrian Norris 		ctrl->dma_irq = platform_get_irq(pdev, 1);
302927c5b17cSBrian Norris 		if ((int)ctrl->dma_irq < 0) {
303027c5b17cSBrian Norris 			dev_err(dev, "missing FLASH_DMA IRQ\n");
30315c05bc00SSimon Arlott 			ret = -ENODEV;
30325c05bc00SSimon Arlott 			goto err;
303327c5b17cSBrian Norris 		}
303427c5b17cSBrian Norris 
303527c5b17cSBrian Norris 		ret = devm_request_irq(dev, ctrl->dma_irq,
303627c5b17cSBrian Norris 				brcmnand_dma_irq, 0, DRV_NAME,
303727c5b17cSBrian Norris 				ctrl);
303827c5b17cSBrian Norris 		if (ret < 0) {
303927c5b17cSBrian Norris 			dev_err(dev, "can't allocate IRQ %d: error %d\n",
304027c5b17cSBrian Norris 					ctrl->dma_irq, ret);
30415c05bc00SSimon Arlott 			goto err;
304227c5b17cSBrian Norris 		}
304327c5b17cSBrian Norris 
304427c5b17cSBrian Norris 		dev_info(dev, "enabling FLASH_DMA\n");
3045a5d53ad2SKamal Dasu 		/* set flash dma transfer function to call */
3046a5d53ad2SKamal Dasu 		ctrl->dma_trans = brcmnand_dma_trans;
3047a5d53ad2SKamal Dasu 	} else	{
3048a5d53ad2SKamal Dasu 		ret = brcmnand_edu_setup(pdev);
3049a5d53ad2SKamal Dasu 		if (ret < 0)
3050a5d53ad2SKamal Dasu 			goto err;
3051a5d53ad2SKamal Dasu 
3052bee3ab8bSKamal Dasu 		if (has_edu(ctrl))
3053a5d53ad2SKamal Dasu 			/* set edu transfer function to call */
3054a5d53ad2SKamal Dasu 			ctrl->dma_trans = brcmnand_edu_trans;
305527c5b17cSBrian Norris 	}
305627c5b17cSBrian Norris 
305727c5b17cSBrian Norris 	/* Disable automatic device ID config, direct addressing */
305827c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
305927c5b17cSBrian Norris 			 CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
306027c5b17cSBrian Norris 	/* Disable XOR addressing */
306127c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
306227c5b17cSBrian Norris 
306327c5b17cSBrian Norris 	if (ctrl->features & BRCMNAND_HAS_WP) {
306427c5b17cSBrian Norris 		/* Permanently disable write protection */
306527c5b17cSBrian Norris 		if (wp_on == 2)
306627c5b17cSBrian Norris 			brcmnand_set_wp(ctrl, false);
306727c5b17cSBrian Norris 	} else {
306827c5b17cSBrian Norris 		wp_on = 0;
306927c5b17cSBrian Norris 	}
307027c5b17cSBrian Norris 
307127c5b17cSBrian Norris 	/* IRQ */
307227c5b17cSBrian Norris 	ctrl->irq = platform_get_irq(pdev, 0);
307327c5b17cSBrian Norris 	if ((int)ctrl->irq < 0) {
307427c5b17cSBrian Norris 		dev_err(dev, "no IRQ defined\n");
30755c05bc00SSimon Arlott 		ret = -ENODEV;
30765c05bc00SSimon Arlott 		goto err;
307727c5b17cSBrian Norris 	}
307827c5b17cSBrian Norris 
3079c26211d3SBrian Norris 	/*
3080c26211d3SBrian Norris 	 * Some SoCs integrate this controller (e.g., its interrupt bits) in
3081c26211d3SBrian Norris 	 * interesting ways
3082c26211d3SBrian Norris 	 */
3083c26211d3SBrian Norris 	if (soc) {
3084c26211d3SBrian Norris 		ctrl->soc = soc;
3085c26211d3SBrian Norris 
3086c26211d3SBrian Norris 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
3087c26211d3SBrian Norris 				       DRV_NAME, ctrl);
3088c26211d3SBrian Norris 
3089c26211d3SBrian Norris 		/* Enable interrupt */
3090c26211d3SBrian Norris 		ctrl->soc->ctlrdy_ack(ctrl->soc);
3091c26211d3SBrian Norris 		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
3092c26211d3SBrian Norris 	} else {
3093c26211d3SBrian Norris 		/* Use standard interrupt infrastructure */
309427c5b17cSBrian Norris 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
309527c5b17cSBrian Norris 				       DRV_NAME, ctrl);
3096c26211d3SBrian Norris 	}
309727c5b17cSBrian Norris 	if (ret < 0) {
309827c5b17cSBrian Norris 		dev_err(dev, "can't allocate IRQ %d: error %d\n",
309927c5b17cSBrian Norris 			ctrl->irq, ret);
31005c05bc00SSimon Arlott 		goto err;
310127c5b17cSBrian Norris 	}
310227c5b17cSBrian Norris 
310327c5b17cSBrian Norris 	for_each_available_child_of_node(dn, child) {
310427c5b17cSBrian Norris 		if (of_device_is_compatible(child, "brcm,nandcs")) {
310527c5b17cSBrian Norris 			struct brcmnand_host *host;
310627c5b17cSBrian Norris 
310727c5b17cSBrian Norris 			host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
3108081976bcSJulia Lawall 			if (!host) {
3109081976bcSJulia Lawall 				of_node_put(child);
31105c05bc00SSimon Arlott 				ret = -ENOMEM;
31115c05bc00SSimon Arlott 				goto err;
3112081976bcSJulia Lawall 			}
311327c5b17cSBrian Norris 			host->pdev = pdev;
311427c5b17cSBrian Norris 			host->ctrl = ctrl;
311527c5b17cSBrian Norris 
3116d121b66dSBrian Norris 			ret = brcmnand_init_cs(host, child);
3117081976bcSJulia Lawall 			if (ret) {
3118081976bcSJulia Lawall 				devm_kfree(dev, host);
311927c5b17cSBrian Norris 				continue; /* Try all chip-selects */
3120081976bcSJulia Lawall 			}
312127c5b17cSBrian Norris 
312227c5b17cSBrian Norris 			list_add_tail(&host->node, &ctrl->host_list);
312327c5b17cSBrian Norris 		}
312427c5b17cSBrian Norris 	}
312527c5b17cSBrian Norris 
312627c5b17cSBrian Norris 	/* No chip-selects could initialize properly */
31275c05bc00SSimon Arlott 	if (list_empty(&ctrl->host_list)) {
31285c05bc00SSimon Arlott 		ret = -ENODEV;
31295c05bc00SSimon Arlott 		goto err;
31305c05bc00SSimon Arlott 	}
313127c5b17cSBrian Norris 
313227c5b17cSBrian Norris 	return 0;
31335c05bc00SSimon Arlott 
31345c05bc00SSimon Arlott err:
31355c05bc00SSimon Arlott 	clk_disable_unprepare(ctrl->clk);
31365c05bc00SSimon Arlott 	return ret;
31375c05bc00SSimon Arlott 
313827c5b17cSBrian Norris }
313927c5b17cSBrian Norris EXPORT_SYMBOL_GPL(brcmnand_probe);
314027c5b17cSBrian Norris 
314127c5b17cSBrian Norris int brcmnand_remove(struct platform_device *pdev)
314227c5b17cSBrian Norris {
314327c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
314427c5b17cSBrian Norris 	struct brcmnand_host *host;
3145937d039dSMiquel Raynal 	struct nand_chip *chip;
3146937d039dSMiquel Raynal 	int ret;
314727c5b17cSBrian Norris 
3148937d039dSMiquel Raynal 	list_for_each_entry(host, &ctrl->host_list, node) {
3149937d039dSMiquel Raynal 		chip = &host->chip;
3150937d039dSMiquel Raynal 		ret = mtd_device_unregister(nand_to_mtd(chip));
3151937d039dSMiquel Raynal 		WARN_ON(ret);
3152937d039dSMiquel Raynal 		nand_cleanup(chip);
3153937d039dSMiquel Raynal 	}
315427c5b17cSBrian Norris 
31555c05bc00SSimon Arlott 	clk_disable_unprepare(ctrl->clk);
31565c05bc00SSimon Arlott 
315727c5b17cSBrian Norris 	dev_set_drvdata(&pdev->dev, NULL);
315827c5b17cSBrian Norris 
315927c5b17cSBrian Norris 	return 0;
316027c5b17cSBrian Norris }
316127c5b17cSBrian Norris EXPORT_SYMBOL_GPL(brcmnand_remove);
316227c5b17cSBrian Norris 
316327c5b17cSBrian Norris MODULE_LICENSE("GPL v2");
316427c5b17cSBrian Norris MODULE_AUTHOR("Kevin Cernekee");
316527c5b17cSBrian Norris MODULE_AUTHOR("Brian Norris");
316627c5b17cSBrian Norris MODULE_DESCRIPTION("NAND driver for Broadcom chips");
316727c5b17cSBrian Norris MODULE_ALIAS("platform:brcmnand");
3168