xref: /linux/drivers/mtd/nand/raw/brcmnand/brcmnand.c (revision 1802d0beecafe581ad584634ba92f8a471d8a63a)
1*1802d0beSThomas 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/version.h>
827c5b17cSBrian Norris #include <linux/module.h>
927c5b17cSBrian Norris #include <linux/init.h>
1027c5b17cSBrian Norris #include <linux/delay.h>
1127c5b17cSBrian Norris #include <linux/device.h>
1227c5b17cSBrian Norris #include <linux/platform_device.h>
1327c5b17cSBrian Norris #include <linux/err.h>
1427c5b17cSBrian Norris #include <linux/completion.h>
1527c5b17cSBrian Norris #include <linux/interrupt.h>
1627c5b17cSBrian Norris #include <linux/spinlock.h>
1727c5b17cSBrian Norris #include <linux/dma-mapping.h>
1827c5b17cSBrian Norris #include <linux/ioport.h>
1927c5b17cSBrian Norris #include <linux/bug.h>
2027c5b17cSBrian Norris #include <linux/kernel.h>
2127c5b17cSBrian Norris #include <linux/bitops.h>
2227c5b17cSBrian Norris #include <linux/mm.h>
2327c5b17cSBrian Norris #include <linux/mtd/mtd.h>
24d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h>
2527c5b17cSBrian Norris #include <linux/mtd/partitions.h>
2627c5b17cSBrian Norris #include <linux/of.h>
2727c5b17cSBrian Norris #include <linux/of_platform.h>
2827c5b17cSBrian Norris #include <linux/slab.h>
2927c5b17cSBrian Norris #include <linux/list.h>
3027c5b17cSBrian Norris #include <linux/log2.h>
3127c5b17cSBrian Norris 
3227c5b17cSBrian Norris #include "brcmnand.h"
3327c5b17cSBrian Norris 
3427c5b17cSBrian Norris /*
3527c5b17cSBrian Norris  * This flag controls if WP stays on between erase/write commands to mitigate
3627c5b17cSBrian Norris  * flash corruption due to power glitches. Values:
3727c5b17cSBrian Norris  * 0: NAND_WP is not used or not available
3827c5b17cSBrian Norris  * 1: NAND_WP is set by default, cleared for erase/write operations
3927c5b17cSBrian Norris  * 2: NAND_WP is always cleared
4027c5b17cSBrian Norris  */
4127c5b17cSBrian Norris static int wp_on = 1;
4227c5b17cSBrian Norris module_param(wp_on, int, 0444);
4327c5b17cSBrian Norris 
4427c5b17cSBrian Norris /***********************************************************************
4527c5b17cSBrian Norris  * Definitions
4627c5b17cSBrian Norris  ***********************************************************************/
4727c5b17cSBrian Norris 
4827c5b17cSBrian Norris #define DRV_NAME			"brcmnand"
4927c5b17cSBrian Norris 
5027c5b17cSBrian Norris #define CMD_NULL			0x00
5127c5b17cSBrian Norris #define CMD_PAGE_READ			0x01
5227c5b17cSBrian Norris #define CMD_SPARE_AREA_READ		0x02
5327c5b17cSBrian Norris #define CMD_STATUS_READ			0x03
5427c5b17cSBrian Norris #define CMD_PROGRAM_PAGE		0x04
5527c5b17cSBrian Norris #define CMD_PROGRAM_SPARE_AREA		0x05
5627c5b17cSBrian Norris #define CMD_COPY_BACK			0x06
5727c5b17cSBrian Norris #define CMD_DEVICE_ID_READ		0x07
5827c5b17cSBrian Norris #define CMD_BLOCK_ERASE			0x08
5927c5b17cSBrian Norris #define CMD_FLASH_RESET			0x09
6027c5b17cSBrian Norris #define CMD_BLOCKS_LOCK			0x0a
6127c5b17cSBrian Norris #define CMD_BLOCKS_LOCK_DOWN		0x0b
6227c5b17cSBrian Norris #define CMD_BLOCKS_UNLOCK		0x0c
6327c5b17cSBrian Norris #define CMD_READ_BLOCKS_LOCK_STATUS	0x0d
6427c5b17cSBrian Norris #define CMD_PARAMETER_READ		0x0e
6527c5b17cSBrian Norris #define CMD_PARAMETER_CHANGE_COL	0x0f
6627c5b17cSBrian Norris #define CMD_LOW_LEVEL_OP		0x10
6727c5b17cSBrian Norris 
6827c5b17cSBrian Norris struct brcm_nand_dma_desc {
6927c5b17cSBrian Norris 	u32 next_desc;
7027c5b17cSBrian Norris 	u32 next_desc_ext;
7127c5b17cSBrian Norris 	u32 cmd_irq;
7227c5b17cSBrian Norris 	u32 dram_addr;
7327c5b17cSBrian Norris 	u32 dram_addr_ext;
7427c5b17cSBrian Norris 	u32 tfr_len;
7527c5b17cSBrian Norris 	u32 total_len;
7627c5b17cSBrian Norris 	u32 flash_addr;
7727c5b17cSBrian Norris 	u32 flash_addr_ext;
7827c5b17cSBrian Norris 	u32 cs;
7927c5b17cSBrian Norris 	u32 pad2[5];
8027c5b17cSBrian Norris 	u32 status_valid;
8127c5b17cSBrian Norris } __packed;
8227c5b17cSBrian Norris 
8327c5b17cSBrian Norris /* Bitfields for brcm_nand_dma_desc::status_valid */
8427c5b17cSBrian Norris #define FLASH_DMA_ECC_ERROR	(1 << 8)
8527c5b17cSBrian Norris #define FLASH_DMA_CORR_ERROR	(1 << 9)
8627c5b17cSBrian Norris 
8727c5b17cSBrian Norris /* 512B flash cache in the NAND controller HW */
8827c5b17cSBrian Norris #define FC_SHIFT		9U
8927c5b17cSBrian Norris #define FC_BYTES		512U
9027c5b17cSBrian Norris #define FC_WORDS		(FC_BYTES >> 2)
9127c5b17cSBrian Norris 
9227c5b17cSBrian Norris #define BRCMNAND_MIN_PAGESIZE	512
9327c5b17cSBrian Norris #define BRCMNAND_MIN_BLOCKSIZE	(8 * 1024)
9427c5b17cSBrian Norris #define BRCMNAND_MIN_DEVSIZE	(4ULL * 1024 * 1024)
9527c5b17cSBrian Norris 
969d2ee0a6SKamal Dasu #define NAND_CTRL_RDY			(INTFC_CTLR_READY | INTFC_FLASH_READY)
979d2ee0a6SKamal Dasu #define NAND_POLL_STATUS_TIMEOUT_MS	100
989d2ee0a6SKamal Dasu 
9927c5b17cSBrian Norris /* Controller feature flags */
10027c5b17cSBrian Norris enum {
10127c5b17cSBrian Norris 	BRCMNAND_HAS_1K_SECTORS			= BIT(0),
10227c5b17cSBrian Norris 	BRCMNAND_HAS_PREFETCH			= BIT(1),
10327c5b17cSBrian Norris 	BRCMNAND_HAS_CACHE_MODE			= BIT(2),
10427c5b17cSBrian Norris 	BRCMNAND_HAS_WP				= BIT(3),
10527c5b17cSBrian Norris };
10627c5b17cSBrian Norris 
10727c5b17cSBrian Norris struct brcmnand_controller {
10827c5b17cSBrian Norris 	struct device		*dev;
1097da45139SMiquel Raynal 	struct nand_controller	controller;
11027c5b17cSBrian Norris 	void __iomem		*nand_base;
11127c5b17cSBrian Norris 	void __iomem		*nand_fc; /* flash cache */
11227c5b17cSBrian Norris 	void __iomem		*flash_dma_base;
11327c5b17cSBrian Norris 	unsigned int		irq;
11427c5b17cSBrian Norris 	unsigned int		dma_irq;
11527c5b17cSBrian Norris 	int			nand_version;
11627c5b17cSBrian Norris 
117c26211d3SBrian Norris 	/* Some SoCs provide custom interrupt status register(s) */
118c26211d3SBrian Norris 	struct brcmnand_soc	*soc;
119c26211d3SBrian Norris 
1205c05bc00SSimon Arlott 	/* Some SoCs have a gateable clock for the controller */
1215c05bc00SSimon Arlott 	struct clk		*clk;
1225c05bc00SSimon Arlott 
12327c5b17cSBrian Norris 	int			cmd_pending;
12427c5b17cSBrian Norris 	bool			dma_pending;
12527c5b17cSBrian Norris 	struct completion	done;
12627c5b17cSBrian Norris 	struct completion	dma_done;
12727c5b17cSBrian Norris 
12827c5b17cSBrian Norris 	/* List of NAND hosts (one for each chip-select) */
12927c5b17cSBrian Norris 	struct list_head host_list;
13027c5b17cSBrian Norris 
13127c5b17cSBrian Norris 	struct brcm_nand_dma_desc *dma_desc;
13227c5b17cSBrian Norris 	dma_addr_t		dma_pa;
13327c5b17cSBrian Norris 
13427c5b17cSBrian Norris 	/* in-memory cache of the FLASH_CACHE, used only for some commands */
135d618baf9SBrian Norris 	u8			flash_cache[FC_BYTES];
13627c5b17cSBrian Norris 
13727c5b17cSBrian Norris 	/* Controller revision details */
13827c5b17cSBrian Norris 	const u16		*reg_offsets;
13927c5b17cSBrian Norris 	unsigned int		reg_spacing; /* between CS1, CS2, ... regs */
14027c5b17cSBrian Norris 	const u8		*cs_offsets; /* within each chip-select */
14127c5b17cSBrian Norris 	const u8		*cs0_offsets; /* within CS0, if different */
14227c5b17cSBrian Norris 	unsigned int		max_block_size;
14327c5b17cSBrian Norris 	const unsigned int	*block_sizes;
14427c5b17cSBrian Norris 	unsigned int		max_page_size;
14527c5b17cSBrian Norris 	const unsigned int	*page_sizes;
14627c5b17cSBrian Norris 	unsigned int		max_oob;
14727c5b17cSBrian Norris 	u32			features;
14827c5b17cSBrian Norris 
14927c5b17cSBrian Norris 	/* for low-power standby/resume only */
15027c5b17cSBrian Norris 	u32			nand_cs_nand_select;
15127c5b17cSBrian Norris 	u32			nand_cs_nand_xor;
15227c5b17cSBrian Norris 	u32			corr_stat_threshold;
15327c5b17cSBrian Norris 	u32			flash_dma_mode;
15427c5b17cSBrian Norris };
15527c5b17cSBrian Norris 
15627c5b17cSBrian Norris struct brcmnand_cfg {
15727c5b17cSBrian Norris 	u64			device_size;
15827c5b17cSBrian Norris 	unsigned int		block_size;
15927c5b17cSBrian Norris 	unsigned int		page_size;
16027c5b17cSBrian Norris 	unsigned int		spare_area_size;
16127c5b17cSBrian Norris 	unsigned int		device_width;
16227c5b17cSBrian Norris 	unsigned int		col_adr_bytes;
16327c5b17cSBrian Norris 	unsigned int		blk_adr_bytes;
16427c5b17cSBrian Norris 	unsigned int		ful_adr_bytes;
16527c5b17cSBrian Norris 	unsigned int		sector_size_1k;
16627c5b17cSBrian Norris 	unsigned int		ecc_level;
16727c5b17cSBrian Norris 	/* use for low-power standby/resume only */
16827c5b17cSBrian Norris 	u32			acc_control;
16927c5b17cSBrian Norris 	u32			config;
17027c5b17cSBrian Norris 	u32			config_ext;
17127c5b17cSBrian Norris 	u32			timing_1;
17227c5b17cSBrian Norris 	u32			timing_2;
17327c5b17cSBrian Norris };
17427c5b17cSBrian Norris 
17527c5b17cSBrian Norris struct brcmnand_host {
17627c5b17cSBrian Norris 	struct list_head	node;
17727c5b17cSBrian Norris 
17827c5b17cSBrian Norris 	struct nand_chip	chip;
17927c5b17cSBrian Norris 	struct platform_device	*pdev;
18027c5b17cSBrian Norris 	int			cs;
18127c5b17cSBrian Norris 
18227c5b17cSBrian Norris 	unsigned int		last_cmd;
18327c5b17cSBrian Norris 	unsigned int		last_byte;
18427c5b17cSBrian Norris 	u64			last_addr;
18527c5b17cSBrian Norris 	struct brcmnand_cfg	hwcfg;
18627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl;
18727c5b17cSBrian Norris };
18827c5b17cSBrian Norris 
18927c5b17cSBrian Norris enum brcmnand_reg {
19027c5b17cSBrian Norris 	BRCMNAND_CMD_START = 0,
19127c5b17cSBrian Norris 	BRCMNAND_CMD_EXT_ADDRESS,
19227c5b17cSBrian Norris 	BRCMNAND_CMD_ADDRESS,
19327c5b17cSBrian Norris 	BRCMNAND_INTFC_STATUS,
19427c5b17cSBrian Norris 	BRCMNAND_CS_SELECT,
19527c5b17cSBrian Norris 	BRCMNAND_CS_XOR,
19627c5b17cSBrian Norris 	BRCMNAND_LL_OP,
19727c5b17cSBrian Norris 	BRCMNAND_CS0_BASE,
19827c5b17cSBrian Norris 	BRCMNAND_CS1_BASE,		/* CS1 regs, if non-contiguous */
19927c5b17cSBrian Norris 	BRCMNAND_CORR_THRESHOLD,
20027c5b17cSBrian Norris 	BRCMNAND_CORR_THRESHOLD_EXT,
20127c5b17cSBrian Norris 	BRCMNAND_UNCORR_COUNT,
20227c5b17cSBrian Norris 	BRCMNAND_CORR_COUNT,
20327c5b17cSBrian Norris 	BRCMNAND_CORR_EXT_ADDR,
20427c5b17cSBrian Norris 	BRCMNAND_CORR_ADDR,
20527c5b17cSBrian Norris 	BRCMNAND_UNCORR_EXT_ADDR,
20627c5b17cSBrian Norris 	BRCMNAND_UNCORR_ADDR,
20727c5b17cSBrian Norris 	BRCMNAND_SEMAPHORE,
20827c5b17cSBrian Norris 	BRCMNAND_ID,
20927c5b17cSBrian Norris 	BRCMNAND_ID_EXT,
21027c5b17cSBrian Norris 	BRCMNAND_LL_RDATA,
21127c5b17cSBrian Norris 	BRCMNAND_OOB_READ_BASE,
21227c5b17cSBrian Norris 	BRCMNAND_OOB_READ_10_BASE,	/* offset 0x10, if non-contiguous */
21327c5b17cSBrian Norris 	BRCMNAND_OOB_WRITE_BASE,
21427c5b17cSBrian Norris 	BRCMNAND_OOB_WRITE_10_BASE,	/* offset 0x10, if non-contiguous */
21527c5b17cSBrian Norris 	BRCMNAND_FC_BASE,
21627c5b17cSBrian Norris };
21727c5b17cSBrian Norris 
21827c5b17cSBrian Norris /* BRCMNAND v4.0 */
21927c5b17cSBrian Norris static const u16 brcmnand_regs_v40[] = {
22027c5b17cSBrian Norris 	[BRCMNAND_CMD_START]		=  0x04,
22127c5b17cSBrian Norris 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
22227c5b17cSBrian Norris 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
22327c5b17cSBrian Norris 	[BRCMNAND_INTFC_STATUS]		=  0x6c,
22427c5b17cSBrian Norris 	[BRCMNAND_CS_SELECT]		=  0x14,
22527c5b17cSBrian Norris 	[BRCMNAND_CS_XOR]		=  0x18,
22627c5b17cSBrian Norris 	[BRCMNAND_LL_OP]		= 0x178,
22727c5b17cSBrian Norris 	[BRCMNAND_CS0_BASE]		=  0x40,
22827c5b17cSBrian Norris 	[BRCMNAND_CS1_BASE]		=  0xd0,
22927c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
23027c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
23127c5b17cSBrian Norris 	[BRCMNAND_UNCORR_COUNT]		=     0,
23227c5b17cSBrian Norris 	[BRCMNAND_CORR_COUNT]		=     0,
23327c5b17cSBrian Norris 	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
23427c5b17cSBrian Norris 	[BRCMNAND_CORR_ADDR]		=  0x74,
23527c5b17cSBrian Norris 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
23627c5b17cSBrian Norris 	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
23727c5b17cSBrian Norris 	[BRCMNAND_SEMAPHORE]		=  0x58,
23827c5b17cSBrian Norris 	[BRCMNAND_ID]			=  0x60,
23927c5b17cSBrian Norris 	[BRCMNAND_ID_EXT]		=  0x64,
24027c5b17cSBrian Norris 	[BRCMNAND_LL_RDATA]		= 0x17c,
24127c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
24227c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
24327c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
24427c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
24527c5b17cSBrian Norris 	[BRCMNAND_FC_BASE]		= 0x200,
24627c5b17cSBrian Norris };
24727c5b17cSBrian Norris 
24827c5b17cSBrian Norris /* BRCMNAND v5.0 */
24927c5b17cSBrian Norris static const u16 brcmnand_regs_v50[] = {
25027c5b17cSBrian Norris 	[BRCMNAND_CMD_START]		=  0x04,
25127c5b17cSBrian Norris 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
25227c5b17cSBrian Norris 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
25327c5b17cSBrian Norris 	[BRCMNAND_INTFC_STATUS]		=  0x6c,
25427c5b17cSBrian Norris 	[BRCMNAND_CS_SELECT]		=  0x14,
25527c5b17cSBrian Norris 	[BRCMNAND_CS_XOR]		=  0x18,
25627c5b17cSBrian Norris 	[BRCMNAND_LL_OP]		= 0x178,
25727c5b17cSBrian Norris 	[BRCMNAND_CS0_BASE]		=  0x40,
25827c5b17cSBrian Norris 	[BRCMNAND_CS1_BASE]		=  0xd0,
25927c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
26027c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
26127c5b17cSBrian Norris 	[BRCMNAND_UNCORR_COUNT]		=     0,
26227c5b17cSBrian Norris 	[BRCMNAND_CORR_COUNT]		=     0,
26327c5b17cSBrian Norris 	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
26427c5b17cSBrian Norris 	[BRCMNAND_CORR_ADDR]		=  0x74,
26527c5b17cSBrian Norris 	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
26627c5b17cSBrian Norris 	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
26727c5b17cSBrian Norris 	[BRCMNAND_SEMAPHORE]		=  0x58,
26827c5b17cSBrian Norris 	[BRCMNAND_ID]			=  0x60,
26927c5b17cSBrian Norris 	[BRCMNAND_ID_EXT]		=  0x64,
27027c5b17cSBrian Norris 	[BRCMNAND_LL_RDATA]		= 0x17c,
27127c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_BASE]	=  0x20,
27227c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
27327c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
27427c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_10_BASE]	= 0x140,
27527c5b17cSBrian Norris 	[BRCMNAND_FC_BASE]		= 0x200,
27627c5b17cSBrian Norris };
27727c5b17cSBrian Norris 
27827c5b17cSBrian Norris /* BRCMNAND v6.0 - v7.1 */
27927c5b17cSBrian Norris static const u16 brcmnand_regs_v60[] = {
28027c5b17cSBrian Norris 	[BRCMNAND_CMD_START]		=  0x04,
28127c5b17cSBrian Norris 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
28227c5b17cSBrian Norris 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
28327c5b17cSBrian Norris 	[BRCMNAND_INTFC_STATUS]		=  0x14,
28427c5b17cSBrian Norris 	[BRCMNAND_CS_SELECT]		=  0x18,
28527c5b17cSBrian Norris 	[BRCMNAND_CS_XOR]		=  0x1c,
28627c5b17cSBrian Norris 	[BRCMNAND_LL_OP]		=  0x20,
28727c5b17cSBrian Norris 	[BRCMNAND_CS0_BASE]		=  0x50,
28827c5b17cSBrian Norris 	[BRCMNAND_CS1_BASE]		=     0,
28927c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD]	=  0xc0,
29027c5b17cSBrian Norris 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xc4,
29127c5b17cSBrian Norris 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
29227c5b17cSBrian Norris 	[BRCMNAND_CORR_COUNT]		= 0x100,
29327c5b17cSBrian Norris 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
29427c5b17cSBrian Norris 	[BRCMNAND_CORR_ADDR]		= 0x110,
29527c5b17cSBrian Norris 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
29627c5b17cSBrian Norris 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
29727c5b17cSBrian Norris 	[BRCMNAND_SEMAPHORE]		= 0x150,
29827c5b17cSBrian Norris 	[BRCMNAND_ID]			= 0x194,
29927c5b17cSBrian Norris 	[BRCMNAND_ID_EXT]		= 0x198,
30027c5b17cSBrian Norris 	[BRCMNAND_LL_RDATA]		= 0x19c,
30127c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
30227c5b17cSBrian Norris 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
30327c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
30427c5b17cSBrian Norris 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
30527c5b17cSBrian Norris 	[BRCMNAND_FC_BASE]		= 0x400,
30627c5b17cSBrian Norris };
30727c5b17cSBrian Norris 
308d267aefcSFlorian Fainelli /* BRCMNAND v7.1 */
309d267aefcSFlorian Fainelli static const u16 brcmnand_regs_v71[] = {
310d267aefcSFlorian Fainelli 	[BRCMNAND_CMD_START]		=  0x04,
311d267aefcSFlorian Fainelli 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
312d267aefcSFlorian Fainelli 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
313d267aefcSFlorian Fainelli 	[BRCMNAND_INTFC_STATUS]		=  0x14,
314d267aefcSFlorian Fainelli 	[BRCMNAND_CS_SELECT]		=  0x18,
315d267aefcSFlorian Fainelli 	[BRCMNAND_CS_XOR]		=  0x1c,
316d267aefcSFlorian Fainelli 	[BRCMNAND_LL_OP]		=  0x20,
317d267aefcSFlorian Fainelli 	[BRCMNAND_CS0_BASE]		=  0x50,
318d267aefcSFlorian Fainelli 	[BRCMNAND_CS1_BASE]		=     0,
319d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD]	=  0xdc,
320d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xe0,
321d267aefcSFlorian Fainelli 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
322d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_COUNT]		= 0x100,
323d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
324d267aefcSFlorian Fainelli 	[BRCMNAND_CORR_ADDR]		= 0x110,
325d267aefcSFlorian Fainelli 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
326d267aefcSFlorian Fainelli 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
327d267aefcSFlorian Fainelli 	[BRCMNAND_SEMAPHORE]		= 0x150,
328d267aefcSFlorian Fainelli 	[BRCMNAND_ID]			= 0x194,
329d267aefcSFlorian Fainelli 	[BRCMNAND_ID_EXT]		= 0x198,
330d267aefcSFlorian Fainelli 	[BRCMNAND_LL_RDATA]		= 0x19c,
331d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
332d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
333d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
334d267aefcSFlorian Fainelli 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
335d267aefcSFlorian Fainelli 	[BRCMNAND_FC_BASE]		= 0x400,
336d267aefcSFlorian Fainelli };
337d267aefcSFlorian Fainelli 
338decba6d4SFlorian Fainelli /* BRCMNAND v7.2 */
339decba6d4SFlorian Fainelli static const u16 brcmnand_regs_v72[] = {
340decba6d4SFlorian Fainelli 	[BRCMNAND_CMD_START]		=  0x04,
341decba6d4SFlorian Fainelli 	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
342decba6d4SFlorian Fainelli 	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
343decba6d4SFlorian Fainelli 	[BRCMNAND_INTFC_STATUS]		=  0x14,
344decba6d4SFlorian Fainelli 	[BRCMNAND_CS_SELECT]		=  0x18,
345decba6d4SFlorian Fainelli 	[BRCMNAND_CS_XOR]		=  0x1c,
346decba6d4SFlorian Fainelli 	[BRCMNAND_LL_OP]		=  0x20,
347decba6d4SFlorian Fainelli 	[BRCMNAND_CS0_BASE]		=  0x50,
348decba6d4SFlorian Fainelli 	[BRCMNAND_CS1_BASE]		=     0,
349decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD]	=  0xdc,
350decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xe0,
351decba6d4SFlorian Fainelli 	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
352decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_COUNT]		= 0x100,
353decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
354decba6d4SFlorian Fainelli 	[BRCMNAND_CORR_ADDR]		= 0x110,
355decba6d4SFlorian Fainelli 	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
356decba6d4SFlorian Fainelli 	[BRCMNAND_UNCORR_ADDR]		= 0x118,
357decba6d4SFlorian Fainelli 	[BRCMNAND_SEMAPHORE]		= 0x150,
358decba6d4SFlorian Fainelli 	[BRCMNAND_ID]			= 0x194,
359decba6d4SFlorian Fainelli 	[BRCMNAND_ID_EXT]		= 0x198,
360decba6d4SFlorian Fainelli 	[BRCMNAND_LL_RDATA]		= 0x19c,
361decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_READ_BASE]	= 0x200,
362decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_READ_10_BASE]	=     0,
363decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_WRITE_BASE]	= 0x400,
364decba6d4SFlorian Fainelli 	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
365decba6d4SFlorian Fainelli 	[BRCMNAND_FC_BASE]		= 0x600,
366decba6d4SFlorian Fainelli };
367decba6d4SFlorian Fainelli 
36827c5b17cSBrian Norris enum brcmnand_cs_reg {
36927c5b17cSBrian Norris 	BRCMNAND_CS_CFG_EXT = 0,
37027c5b17cSBrian Norris 	BRCMNAND_CS_CFG,
37127c5b17cSBrian Norris 	BRCMNAND_CS_ACC_CONTROL,
37227c5b17cSBrian Norris 	BRCMNAND_CS_TIMING1,
37327c5b17cSBrian Norris 	BRCMNAND_CS_TIMING2,
37427c5b17cSBrian Norris };
37527c5b17cSBrian Norris 
37627c5b17cSBrian Norris /* Per chip-select offsets for v7.1 */
37727c5b17cSBrian Norris static const u8 brcmnand_cs_offsets_v71[] = {
37827c5b17cSBrian Norris 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
37927c5b17cSBrian Norris 	[BRCMNAND_CS_CFG_EXT]		= 0x04,
38027c5b17cSBrian Norris 	[BRCMNAND_CS_CFG]		= 0x08,
38127c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING1]		= 0x0c,
38227c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING2]		= 0x10,
38327c5b17cSBrian Norris };
38427c5b17cSBrian Norris 
38527c5b17cSBrian Norris /* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
38627c5b17cSBrian Norris static const u8 brcmnand_cs_offsets[] = {
38727c5b17cSBrian Norris 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
38827c5b17cSBrian Norris 	[BRCMNAND_CS_CFG_EXT]		= 0x04,
38927c5b17cSBrian Norris 	[BRCMNAND_CS_CFG]		= 0x04,
39027c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING1]		= 0x08,
39127c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING2]		= 0x0c,
39227c5b17cSBrian Norris };
39327c5b17cSBrian Norris 
39427c5b17cSBrian Norris /* Per chip-select offset for <= v5.0 on CS0 only */
39527c5b17cSBrian Norris static const u8 brcmnand_cs_offsets_cs0[] = {
39627c5b17cSBrian Norris 	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
39727c5b17cSBrian Norris 	[BRCMNAND_CS_CFG_EXT]		= 0x08,
39827c5b17cSBrian Norris 	[BRCMNAND_CS_CFG]		= 0x08,
39927c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING1]		= 0x10,
40027c5b17cSBrian Norris 	[BRCMNAND_CS_TIMING2]		= 0x14,
40127c5b17cSBrian Norris };
40227c5b17cSBrian Norris 
4033f06d2a9SBrian Norris /*
4043f06d2a9SBrian Norris  * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
4053f06d2a9SBrian Norris  * one config register, but once the bitfields overflowed, newer controllers
4063f06d2a9SBrian Norris  * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
4073f06d2a9SBrian Norris  */
4083f06d2a9SBrian Norris enum {
4093f06d2a9SBrian Norris 	CFG_BLK_ADR_BYTES_SHIFT		= 8,
4103f06d2a9SBrian Norris 	CFG_COL_ADR_BYTES_SHIFT		= 12,
4113f06d2a9SBrian Norris 	CFG_FUL_ADR_BYTES_SHIFT		= 16,
4123f06d2a9SBrian Norris 	CFG_BUS_WIDTH_SHIFT		= 23,
4133f06d2a9SBrian Norris 	CFG_BUS_WIDTH			= BIT(CFG_BUS_WIDTH_SHIFT),
4143f06d2a9SBrian Norris 	CFG_DEVICE_SIZE_SHIFT		= 24,
4153f06d2a9SBrian Norris 
4163f06d2a9SBrian Norris 	/* Only for pre-v7.1 (with no CFG_EXT register) */
4173f06d2a9SBrian Norris 	CFG_PAGE_SIZE_SHIFT		= 20,
4183f06d2a9SBrian Norris 	CFG_BLK_SIZE_SHIFT		= 28,
4193f06d2a9SBrian Norris 
4203f06d2a9SBrian Norris 	/* Only for v7.1+ (with CFG_EXT register) */
4213f06d2a9SBrian Norris 	CFG_EXT_PAGE_SIZE_SHIFT		= 0,
4223f06d2a9SBrian Norris 	CFG_EXT_BLK_SIZE_SHIFT		= 4,
4233f06d2a9SBrian Norris };
4243f06d2a9SBrian Norris 
42527c5b17cSBrian Norris /* BRCMNAND_INTFC_STATUS */
42627c5b17cSBrian Norris enum {
42727c5b17cSBrian Norris 	INTFC_FLASH_STATUS		= GENMASK(7, 0),
42827c5b17cSBrian Norris 
42927c5b17cSBrian Norris 	INTFC_ERASED			= BIT(27),
43027c5b17cSBrian Norris 	INTFC_OOB_VALID			= BIT(28),
43127c5b17cSBrian Norris 	INTFC_CACHE_VALID		= BIT(29),
43227c5b17cSBrian Norris 	INTFC_FLASH_READY		= BIT(30),
43327c5b17cSBrian Norris 	INTFC_CTLR_READY		= BIT(31),
43427c5b17cSBrian Norris };
43527c5b17cSBrian Norris 
43627c5b17cSBrian Norris static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
43727c5b17cSBrian Norris {
43827c5b17cSBrian Norris 	return brcmnand_readl(ctrl->nand_base + offs);
43927c5b17cSBrian Norris }
44027c5b17cSBrian Norris 
44127c5b17cSBrian Norris static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
44227c5b17cSBrian Norris 				 u32 val)
44327c5b17cSBrian Norris {
44427c5b17cSBrian Norris 	brcmnand_writel(val, ctrl->nand_base + offs);
44527c5b17cSBrian Norris }
44627c5b17cSBrian Norris 
44727c5b17cSBrian Norris static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
44827c5b17cSBrian Norris {
44927c5b17cSBrian Norris 	static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
45027c5b17cSBrian Norris 	static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
45127c5b17cSBrian Norris 	static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
45227c5b17cSBrian Norris 
45327c5b17cSBrian Norris 	ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
45427c5b17cSBrian Norris 
45527c5b17cSBrian Norris 	/* Only support v4.0+? */
45627c5b17cSBrian Norris 	if (ctrl->nand_version < 0x0400) {
45727c5b17cSBrian Norris 		dev_err(ctrl->dev, "version %#x not supported\n",
45827c5b17cSBrian Norris 			ctrl->nand_version);
45927c5b17cSBrian Norris 		return -ENODEV;
46027c5b17cSBrian Norris 	}
46127c5b17cSBrian Norris 
46227c5b17cSBrian Norris 	/* Register offsets */
463decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
464decba6d4SFlorian Fainelli 		ctrl->reg_offsets = brcmnand_regs_v72;
465decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0701)
466d267aefcSFlorian Fainelli 		ctrl->reg_offsets = brcmnand_regs_v71;
467d267aefcSFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
46827c5b17cSBrian Norris 		ctrl->reg_offsets = brcmnand_regs_v60;
46927c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
47027c5b17cSBrian Norris 		ctrl->reg_offsets = brcmnand_regs_v50;
47127c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0400)
47227c5b17cSBrian Norris 		ctrl->reg_offsets = brcmnand_regs_v40;
47327c5b17cSBrian Norris 
47427c5b17cSBrian Norris 	/* Chip-select stride */
47527c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0701)
47627c5b17cSBrian Norris 		ctrl->reg_spacing = 0x14;
47727c5b17cSBrian Norris 	else
47827c5b17cSBrian Norris 		ctrl->reg_spacing = 0x10;
47927c5b17cSBrian Norris 
48027c5b17cSBrian Norris 	/* Per chip-select registers */
48127c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0701) {
48227c5b17cSBrian Norris 		ctrl->cs_offsets = brcmnand_cs_offsets_v71;
48327c5b17cSBrian Norris 	} else {
48427c5b17cSBrian Norris 		ctrl->cs_offsets = brcmnand_cs_offsets;
48527c5b17cSBrian Norris 
48627c5b17cSBrian Norris 		/* v5.0 and earlier has a different CS0 offset layout */
48727c5b17cSBrian Norris 		if (ctrl->nand_version <= 0x0500)
48827c5b17cSBrian Norris 			ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
48927c5b17cSBrian Norris 	}
49027c5b17cSBrian Norris 
49127c5b17cSBrian Norris 	/* Page / block sizes */
49227c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0701) {
49327c5b17cSBrian Norris 		/* >= v7.1 use nice power-of-2 values! */
49427c5b17cSBrian Norris 		ctrl->max_page_size = 16 * 1024;
49527c5b17cSBrian Norris 		ctrl->max_block_size = 2 * 1024 * 1024;
49627c5b17cSBrian Norris 	} else {
49727c5b17cSBrian Norris 		ctrl->page_sizes = page_sizes;
49827c5b17cSBrian Norris 		if (ctrl->nand_version >= 0x0600)
49927c5b17cSBrian Norris 			ctrl->block_sizes = block_sizes_v6;
50027c5b17cSBrian Norris 		else
50127c5b17cSBrian Norris 			ctrl->block_sizes = block_sizes_v4;
50227c5b17cSBrian Norris 
50327c5b17cSBrian Norris 		if (ctrl->nand_version < 0x0400) {
50427c5b17cSBrian Norris 			ctrl->max_page_size = 4096;
50527c5b17cSBrian Norris 			ctrl->max_block_size = 512 * 1024;
50627c5b17cSBrian Norris 		}
50727c5b17cSBrian Norris 	}
50827c5b17cSBrian Norris 
50927c5b17cSBrian Norris 	/* Maximum spare area sector size (per 512B) */
510decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
511decba6d4SFlorian Fainelli 		ctrl->max_oob = 128;
512decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
51327c5b17cSBrian Norris 		ctrl->max_oob = 64;
51427c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
51527c5b17cSBrian Norris 		ctrl->max_oob = 32;
51627c5b17cSBrian Norris 	else
51727c5b17cSBrian Norris 		ctrl->max_oob = 16;
51827c5b17cSBrian Norris 
51927c5b17cSBrian Norris 	/* v6.0 and newer (except v6.1) have prefetch support */
52027c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
52127c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_PREFETCH;
52227c5b17cSBrian Norris 
52327c5b17cSBrian Norris 	/*
52427c5b17cSBrian Norris 	 * v6.x has cache mode, but it's implemented differently. Ignore it for
52527c5b17cSBrian Norris 	 * now.
52627c5b17cSBrian Norris 	 */
52727c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0700)
52827c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
52927c5b17cSBrian Norris 
53027c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0500)
53127c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
53227c5b17cSBrian Norris 
53327c5b17cSBrian Norris 	if (ctrl->nand_version >= 0x0700)
53427c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_WP;
53527c5b17cSBrian Norris 	else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
53627c5b17cSBrian Norris 		ctrl->features |= BRCMNAND_HAS_WP;
53727c5b17cSBrian Norris 
53827c5b17cSBrian Norris 	return 0;
53927c5b17cSBrian Norris }
54027c5b17cSBrian Norris 
54127c5b17cSBrian Norris static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
54227c5b17cSBrian Norris 		enum brcmnand_reg reg)
54327c5b17cSBrian Norris {
54427c5b17cSBrian Norris 	u16 offs = ctrl->reg_offsets[reg];
54527c5b17cSBrian Norris 
54627c5b17cSBrian Norris 	if (offs)
54727c5b17cSBrian Norris 		return nand_readreg(ctrl, offs);
54827c5b17cSBrian Norris 	else
54927c5b17cSBrian Norris 		return 0;
55027c5b17cSBrian Norris }
55127c5b17cSBrian Norris 
55227c5b17cSBrian Norris static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
55327c5b17cSBrian Norris 				      enum brcmnand_reg reg, u32 val)
55427c5b17cSBrian Norris {
55527c5b17cSBrian Norris 	u16 offs = ctrl->reg_offsets[reg];
55627c5b17cSBrian Norris 
55727c5b17cSBrian Norris 	if (offs)
55827c5b17cSBrian Norris 		nand_writereg(ctrl, offs, val);
55927c5b17cSBrian Norris }
56027c5b17cSBrian Norris 
56127c5b17cSBrian Norris static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
56227c5b17cSBrian Norris 				    enum brcmnand_reg reg, u32 mask, unsigned
56327c5b17cSBrian Norris 				    int shift, u32 val)
56427c5b17cSBrian Norris {
56527c5b17cSBrian Norris 	u32 tmp = brcmnand_read_reg(ctrl, reg);
56627c5b17cSBrian Norris 
56727c5b17cSBrian Norris 	tmp &= ~mask;
56827c5b17cSBrian Norris 	tmp |= val << shift;
56927c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, reg, tmp);
57027c5b17cSBrian Norris }
57127c5b17cSBrian Norris 
57227c5b17cSBrian Norris static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
57327c5b17cSBrian Norris {
57427c5b17cSBrian Norris 	return __raw_readl(ctrl->nand_fc + word * 4);
57527c5b17cSBrian Norris }
57627c5b17cSBrian Norris 
57727c5b17cSBrian Norris static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
57827c5b17cSBrian Norris 				     int word, u32 val)
57927c5b17cSBrian Norris {
58027c5b17cSBrian Norris 	__raw_writel(val, ctrl->nand_fc + word * 4);
58127c5b17cSBrian Norris }
58227c5b17cSBrian Norris 
58327c5b17cSBrian Norris static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
58427c5b17cSBrian Norris 				     enum brcmnand_cs_reg reg)
58527c5b17cSBrian Norris {
58627c5b17cSBrian Norris 	u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
58727c5b17cSBrian Norris 	u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
58827c5b17cSBrian Norris 	u8 cs_offs;
58927c5b17cSBrian Norris 
59027c5b17cSBrian Norris 	if (cs == 0 && ctrl->cs0_offsets)
59127c5b17cSBrian Norris 		cs_offs = ctrl->cs0_offsets[reg];
59227c5b17cSBrian Norris 	else
59327c5b17cSBrian Norris 		cs_offs = ctrl->cs_offsets[reg];
59427c5b17cSBrian Norris 
59527c5b17cSBrian Norris 	if (cs && offs_cs1)
59627c5b17cSBrian Norris 		return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
59727c5b17cSBrian Norris 
59827c5b17cSBrian Norris 	return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
59927c5b17cSBrian Norris }
60027c5b17cSBrian Norris 
60127c5b17cSBrian Norris static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
60227c5b17cSBrian Norris {
60327c5b17cSBrian Norris 	if (ctrl->nand_version < 0x0600)
60427c5b17cSBrian Norris 		return 1;
60527c5b17cSBrian Norris 	return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
60627c5b17cSBrian Norris }
60727c5b17cSBrian Norris 
60827c5b17cSBrian Norris static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
60927c5b17cSBrian Norris {
61027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
61127c5b17cSBrian Norris 	unsigned int shift = 0, bits;
61227c5b17cSBrian Norris 	enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
61327c5b17cSBrian Norris 	int cs = host->cs;
61427c5b17cSBrian Norris 
615decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
616decba6d4SFlorian Fainelli 		bits = 7;
617decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
61827c5b17cSBrian Norris 		bits = 6;
61927c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
62027c5b17cSBrian Norris 		bits = 5;
62127c5b17cSBrian Norris 	else
62227c5b17cSBrian Norris 		bits = 4;
62327c5b17cSBrian Norris 
624decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702) {
625decba6d4SFlorian Fainelli 		if (cs >= 4)
626decba6d4SFlorian Fainelli 			reg = BRCMNAND_CORR_THRESHOLD_EXT;
627decba6d4SFlorian Fainelli 		shift = (cs % 4) * bits;
628decba6d4SFlorian Fainelli 	} else if (ctrl->nand_version >= 0x0600) {
62927c5b17cSBrian Norris 		if (cs >= 5)
63027c5b17cSBrian Norris 			reg = BRCMNAND_CORR_THRESHOLD_EXT;
63127c5b17cSBrian Norris 		shift = (cs % 5) * bits;
63227c5b17cSBrian Norris 	}
63327c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
63427c5b17cSBrian Norris }
63527c5b17cSBrian Norris 
63627c5b17cSBrian Norris static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
63727c5b17cSBrian Norris {
638269ecf03SFlorian Fainelli 	if (ctrl->nand_version < 0x0602)
63927c5b17cSBrian Norris 		return 24;
64027c5b17cSBrian Norris 	return 0;
64127c5b17cSBrian Norris }
64227c5b17cSBrian Norris 
64327c5b17cSBrian Norris /***********************************************************************
64427c5b17cSBrian Norris  * NAND ACC CONTROL bitfield
64527c5b17cSBrian Norris  *
64627c5b17cSBrian Norris  * Some bits have remained constant throughout hardware revision, while
64727c5b17cSBrian Norris  * others have shifted around.
64827c5b17cSBrian Norris  ***********************************************************************/
64927c5b17cSBrian Norris 
65027c5b17cSBrian Norris /* Constant for all versions (where supported) */
65127c5b17cSBrian Norris enum {
65227c5b17cSBrian Norris 	/* See BRCMNAND_HAS_CACHE_MODE */
65327c5b17cSBrian Norris 	ACC_CONTROL_CACHE_MODE				= BIT(22),
65427c5b17cSBrian Norris 
65527c5b17cSBrian Norris 	/* See BRCMNAND_HAS_PREFETCH */
65627c5b17cSBrian Norris 	ACC_CONTROL_PREFETCH				= BIT(23),
65727c5b17cSBrian Norris 
65827c5b17cSBrian Norris 	ACC_CONTROL_PAGE_HIT				= BIT(24),
65927c5b17cSBrian Norris 	ACC_CONTROL_WR_PREEMPT				= BIT(25),
66027c5b17cSBrian Norris 	ACC_CONTROL_PARTIAL_PAGE			= BIT(26),
66127c5b17cSBrian Norris 	ACC_CONTROL_RD_ERASED				= BIT(27),
66227c5b17cSBrian Norris 	ACC_CONTROL_FAST_PGM_RDIN			= BIT(28),
66327c5b17cSBrian Norris 	ACC_CONTROL_WR_ECC				= BIT(30),
66427c5b17cSBrian Norris 	ACC_CONTROL_RD_ECC				= BIT(31),
66527c5b17cSBrian Norris };
66627c5b17cSBrian Norris 
66727c5b17cSBrian Norris static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
66827c5b17cSBrian Norris {
669decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
670decba6d4SFlorian Fainelli 		return GENMASK(7, 0);
671decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
67227c5b17cSBrian Norris 		return GENMASK(6, 0);
67327c5b17cSBrian Norris 	else
67427c5b17cSBrian Norris 		return GENMASK(5, 0);
67527c5b17cSBrian Norris }
67627c5b17cSBrian Norris 
67727c5b17cSBrian Norris #define NAND_ACC_CONTROL_ECC_SHIFT	16
678decba6d4SFlorian Fainelli #define NAND_ACC_CONTROL_ECC_EXT_SHIFT	13
67927c5b17cSBrian Norris 
68027c5b17cSBrian Norris static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
68127c5b17cSBrian Norris {
68227c5b17cSBrian Norris 	u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
68327c5b17cSBrian Norris 
684decba6d4SFlorian Fainelli 	mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
685decba6d4SFlorian Fainelli 
686decba6d4SFlorian Fainelli 	/* v7.2 includes additional ECC levels */
687decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
688decba6d4SFlorian Fainelli 		mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
689decba6d4SFlorian Fainelli 
690decba6d4SFlorian Fainelli 	return mask;
69127c5b17cSBrian Norris }
69227c5b17cSBrian Norris 
69327c5b17cSBrian Norris static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
69427c5b17cSBrian Norris {
69527c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
69627c5b17cSBrian Norris 	u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
69727c5b17cSBrian Norris 	u32 acc_control = nand_readreg(ctrl, offs);
69827c5b17cSBrian Norris 	u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
69927c5b17cSBrian Norris 
70027c5b17cSBrian Norris 	if (en) {
70127c5b17cSBrian Norris 		acc_control |= ecc_flags; /* enable RD/WR ECC */
70227c5b17cSBrian Norris 		acc_control |= host->hwcfg.ecc_level
70327c5b17cSBrian Norris 			       << NAND_ACC_CONTROL_ECC_SHIFT;
70427c5b17cSBrian Norris 	} else {
70527c5b17cSBrian Norris 		acc_control &= ~ecc_flags; /* disable RD/WR ECC */
70627c5b17cSBrian Norris 		acc_control &= ~brcmnand_ecc_level_mask(ctrl);
70727c5b17cSBrian Norris 	}
70827c5b17cSBrian Norris 
70927c5b17cSBrian Norris 	nand_writereg(ctrl, offs, acc_control);
71027c5b17cSBrian Norris }
71127c5b17cSBrian Norris 
71227c5b17cSBrian Norris static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
71327c5b17cSBrian Norris {
714decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
715decba6d4SFlorian Fainelli 		return 9;
716decba6d4SFlorian Fainelli 	else if (ctrl->nand_version >= 0x0600)
71727c5b17cSBrian Norris 		return 7;
71827c5b17cSBrian Norris 	else if (ctrl->nand_version >= 0x0500)
71927c5b17cSBrian Norris 		return 6;
72027c5b17cSBrian Norris 	else
72127c5b17cSBrian Norris 		return -1;
72227c5b17cSBrian Norris }
72327c5b17cSBrian Norris 
72427c5b17cSBrian Norris static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
72527c5b17cSBrian Norris {
72627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
72727c5b17cSBrian Norris 	int shift = brcmnand_sector_1k_shift(ctrl);
72827c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
72927c5b17cSBrian Norris 						  BRCMNAND_CS_ACC_CONTROL);
73027c5b17cSBrian Norris 
73127c5b17cSBrian Norris 	if (shift < 0)
73227c5b17cSBrian Norris 		return 0;
73327c5b17cSBrian Norris 
73427c5b17cSBrian Norris 	return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
73527c5b17cSBrian Norris }
73627c5b17cSBrian Norris 
73727c5b17cSBrian Norris static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
73827c5b17cSBrian Norris {
73927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
74027c5b17cSBrian Norris 	int shift = brcmnand_sector_1k_shift(ctrl);
74127c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
74227c5b17cSBrian Norris 						  BRCMNAND_CS_ACC_CONTROL);
74327c5b17cSBrian Norris 	u32 tmp;
74427c5b17cSBrian Norris 
74527c5b17cSBrian Norris 	if (shift < 0)
74627c5b17cSBrian Norris 		return;
74727c5b17cSBrian Norris 
74827c5b17cSBrian Norris 	tmp = nand_readreg(ctrl, acc_control_offs);
74927c5b17cSBrian Norris 	tmp &= ~(1 << shift);
75027c5b17cSBrian Norris 	tmp |= (!!val) << shift;
75127c5b17cSBrian Norris 	nand_writereg(ctrl, acc_control_offs, tmp);
75227c5b17cSBrian Norris }
75327c5b17cSBrian Norris 
75427c5b17cSBrian Norris /***********************************************************************
75527c5b17cSBrian Norris  * CS_NAND_SELECT
75627c5b17cSBrian Norris  ***********************************************************************/
75727c5b17cSBrian Norris 
75827c5b17cSBrian Norris enum {
75927c5b17cSBrian Norris 	CS_SELECT_NAND_WP			= BIT(29),
76027c5b17cSBrian Norris 	CS_SELECT_AUTO_DEVICE_ID_CFG		= BIT(30),
76127c5b17cSBrian Norris };
76227c5b17cSBrian Norris 
7639d2ee0a6SKamal Dasu static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
7649d2ee0a6SKamal Dasu 				    u32 mask, u32 expected_val,
7659d2ee0a6SKamal Dasu 				    unsigned long timeout_ms)
7669d2ee0a6SKamal Dasu {
7679d2ee0a6SKamal Dasu 	unsigned long limit;
7689d2ee0a6SKamal Dasu 	u32 val;
7699d2ee0a6SKamal Dasu 
7709d2ee0a6SKamal Dasu 	if (!timeout_ms)
7719d2ee0a6SKamal Dasu 		timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
7729d2ee0a6SKamal Dasu 
7739d2ee0a6SKamal Dasu 	limit = jiffies + msecs_to_jiffies(timeout_ms);
7749d2ee0a6SKamal Dasu 	do {
7759d2ee0a6SKamal Dasu 		val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
7769d2ee0a6SKamal Dasu 		if ((val & mask) == expected_val)
7779d2ee0a6SKamal Dasu 			return 0;
7789d2ee0a6SKamal Dasu 
7799d2ee0a6SKamal Dasu 		cpu_relax();
7809d2ee0a6SKamal Dasu 	} while (time_after(limit, jiffies));
7819d2ee0a6SKamal Dasu 
7829d2ee0a6SKamal Dasu 	dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
7839d2ee0a6SKamal Dasu 		 expected_val, val & mask);
7849d2ee0a6SKamal Dasu 
7859d2ee0a6SKamal Dasu 	return -ETIMEDOUT;
7869d2ee0a6SKamal Dasu }
7879d2ee0a6SKamal Dasu 
78827c5b17cSBrian Norris static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
78927c5b17cSBrian Norris {
79027c5b17cSBrian Norris 	u32 val = en ? CS_SELECT_NAND_WP : 0;
79127c5b17cSBrian Norris 
79227c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
79327c5b17cSBrian Norris }
79427c5b17cSBrian Norris 
79527c5b17cSBrian Norris /***********************************************************************
79627c5b17cSBrian Norris  * Flash DMA
79727c5b17cSBrian Norris  ***********************************************************************/
79827c5b17cSBrian Norris 
79927c5b17cSBrian Norris enum flash_dma_reg {
80027c5b17cSBrian Norris 	FLASH_DMA_REVISION		= 0x00,
80127c5b17cSBrian Norris 	FLASH_DMA_FIRST_DESC		= 0x04,
80227c5b17cSBrian Norris 	FLASH_DMA_FIRST_DESC_EXT	= 0x08,
80327c5b17cSBrian Norris 	FLASH_DMA_CTRL			= 0x0c,
80427c5b17cSBrian Norris 	FLASH_DMA_MODE			= 0x10,
80527c5b17cSBrian Norris 	FLASH_DMA_STATUS		= 0x14,
80627c5b17cSBrian Norris 	FLASH_DMA_INTERRUPT_DESC	= 0x18,
80727c5b17cSBrian Norris 	FLASH_DMA_INTERRUPT_DESC_EXT	= 0x1c,
80827c5b17cSBrian Norris 	FLASH_DMA_ERROR_STATUS		= 0x20,
80927c5b17cSBrian Norris 	FLASH_DMA_CURRENT_DESC		= 0x24,
81027c5b17cSBrian Norris 	FLASH_DMA_CURRENT_DESC_EXT	= 0x28,
81127c5b17cSBrian Norris };
81227c5b17cSBrian Norris 
81327c5b17cSBrian Norris static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
81427c5b17cSBrian Norris {
81527c5b17cSBrian Norris 	return ctrl->flash_dma_base;
81627c5b17cSBrian Norris }
81727c5b17cSBrian Norris 
81827c5b17cSBrian Norris static inline bool flash_dma_buf_ok(const void *buf)
81927c5b17cSBrian Norris {
82027c5b17cSBrian Norris 	return buf && !is_vmalloc_addr(buf) &&
82127c5b17cSBrian Norris 		likely(IS_ALIGNED((uintptr_t)buf, 4));
82227c5b17cSBrian Norris }
82327c5b17cSBrian Norris 
82427c5b17cSBrian Norris static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
82527c5b17cSBrian Norris 				    u32 val)
82627c5b17cSBrian Norris {
82727c5b17cSBrian Norris 	brcmnand_writel(val, ctrl->flash_dma_base + offs);
82827c5b17cSBrian Norris }
82927c5b17cSBrian Norris 
83027c5b17cSBrian Norris static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
83127c5b17cSBrian Norris {
83227c5b17cSBrian Norris 	return brcmnand_readl(ctrl->flash_dma_base + offs);
83327c5b17cSBrian Norris }
83427c5b17cSBrian Norris 
83527c5b17cSBrian Norris /* Low-level operation types: command, address, write, or read */
83627c5b17cSBrian Norris enum brcmnand_llop_type {
83727c5b17cSBrian Norris 	LL_OP_CMD,
83827c5b17cSBrian Norris 	LL_OP_ADDR,
83927c5b17cSBrian Norris 	LL_OP_WR,
84027c5b17cSBrian Norris 	LL_OP_RD,
84127c5b17cSBrian Norris };
84227c5b17cSBrian Norris 
84327c5b17cSBrian Norris /***********************************************************************
84427c5b17cSBrian Norris  * Internal support functions
84527c5b17cSBrian Norris  ***********************************************************************/
84627c5b17cSBrian Norris 
847decba6d4SFlorian Fainelli static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
848decba6d4SFlorian Fainelli 				  struct brcmnand_cfg *cfg)
84927c5b17cSBrian Norris {
850decba6d4SFlorian Fainelli 	if (ctrl->nand_version <= 0x0701)
85127c5b17cSBrian Norris 		return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
85227c5b17cSBrian Norris 			cfg->ecc_level == 15;
853decba6d4SFlorian Fainelli 	else
854decba6d4SFlorian Fainelli 		return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
855decba6d4SFlorian Fainelli 			cfg->ecc_level == 15) ||
856decba6d4SFlorian Fainelli 			(cfg->spare_area_size == 28 && cfg->ecc_level == 16));
85727c5b17cSBrian Norris }
85827c5b17cSBrian Norris 
85927c5b17cSBrian Norris /*
860ef5eeea6SBoris Brezillon  * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
861ef5eeea6SBoris Brezillon  * the layout/configuration.
862ef5eeea6SBoris Brezillon  * Returns -ERRCODE on failure.
86327c5b17cSBrian Norris  */
864ef5eeea6SBoris Brezillon static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
865ef5eeea6SBoris Brezillon 					  struct mtd_oob_region *oobregion)
86627c5b17cSBrian Norris {
867ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
868ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
86927c5b17cSBrian Norris 	struct brcmnand_cfg *cfg = &host->hwcfg;
870ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
871ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
87227c5b17cSBrian Norris 
873ef5eeea6SBoris Brezillon 	if (section >= sectors)
874ef5eeea6SBoris Brezillon 		return -ERANGE;
87527c5b17cSBrian Norris 
876ef5eeea6SBoris Brezillon 	oobregion->offset = (section * sas) + 6;
877ef5eeea6SBoris Brezillon 	oobregion->length = 3;
87827c5b17cSBrian Norris 
879ef5eeea6SBoris Brezillon 	return 0;
880ef5eeea6SBoris Brezillon }
881ef5eeea6SBoris Brezillon 
882ef5eeea6SBoris Brezillon static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
883ef5eeea6SBoris Brezillon 					   struct mtd_oob_region *oobregion)
884ef5eeea6SBoris Brezillon {
885ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
886ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
887ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
888ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
889ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
890ef5eeea6SBoris Brezillon 
891ef5eeea6SBoris Brezillon 	if (section >= sectors * 2)
892ef5eeea6SBoris Brezillon 		return -ERANGE;
893ef5eeea6SBoris Brezillon 
894ef5eeea6SBoris Brezillon 	oobregion->offset = (section / 2) * sas;
895ef5eeea6SBoris Brezillon 
896ef5eeea6SBoris Brezillon 	if (section & 1) {
897ef5eeea6SBoris Brezillon 		oobregion->offset += 9;
898ef5eeea6SBoris Brezillon 		oobregion->length = 7;
89927c5b17cSBrian Norris 	} else {
900ef5eeea6SBoris Brezillon 		oobregion->length = 6;
901ef5eeea6SBoris Brezillon 
902ef5eeea6SBoris Brezillon 		/* First sector of each page may have BBI */
903ef5eeea6SBoris Brezillon 		if (!section) {
904ef5eeea6SBoris Brezillon 			/*
905ef5eeea6SBoris Brezillon 			 * Small-page NAND use byte 6 for BBI while large-page
906ef5eeea6SBoris Brezillon 			 * NAND use byte 0.
907ef5eeea6SBoris Brezillon 			 */
908ef5eeea6SBoris Brezillon 			if (cfg->page_size > 512)
909ef5eeea6SBoris Brezillon 				oobregion->offset++;
910ef5eeea6SBoris Brezillon 			oobregion->length--;
91127c5b17cSBrian Norris 		}
91227c5b17cSBrian Norris 	}
913f5b8aa78SBoris BREZILLON 
914ef5eeea6SBoris Brezillon 	return 0;
915ef5eeea6SBoris Brezillon }
916ef5eeea6SBoris Brezillon 
917ef5eeea6SBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
918ef5eeea6SBoris Brezillon 	.ecc = brcmnand_hamming_ooblayout_ecc,
919ef5eeea6SBoris Brezillon 	.free = brcmnand_hamming_ooblayout_free,
920ef5eeea6SBoris Brezillon };
921ef5eeea6SBoris Brezillon 
922ef5eeea6SBoris Brezillon static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
923ef5eeea6SBoris Brezillon 				      struct mtd_oob_region *oobregion)
924ef5eeea6SBoris Brezillon {
925ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
926ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
927ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
928ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
929ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
930ef5eeea6SBoris Brezillon 
931ef5eeea6SBoris Brezillon 	if (section >= sectors)
932ef5eeea6SBoris Brezillon 		return -ERANGE;
933ef5eeea6SBoris Brezillon 
934ef5eeea6SBoris Brezillon 	oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
935ef5eeea6SBoris Brezillon 	oobregion->length = chip->ecc.bytes;
936ef5eeea6SBoris Brezillon 
937ef5eeea6SBoris Brezillon 	return 0;
938ef5eeea6SBoris Brezillon }
939ef5eeea6SBoris Brezillon 
940ef5eeea6SBoris Brezillon static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
941ef5eeea6SBoris Brezillon 					  struct mtd_oob_region *oobregion)
942ef5eeea6SBoris Brezillon {
943ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
944ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
945ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
946ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
947ef5eeea6SBoris Brezillon 	int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
948ef5eeea6SBoris Brezillon 
949ef5eeea6SBoris Brezillon 	if (section >= sectors)
950ef5eeea6SBoris Brezillon 		return -ERANGE;
951ef5eeea6SBoris Brezillon 
952ef5eeea6SBoris Brezillon 	if (sas <= chip->ecc.bytes)
953ef5eeea6SBoris Brezillon 		return 0;
954ef5eeea6SBoris Brezillon 
955ef5eeea6SBoris Brezillon 	oobregion->offset = section * sas;
956ef5eeea6SBoris Brezillon 	oobregion->length = sas - chip->ecc.bytes;
957ef5eeea6SBoris Brezillon 
958ef5eeea6SBoris Brezillon 	if (!section) {
959ef5eeea6SBoris Brezillon 		oobregion->offset++;
960ef5eeea6SBoris Brezillon 		oobregion->length--;
961ef5eeea6SBoris Brezillon 	}
962ef5eeea6SBoris Brezillon 
963ef5eeea6SBoris Brezillon 	return 0;
964ef5eeea6SBoris Brezillon }
965ef5eeea6SBoris Brezillon 
966ef5eeea6SBoris Brezillon static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
967ef5eeea6SBoris Brezillon 					  struct mtd_oob_region *oobregion)
968ef5eeea6SBoris Brezillon {
969ef5eeea6SBoris Brezillon 	struct nand_chip *chip = mtd_to_nand(mtd);
970ef5eeea6SBoris Brezillon 	struct brcmnand_host *host = nand_get_controller_data(chip);
971ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *cfg = &host->hwcfg;
972ef5eeea6SBoris Brezillon 	int sas = cfg->spare_area_size << cfg->sector_size_1k;
973ef5eeea6SBoris Brezillon 
974ef5eeea6SBoris Brezillon 	if (section > 1 || sas - chip->ecc.bytes < 6 ||
975ef5eeea6SBoris Brezillon 	    (section && sas - chip->ecc.bytes == 6))
976ef5eeea6SBoris Brezillon 		return -ERANGE;
977ef5eeea6SBoris Brezillon 
978ef5eeea6SBoris Brezillon 	if (!section) {
979ef5eeea6SBoris Brezillon 		oobregion->offset = 0;
980ef5eeea6SBoris Brezillon 		oobregion->length = 5;
981ef5eeea6SBoris Brezillon 	} else {
982ef5eeea6SBoris Brezillon 		oobregion->offset = 6;
983ef5eeea6SBoris Brezillon 		oobregion->length = sas - chip->ecc.bytes - 6;
984ef5eeea6SBoris Brezillon 	}
985ef5eeea6SBoris Brezillon 
986ef5eeea6SBoris Brezillon 	return 0;
987ef5eeea6SBoris Brezillon }
988ef5eeea6SBoris Brezillon 
989ef5eeea6SBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
990ef5eeea6SBoris Brezillon 	.ecc = brcmnand_bch_ooblayout_ecc,
991ef5eeea6SBoris Brezillon 	.free = brcmnand_bch_ooblayout_free_lp,
992ef5eeea6SBoris Brezillon };
993ef5eeea6SBoris Brezillon 
994ef5eeea6SBoris Brezillon static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
995ef5eeea6SBoris Brezillon 	.ecc = brcmnand_bch_ooblayout_ecc,
996ef5eeea6SBoris Brezillon 	.free = brcmnand_bch_ooblayout_free_sp,
997ef5eeea6SBoris Brezillon };
998ef5eeea6SBoris Brezillon 
999ef5eeea6SBoris Brezillon static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
1000ef5eeea6SBoris Brezillon {
1001ef5eeea6SBoris Brezillon 	struct brcmnand_cfg *p = &host->hwcfg;
1002ef5eeea6SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
1003ef5eeea6SBoris Brezillon 	struct nand_ecc_ctrl *ecc = &host->chip.ecc;
1004ef5eeea6SBoris Brezillon 	unsigned int ecc_level = p->ecc_level;
1005ef5eeea6SBoris Brezillon 	int sas = p->spare_area_size << p->sector_size_1k;
1006ef5eeea6SBoris Brezillon 	int sectors = p->page_size / (512 << p->sector_size_1k);
1007ef5eeea6SBoris Brezillon 
1008ef5eeea6SBoris Brezillon 	if (p->sector_size_1k)
1009ef5eeea6SBoris Brezillon 		ecc_level <<= 1;
1010ef5eeea6SBoris Brezillon 
1011decba6d4SFlorian Fainelli 	if (is_hamming_ecc(host->ctrl, p)) {
1012ef5eeea6SBoris Brezillon 		ecc->bytes = 3 * sectors;
1013ef5eeea6SBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
1014ef5eeea6SBoris Brezillon 		return 0;
101527c5b17cSBrian Norris 	}
101627c5b17cSBrian Norris 
101727c5b17cSBrian Norris 	/*
101827c5b17cSBrian Norris 	 * CONTROLLER_VERSION:
101927c5b17cSBrian Norris 	 *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
102027c5b17cSBrian Norris 	 *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
102127c5b17cSBrian Norris 	 * But we will just be conservative.
102227c5b17cSBrian Norris 	 */
1023ef5eeea6SBoris Brezillon 	ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
1024ef5eeea6SBoris Brezillon 	if (p->page_size == 512)
1025ef5eeea6SBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
1026ef5eeea6SBoris Brezillon 	else
1027ef5eeea6SBoris Brezillon 		mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
1028ef5eeea6SBoris Brezillon 
1029ef5eeea6SBoris Brezillon 	if (ecc->bytes >= sas) {
103027c5b17cSBrian Norris 		dev_err(&host->pdev->dev,
103127c5b17cSBrian Norris 			"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
1032ef5eeea6SBoris Brezillon 			ecc->bytes, sas);
1033ef5eeea6SBoris Brezillon 		return -EINVAL;
103427c5b17cSBrian Norris 	}
103527c5b17cSBrian Norris 
1036ef5eeea6SBoris Brezillon 	return 0;
103727c5b17cSBrian Norris }
103827c5b17cSBrian Norris 
103927c5b17cSBrian Norris static void brcmnand_wp(struct mtd_info *mtd, int wp)
104027c5b17cSBrian Norris {
10414bd4ebccSBoris BREZILLON 	struct nand_chip *chip = mtd_to_nand(mtd);
1042d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
104327c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
104427c5b17cSBrian Norris 
104527c5b17cSBrian Norris 	if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
104627c5b17cSBrian Norris 		static int old_wp = -1;
10479d2ee0a6SKamal Dasu 		int ret;
104827c5b17cSBrian Norris 
104927c5b17cSBrian Norris 		if (old_wp != wp) {
105027c5b17cSBrian Norris 			dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
105127c5b17cSBrian Norris 			old_wp = wp;
105227c5b17cSBrian Norris 		}
10539d2ee0a6SKamal Dasu 
10549d2ee0a6SKamal Dasu 		/*
10559d2ee0a6SKamal Dasu 		 * make sure ctrl/flash ready before and after
10569d2ee0a6SKamal Dasu 		 * changing state of #WP pin
10579d2ee0a6SKamal Dasu 		 */
10589d2ee0a6SKamal Dasu 		ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
10599d2ee0a6SKamal Dasu 					       NAND_STATUS_READY,
10609d2ee0a6SKamal Dasu 					       NAND_CTRL_RDY |
10619d2ee0a6SKamal Dasu 					       NAND_STATUS_READY, 0);
10629d2ee0a6SKamal Dasu 		if (ret)
10639d2ee0a6SKamal Dasu 			return;
10649d2ee0a6SKamal Dasu 
106527c5b17cSBrian Norris 		brcmnand_set_wp(ctrl, wp);
106697d90da8SBoris Brezillon 		nand_status_op(chip, NULL);
10679d2ee0a6SKamal Dasu 		/* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
10689d2ee0a6SKamal Dasu 		ret = bcmnand_ctrl_poll_status(ctrl,
10699d2ee0a6SKamal Dasu 					       NAND_CTRL_RDY |
10709d2ee0a6SKamal Dasu 					       NAND_STATUS_READY |
10719d2ee0a6SKamal Dasu 					       NAND_STATUS_WP,
10729d2ee0a6SKamal Dasu 					       NAND_CTRL_RDY |
10739d2ee0a6SKamal Dasu 					       NAND_STATUS_READY |
10749d2ee0a6SKamal Dasu 					       (wp ? 0 : NAND_STATUS_WP), 0);
10759d2ee0a6SKamal Dasu 
10769d2ee0a6SKamal Dasu 		if (ret)
10779d2ee0a6SKamal Dasu 			dev_err_ratelimited(&host->pdev->dev,
10789d2ee0a6SKamal Dasu 					    "nand #WP expected %s\n",
10799d2ee0a6SKamal Dasu 					    wp ? "on" : "off");
108027c5b17cSBrian Norris 	}
108127c5b17cSBrian Norris }
108227c5b17cSBrian Norris 
108327c5b17cSBrian Norris /* Helper functions for reading and writing OOB registers */
108427c5b17cSBrian Norris static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
108527c5b17cSBrian Norris {
108627c5b17cSBrian Norris 	u16 offset0, offset10, reg_offs;
108727c5b17cSBrian Norris 
108827c5b17cSBrian Norris 	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
108927c5b17cSBrian Norris 	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
109027c5b17cSBrian Norris 
109127c5b17cSBrian Norris 	if (offs >= ctrl->max_oob)
109227c5b17cSBrian Norris 		return 0x77;
109327c5b17cSBrian Norris 
109427c5b17cSBrian Norris 	if (offs >= 16 && offset10)
109527c5b17cSBrian Norris 		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
109627c5b17cSBrian Norris 	else
109727c5b17cSBrian Norris 		reg_offs = offset0 + (offs & ~0x03);
109827c5b17cSBrian Norris 
109927c5b17cSBrian Norris 	return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
110027c5b17cSBrian Norris }
110127c5b17cSBrian Norris 
110227c5b17cSBrian Norris static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
110327c5b17cSBrian Norris 				 u32 data)
110427c5b17cSBrian Norris {
110527c5b17cSBrian Norris 	u16 offset0, offset10, reg_offs;
110627c5b17cSBrian Norris 
110727c5b17cSBrian Norris 	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
110827c5b17cSBrian Norris 	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
110927c5b17cSBrian Norris 
111027c5b17cSBrian Norris 	if (offs >= ctrl->max_oob)
111127c5b17cSBrian Norris 		return;
111227c5b17cSBrian Norris 
111327c5b17cSBrian Norris 	if (offs >= 16 && offset10)
111427c5b17cSBrian Norris 		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
111527c5b17cSBrian Norris 	else
111627c5b17cSBrian Norris 		reg_offs = offset0 + (offs & ~0x03);
111727c5b17cSBrian Norris 
111827c5b17cSBrian Norris 	nand_writereg(ctrl, reg_offs, data);
111927c5b17cSBrian Norris }
112027c5b17cSBrian Norris 
112127c5b17cSBrian Norris /*
112227c5b17cSBrian Norris  * read_oob_from_regs - read data from OOB registers
112327c5b17cSBrian Norris  * @ctrl: NAND controller
112427c5b17cSBrian Norris  * @i: sub-page sector index
112527c5b17cSBrian Norris  * @oob: buffer to read to
112627c5b17cSBrian Norris  * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
112727c5b17cSBrian Norris  * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
112827c5b17cSBrian Norris  */
112927c5b17cSBrian Norris static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
113027c5b17cSBrian Norris 			      int sas, int sector_1k)
113127c5b17cSBrian Norris {
113227c5b17cSBrian Norris 	int tbytes = sas << sector_1k;
113327c5b17cSBrian Norris 	int j;
113427c5b17cSBrian Norris 
113527c5b17cSBrian Norris 	/* Adjust OOB values for 1K sector size */
113627c5b17cSBrian Norris 	if (sector_1k && (i & 0x01))
113727c5b17cSBrian Norris 		tbytes = max(0, tbytes - (int)ctrl->max_oob);
113827c5b17cSBrian Norris 	tbytes = min_t(int, tbytes, ctrl->max_oob);
113927c5b17cSBrian Norris 
114027c5b17cSBrian Norris 	for (j = 0; j < tbytes; j++)
114127c5b17cSBrian Norris 		oob[j] = oob_reg_read(ctrl, j);
114227c5b17cSBrian Norris 	return tbytes;
114327c5b17cSBrian Norris }
114427c5b17cSBrian Norris 
114527c5b17cSBrian Norris /*
114627c5b17cSBrian Norris  * write_oob_to_regs - write data to OOB registers
114727c5b17cSBrian Norris  * @i: sub-page sector index
114827c5b17cSBrian Norris  * @oob: buffer to write from
114927c5b17cSBrian Norris  * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
115027c5b17cSBrian Norris  * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
115127c5b17cSBrian Norris  */
115227c5b17cSBrian Norris static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
115327c5b17cSBrian Norris 			     const u8 *oob, int sas, int sector_1k)
115427c5b17cSBrian Norris {
115527c5b17cSBrian Norris 	int tbytes = sas << sector_1k;
115627c5b17cSBrian Norris 	int j;
115727c5b17cSBrian Norris 
115827c5b17cSBrian Norris 	/* Adjust OOB values for 1K sector size */
115927c5b17cSBrian Norris 	if (sector_1k && (i & 0x01))
116027c5b17cSBrian Norris 		tbytes = max(0, tbytes - (int)ctrl->max_oob);
116127c5b17cSBrian Norris 	tbytes = min_t(int, tbytes, ctrl->max_oob);
116227c5b17cSBrian Norris 
116327c5b17cSBrian Norris 	for (j = 0; j < tbytes; j += 4)
116427c5b17cSBrian Norris 		oob_reg_write(ctrl, j,
116527c5b17cSBrian Norris 				(oob[j + 0] << 24) |
116627c5b17cSBrian Norris 				(oob[j + 1] << 16) |
116727c5b17cSBrian Norris 				(oob[j + 2] <<  8) |
116827c5b17cSBrian Norris 				(oob[j + 3] <<  0));
116927c5b17cSBrian Norris 	return tbytes;
117027c5b17cSBrian Norris }
117127c5b17cSBrian Norris 
117227c5b17cSBrian Norris static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
117327c5b17cSBrian Norris {
117427c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = data;
117527c5b17cSBrian Norris 
117627c5b17cSBrian Norris 	/* Discard all NAND_CTLRDY interrupts during DMA */
117727c5b17cSBrian Norris 	if (ctrl->dma_pending)
117827c5b17cSBrian Norris 		return IRQ_HANDLED;
117927c5b17cSBrian Norris 
118027c5b17cSBrian Norris 	complete(&ctrl->done);
118127c5b17cSBrian Norris 	return IRQ_HANDLED;
118227c5b17cSBrian Norris }
118327c5b17cSBrian Norris 
1184c26211d3SBrian Norris /* Handle SoC-specific interrupt hardware */
1185c26211d3SBrian Norris static irqreturn_t brcmnand_irq(int irq, void *data)
1186c26211d3SBrian Norris {
1187c26211d3SBrian Norris 	struct brcmnand_controller *ctrl = data;
1188c26211d3SBrian Norris 
1189c26211d3SBrian Norris 	if (ctrl->soc->ctlrdy_ack(ctrl->soc))
1190c26211d3SBrian Norris 		return brcmnand_ctlrdy_irq(irq, data);
1191c26211d3SBrian Norris 
1192c26211d3SBrian Norris 	return IRQ_NONE;
1193c26211d3SBrian Norris }
1194c26211d3SBrian Norris 
119527c5b17cSBrian Norris static irqreturn_t brcmnand_dma_irq(int irq, void *data)
119627c5b17cSBrian Norris {
119727c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = data;
119827c5b17cSBrian Norris 
119927c5b17cSBrian Norris 	complete(&ctrl->dma_done);
120027c5b17cSBrian Norris 
120127c5b17cSBrian Norris 	return IRQ_HANDLED;
120227c5b17cSBrian Norris }
120327c5b17cSBrian Norris 
120427c5b17cSBrian Norris static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
120527c5b17cSBrian Norris {
120627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
12079d2ee0a6SKamal Dasu 	int ret;
120827c5b17cSBrian Norris 
120927c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
121027c5b17cSBrian Norris 		brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
121127c5b17cSBrian Norris 	BUG_ON(ctrl->cmd_pending != 0);
121227c5b17cSBrian Norris 	ctrl->cmd_pending = cmd;
121327c5b17cSBrian Norris 
12149d2ee0a6SKamal Dasu 	ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
12159d2ee0a6SKamal Dasu 	WARN_ON(ret);
121627c5b17cSBrian Norris 
121727c5b17cSBrian Norris 	mb(); /* flush previous writes */
121827c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
121927c5b17cSBrian Norris 			   cmd << brcmnand_cmd_shift(ctrl));
122027c5b17cSBrian Norris }
122127c5b17cSBrian Norris 
122227c5b17cSBrian Norris /***********************************************************************
122327c5b17cSBrian Norris  * NAND MTD API: read/program/erase
122427c5b17cSBrian Norris  ***********************************************************************/
122527c5b17cSBrian Norris 
12260f808c16SBoris Brezillon static void brcmnand_cmd_ctrl(struct nand_chip *chip, int dat,
122727c5b17cSBrian Norris 			      unsigned int ctrl)
122827c5b17cSBrian Norris {
122927c5b17cSBrian Norris 	/* intentionally left blank */
123027c5b17cSBrian Norris }
123127c5b17cSBrian Norris 
1232f1d46942SBoris Brezillon static int brcmnand_waitfunc(struct nand_chip *chip)
123327c5b17cSBrian Norris {
1234d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
123527c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
123627c5b17cSBrian Norris 	unsigned long timeo = msecs_to_jiffies(100);
123727c5b17cSBrian Norris 
123827c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
123927c5b17cSBrian Norris 	if (ctrl->cmd_pending &&
124027c5b17cSBrian Norris 			wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
124127c5b17cSBrian Norris 		u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
124227c5b17cSBrian Norris 					>> brcmnand_cmd_shift(ctrl);
124327c5b17cSBrian Norris 
124427c5b17cSBrian Norris 		dev_err_ratelimited(ctrl->dev,
124527c5b17cSBrian Norris 			"timeout waiting for command %#02x\n", cmd);
124627c5b17cSBrian Norris 		dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
124727c5b17cSBrian Norris 			brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
124827c5b17cSBrian Norris 	}
124927c5b17cSBrian Norris 	ctrl->cmd_pending = 0;
125027c5b17cSBrian Norris 	return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
125127c5b17cSBrian Norris 				 INTFC_FLASH_STATUS;
125227c5b17cSBrian Norris }
125327c5b17cSBrian Norris 
125427c5b17cSBrian Norris enum {
125527c5b17cSBrian Norris 	LLOP_RE				= BIT(16),
125627c5b17cSBrian Norris 	LLOP_WE				= BIT(17),
125727c5b17cSBrian Norris 	LLOP_ALE			= BIT(18),
125827c5b17cSBrian Norris 	LLOP_CLE			= BIT(19),
125927c5b17cSBrian Norris 	LLOP_RETURN_IDLE		= BIT(31),
126027c5b17cSBrian Norris 
126127c5b17cSBrian Norris 	LLOP_DATA_MASK			= GENMASK(15, 0),
126227c5b17cSBrian Norris };
126327c5b17cSBrian Norris 
126427c5b17cSBrian Norris static int brcmnand_low_level_op(struct brcmnand_host *host,
126527c5b17cSBrian Norris 				 enum brcmnand_llop_type type, u32 data,
126627c5b17cSBrian Norris 				 bool last_op)
126727c5b17cSBrian Norris {
126827c5b17cSBrian Norris 	struct nand_chip *chip = &host->chip;
126927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
127027c5b17cSBrian Norris 	u32 tmp;
127127c5b17cSBrian Norris 
127227c5b17cSBrian Norris 	tmp = data & LLOP_DATA_MASK;
127327c5b17cSBrian Norris 	switch (type) {
127427c5b17cSBrian Norris 	case LL_OP_CMD:
127527c5b17cSBrian Norris 		tmp |= LLOP_WE | LLOP_CLE;
127627c5b17cSBrian Norris 		break;
127727c5b17cSBrian Norris 	case LL_OP_ADDR:
127827c5b17cSBrian Norris 		/* WE | ALE */
127927c5b17cSBrian Norris 		tmp |= LLOP_WE | LLOP_ALE;
128027c5b17cSBrian Norris 		break;
128127c5b17cSBrian Norris 	case LL_OP_WR:
128227c5b17cSBrian Norris 		/* WE */
128327c5b17cSBrian Norris 		tmp |= LLOP_WE;
128427c5b17cSBrian Norris 		break;
128527c5b17cSBrian Norris 	case LL_OP_RD:
128627c5b17cSBrian Norris 		/* RE */
128727c5b17cSBrian Norris 		tmp |= LLOP_RE;
128827c5b17cSBrian Norris 		break;
128927c5b17cSBrian Norris 	}
129027c5b17cSBrian Norris 	if (last_op)
129127c5b17cSBrian Norris 		/* RETURN_IDLE */
129227c5b17cSBrian Norris 		tmp |= LLOP_RETURN_IDLE;
129327c5b17cSBrian Norris 
129427c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
129527c5b17cSBrian Norris 
129627c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
129727c5b17cSBrian Norris 	(void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
129827c5b17cSBrian Norris 
129927c5b17cSBrian Norris 	brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
1300f1d46942SBoris Brezillon 	return brcmnand_waitfunc(chip);
130127c5b17cSBrian Norris }
130227c5b17cSBrian Norris 
13035295cf2eSBoris Brezillon static void brcmnand_cmdfunc(struct nand_chip *chip, unsigned command,
130427c5b17cSBrian Norris 			     int column, int page_addr)
130527c5b17cSBrian Norris {
13065295cf2eSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1307d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
130827c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
130927c5b17cSBrian Norris 	u64 addr = (u64)page_addr << chip->page_shift;
131027c5b17cSBrian Norris 	int native_cmd = 0;
131127c5b17cSBrian Norris 
131227c5b17cSBrian Norris 	if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
131327c5b17cSBrian Norris 			command == NAND_CMD_RNDOUT)
131427c5b17cSBrian Norris 		addr = (u64)column;
131527c5b17cSBrian Norris 	/* Avoid propagating a negative, don't-care address */
131627c5b17cSBrian Norris 	else if (page_addr < 0)
131727c5b17cSBrian Norris 		addr = 0;
131827c5b17cSBrian Norris 
131927c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
132027c5b17cSBrian Norris 		(unsigned long long)addr);
132127c5b17cSBrian Norris 
132227c5b17cSBrian Norris 	host->last_cmd = command;
132327c5b17cSBrian Norris 	host->last_byte = 0;
132427c5b17cSBrian Norris 	host->last_addr = addr;
132527c5b17cSBrian Norris 
132627c5b17cSBrian Norris 	switch (command) {
132727c5b17cSBrian Norris 	case NAND_CMD_RESET:
132827c5b17cSBrian Norris 		native_cmd = CMD_FLASH_RESET;
132927c5b17cSBrian Norris 		break;
133027c5b17cSBrian Norris 	case NAND_CMD_STATUS:
133127c5b17cSBrian Norris 		native_cmd = CMD_STATUS_READ;
133227c5b17cSBrian Norris 		break;
133327c5b17cSBrian Norris 	case NAND_CMD_READID:
133427c5b17cSBrian Norris 		native_cmd = CMD_DEVICE_ID_READ;
133527c5b17cSBrian Norris 		break;
133627c5b17cSBrian Norris 	case NAND_CMD_READOOB:
133727c5b17cSBrian Norris 		native_cmd = CMD_SPARE_AREA_READ;
133827c5b17cSBrian Norris 		break;
133927c5b17cSBrian Norris 	case NAND_CMD_ERASE1:
134027c5b17cSBrian Norris 		native_cmd = CMD_BLOCK_ERASE;
134127c5b17cSBrian Norris 		brcmnand_wp(mtd, 0);
134227c5b17cSBrian Norris 		break;
134327c5b17cSBrian Norris 	case NAND_CMD_PARAM:
134427c5b17cSBrian Norris 		native_cmd = CMD_PARAMETER_READ;
134527c5b17cSBrian Norris 		break;
134627c5b17cSBrian Norris 	case NAND_CMD_SET_FEATURES:
134727c5b17cSBrian Norris 	case NAND_CMD_GET_FEATURES:
134827c5b17cSBrian Norris 		brcmnand_low_level_op(host, LL_OP_CMD, command, false);
134927c5b17cSBrian Norris 		brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
135027c5b17cSBrian Norris 		break;
135127c5b17cSBrian Norris 	case NAND_CMD_RNDOUT:
135227c5b17cSBrian Norris 		native_cmd = CMD_PARAMETER_CHANGE_COL;
135327c5b17cSBrian Norris 		addr &= ~((u64)(FC_BYTES - 1));
135427c5b17cSBrian Norris 		/*
135527c5b17cSBrian Norris 		 * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
135627c5b17cSBrian Norris 		 * NB: hwcfg.sector_size_1k may not be initialized yet
135727c5b17cSBrian Norris 		 */
135827c5b17cSBrian Norris 		if (brcmnand_get_sector_size_1k(host)) {
135927c5b17cSBrian Norris 			host->hwcfg.sector_size_1k =
136027c5b17cSBrian Norris 				brcmnand_get_sector_size_1k(host);
136127c5b17cSBrian Norris 			brcmnand_set_sector_size_1k(host, 0);
136227c5b17cSBrian Norris 		}
136327c5b17cSBrian Norris 		break;
136427c5b17cSBrian Norris 	}
136527c5b17cSBrian Norris 
136627c5b17cSBrian Norris 	if (!native_cmd)
136727c5b17cSBrian Norris 		return;
136827c5b17cSBrian Norris 
136927c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
137027c5b17cSBrian Norris 		(host->cs << 16) | ((addr >> 32) & 0xffff));
137127c5b17cSBrian Norris 	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
137227c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr));
137327c5b17cSBrian Norris 	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
137427c5b17cSBrian Norris 
137527c5b17cSBrian Norris 	brcmnand_send_cmd(host, native_cmd);
1376f1d46942SBoris Brezillon 	brcmnand_waitfunc(chip);
137727c5b17cSBrian Norris 
137827c5b17cSBrian Norris 	if (native_cmd == CMD_PARAMETER_READ ||
137927c5b17cSBrian Norris 			native_cmd == CMD_PARAMETER_CHANGE_COL) {
1380d618baf9SBrian Norris 		/* Copy flash cache word-wise */
1381d618baf9SBrian Norris 		u32 *flash_cache = (u32 *)ctrl->flash_cache;
138227c5b17cSBrian Norris 		int i;
1383c26211d3SBrian Norris 
1384eab7fdc7SRay Jui 		brcmnand_soc_data_bus_prepare(ctrl->soc, true);
1385c26211d3SBrian Norris 
138627c5b17cSBrian Norris 		/*
138727c5b17cSBrian Norris 		 * Must cache the FLASH_CACHE now, since changes in
138827c5b17cSBrian Norris 		 * SECTOR_SIZE_1K may invalidate it
138927c5b17cSBrian Norris 		 */
139027c5b17cSBrian Norris 		for (i = 0; i < FC_WORDS; i++)
1391d618baf9SBrian Norris 			/*
1392d618baf9SBrian Norris 			 * Flash cache is big endian for parameter pages, at
1393d618baf9SBrian Norris 			 * least on STB SoCs
1394d618baf9SBrian Norris 			 */
1395d618baf9SBrian Norris 			flash_cache[i] = be32_to_cpu(brcmnand_read_fc(ctrl, i));
1396c26211d3SBrian Norris 
1397eab7fdc7SRay Jui 		brcmnand_soc_data_bus_unprepare(ctrl->soc, true);
1398c26211d3SBrian Norris 
139927c5b17cSBrian Norris 		/* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
140027c5b17cSBrian Norris 		if (host->hwcfg.sector_size_1k)
140127c5b17cSBrian Norris 			brcmnand_set_sector_size_1k(host,
140227c5b17cSBrian Norris 						    host->hwcfg.sector_size_1k);
140327c5b17cSBrian Norris 	}
140427c5b17cSBrian Norris 
140527c5b17cSBrian Norris 	/* Re-enable protection is necessary only after erase */
140627c5b17cSBrian Norris 	if (command == NAND_CMD_ERASE1)
140727c5b17cSBrian Norris 		brcmnand_wp(mtd, 1);
140827c5b17cSBrian Norris }
140927c5b17cSBrian Norris 
14107e534323SBoris Brezillon static uint8_t brcmnand_read_byte(struct nand_chip *chip)
141127c5b17cSBrian Norris {
1412d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
141327c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
141427c5b17cSBrian Norris 	uint8_t ret = 0;
141527c5b17cSBrian Norris 	int addr, offs;
141627c5b17cSBrian Norris 
141727c5b17cSBrian Norris 	switch (host->last_cmd) {
141827c5b17cSBrian Norris 	case NAND_CMD_READID:
141927c5b17cSBrian Norris 		if (host->last_byte < 4)
142027c5b17cSBrian Norris 			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
142127c5b17cSBrian Norris 				(24 - (host->last_byte << 3));
142227c5b17cSBrian Norris 		else if (host->last_byte < 8)
142327c5b17cSBrian Norris 			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
142427c5b17cSBrian Norris 				(56 - (host->last_byte << 3));
142527c5b17cSBrian Norris 		break;
142627c5b17cSBrian Norris 
142727c5b17cSBrian Norris 	case NAND_CMD_READOOB:
142827c5b17cSBrian Norris 		ret = oob_reg_read(ctrl, host->last_byte);
142927c5b17cSBrian Norris 		break;
143027c5b17cSBrian Norris 
143127c5b17cSBrian Norris 	case NAND_CMD_STATUS:
143227c5b17cSBrian Norris 		ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
143327c5b17cSBrian Norris 					INTFC_FLASH_STATUS;
143427c5b17cSBrian Norris 		if (wp_on) /* hide WP status */
143527c5b17cSBrian Norris 			ret |= NAND_STATUS_WP;
143627c5b17cSBrian Norris 		break;
143727c5b17cSBrian Norris 
143827c5b17cSBrian Norris 	case NAND_CMD_PARAM:
143927c5b17cSBrian Norris 	case NAND_CMD_RNDOUT:
144027c5b17cSBrian Norris 		addr = host->last_addr + host->last_byte;
144127c5b17cSBrian Norris 		offs = addr & (FC_BYTES - 1);
144227c5b17cSBrian Norris 
144327c5b17cSBrian Norris 		/* At FC_BYTES boundary, switch to next column */
144427c5b17cSBrian Norris 		if (host->last_byte > 0 && offs == 0)
144597d90da8SBoris Brezillon 			nand_change_read_column_op(chip, addr, NULL, 0, false);
144627c5b17cSBrian Norris 
1447d618baf9SBrian Norris 		ret = ctrl->flash_cache[offs];
144827c5b17cSBrian Norris 		break;
144927c5b17cSBrian Norris 	case NAND_CMD_GET_FEATURES:
145027c5b17cSBrian Norris 		if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
145127c5b17cSBrian Norris 			ret = 0;
145227c5b17cSBrian Norris 		} else {
145327c5b17cSBrian Norris 			bool last = host->last_byte ==
145427c5b17cSBrian Norris 				ONFI_SUBFEATURE_PARAM_LEN - 1;
145527c5b17cSBrian Norris 			brcmnand_low_level_op(host, LL_OP_RD, 0, last);
145627c5b17cSBrian Norris 			ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
145727c5b17cSBrian Norris 		}
145827c5b17cSBrian Norris 	}
145927c5b17cSBrian Norris 
146027c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
146127c5b17cSBrian Norris 	host->last_byte++;
146227c5b17cSBrian Norris 
146327c5b17cSBrian Norris 	return ret;
146427c5b17cSBrian Norris }
146527c5b17cSBrian Norris 
14667e534323SBoris Brezillon static void brcmnand_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
146727c5b17cSBrian Norris {
146827c5b17cSBrian Norris 	int i;
146927c5b17cSBrian Norris 
147027c5b17cSBrian Norris 	for (i = 0; i < len; i++, buf++)
14717e534323SBoris Brezillon 		*buf = brcmnand_read_byte(chip);
147227c5b17cSBrian Norris }
147327c5b17cSBrian Norris 
1474c0739d85SBoris Brezillon static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf,
147527c5b17cSBrian Norris 			       int len)
147627c5b17cSBrian Norris {
147727c5b17cSBrian Norris 	int i;
1478d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
147927c5b17cSBrian Norris 
148027c5b17cSBrian Norris 	switch (host->last_cmd) {
148127c5b17cSBrian Norris 	case NAND_CMD_SET_FEATURES:
148227c5b17cSBrian Norris 		for (i = 0; i < len; i++)
148327c5b17cSBrian Norris 			brcmnand_low_level_op(host, LL_OP_WR, buf[i],
148427c5b17cSBrian Norris 						  (i + 1) == len);
148527c5b17cSBrian Norris 		break;
148627c5b17cSBrian Norris 	default:
148727c5b17cSBrian Norris 		BUG();
148827c5b17cSBrian Norris 		break;
148927c5b17cSBrian Norris 	}
149027c5b17cSBrian Norris }
149127c5b17cSBrian Norris 
149227c5b17cSBrian Norris /**
149327c5b17cSBrian Norris  * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
149427c5b17cSBrian Norris  * following ahead of time:
149527c5b17cSBrian Norris  *  - Is this descriptor the beginning or end of a linked list?
149627c5b17cSBrian Norris  *  - What is the (DMA) address of the next descriptor in the linked list?
149727c5b17cSBrian Norris  */
149827c5b17cSBrian Norris static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
149927c5b17cSBrian Norris 				  struct brcm_nand_dma_desc *desc, u64 addr,
150027c5b17cSBrian Norris 				  dma_addr_t buf, u32 len, u8 dma_cmd,
150127c5b17cSBrian Norris 				  bool begin, bool end,
150227c5b17cSBrian Norris 				  dma_addr_t next_desc)
150327c5b17cSBrian Norris {
150427c5b17cSBrian Norris 	memset(desc, 0, sizeof(*desc));
150527c5b17cSBrian Norris 	/* Descriptors are written in native byte order (wordwise) */
150627c5b17cSBrian Norris 	desc->next_desc = lower_32_bits(next_desc);
150727c5b17cSBrian Norris 	desc->next_desc_ext = upper_32_bits(next_desc);
150827c5b17cSBrian Norris 	desc->cmd_irq = (dma_cmd << 24) |
150927c5b17cSBrian Norris 		(end ? (0x03 << 8) : 0) | /* IRQ | STOP */
151027c5b17cSBrian Norris 		(!!begin) | ((!!end) << 1); /* head, tail */
151127c5b17cSBrian Norris #ifdef CONFIG_CPU_BIG_ENDIAN
151227c5b17cSBrian Norris 	desc->cmd_irq |= 0x01 << 12;
151327c5b17cSBrian Norris #endif
151427c5b17cSBrian Norris 	desc->dram_addr = lower_32_bits(buf);
151527c5b17cSBrian Norris 	desc->dram_addr_ext = upper_32_bits(buf);
151627c5b17cSBrian Norris 	desc->tfr_len = len;
151727c5b17cSBrian Norris 	desc->total_len = len;
151827c5b17cSBrian Norris 	desc->flash_addr = lower_32_bits(addr);
151927c5b17cSBrian Norris 	desc->flash_addr_ext = upper_32_bits(addr);
152027c5b17cSBrian Norris 	desc->cs = host->cs;
152127c5b17cSBrian Norris 	desc->status_valid = 0x01;
152227c5b17cSBrian Norris 	return 0;
152327c5b17cSBrian Norris }
152427c5b17cSBrian Norris 
152527c5b17cSBrian Norris /**
152627c5b17cSBrian Norris  * Kick the FLASH_DMA engine, with a given DMA descriptor
152727c5b17cSBrian Norris  */
152827c5b17cSBrian Norris static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
152927c5b17cSBrian Norris {
153027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
153127c5b17cSBrian Norris 	unsigned long timeo = msecs_to_jiffies(100);
153227c5b17cSBrian Norris 
153327c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
153427c5b17cSBrian Norris 	(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
153527c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc));
153627c5b17cSBrian Norris 	(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
153727c5b17cSBrian Norris 
153827c5b17cSBrian Norris 	/* Start FLASH_DMA engine */
153927c5b17cSBrian Norris 	ctrl->dma_pending = true;
154027c5b17cSBrian Norris 	mb(); /* flush previous writes */
154127c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
154227c5b17cSBrian Norris 
154327c5b17cSBrian Norris 	if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
154427c5b17cSBrian Norris 		dev_err(ctrl->dev,
154527c5b17cSBrian Norris 				"timeout waiting for DMA; status %#x, error status %#x\n",
154627c5b17cSBrian Norris 				flash_dma_readl(ctrl, FLASH_DMA_STATUS),
154727c5b17cSBrian Norris 				flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
154827c5b17cSBrian Norris 	}
154927c5b17cSBrian Norris 	ctrl->dma_pending = false;
155027c5b17cSBrian Norris 	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
155127c5b17cSBrian Norris }
155227c5b17cSBrian Norris 
155327c5b17cSBrian Norris static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
155427c5b17cSBrian Norris 			      u32 len, u8 dma_cmd)
155527c5b17cSBrian Norris {
155627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
155727c5b17cSBrian Norris 	dma_addr_t buf_pa;
155827c5b17cSBrian Norris 	int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
155927c5b17cSBrian Norris 
156027c5b17cSBrian Norris 	buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
156127c5b17cSBrian Norris 	if (dma_mapping_error(ctrl->dev, buf_pa)) {
156227c5b17cSBrian Norris 		dev_err(ctrl->dev, "unable to map buffer for DMA\n");
156327c5b17cSBrian Norris 		return -ENOMEM;
156427c5b17cSBrian Norris 	}
156527c5b17cSBrian Norris 
156627c5b17cSBrian Norris 	brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
156727c5b17cSBrian Norris 				   dma_cmd, true, true, 0);
156827c5b17cSBrian Norris 
156927c5b17cSBrian Norris 	brcmnand_dma_run(host, ctrl->dma_pa);
157027c5b17cSBrian Norris 
157127c5b17cSBrian Norris 	dma_unmap_single(ctrl->dev, buf_pa, len, dir);
157227c5b17cSBrian Norris 
157327c5b17cSBrian Norris 	if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
157427c5b17cSBrian Norris 		return -EBADMSG;
157527c5b17cSBrian Norris 	else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
157627c5b17cSBrian Norris 		return -EUCLEAN;
157727c5b17cSBrian Norris 
157827c5b17cSBrian Norris 	return 0;
157927c5b17cSBrian Norris }
158027c5b17cSBrian Norris 
158127c5b17cSBrian Norris /*
158227c5b17cSBrian Norris  * Assumes proper CS is already set
158327c5b17cSBrian Norris  */
158427c5b17cSBrian Norris static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
158527c5b17cSBrian Norris 				u64 addr, unsigned int trans, u32 *buf,
158627c5b17cSBrian Norris 				u8 *oob, u64 *err_addr)
158727c5b17cSBrian Norris {
1588d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
158927c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
159027c5b17cSBrian Norris 	int i, j, ret = 0;
159127c5b17cSBrian Norris 
159227c5b17cSBrian Norris 	/* Clear error addresses */
159327c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
159427c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
159504016697SSimon Arlott 	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_EXT_ADDR, 0);
159604016697SSimon Arlott 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_EXT_ADDR, 0);
159727c5b17cSBrian Norris 
159827c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
159927c5b17cSBrian Norris 			(host->cs << 16) | ((addr >> 32) & 0xffff));
160027c5b17cSBrian Norris 	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
160127c5b17cSBrian Norris 
160227c5b17cSBrian Norris 	for (i = 0; i < trans; i++, addr += FC_BYTES) {
160327c5b17cSBrian Norris 		brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
160427c5b17cSBrian Norris 				   lower_32_bits(addr));
160527c5b17cSBrian Norris 		(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
160627c5b17cSBrian Norris 		/* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
160727c5b17cSBrian Norris 		brcmnand_send_cmd(host, CMD_PAGE_READ);
1608f1d46942SBoris Brezillon 		brcmnand_waitfunc(chip);
160927c5b17cSBrian Norris 
1610c26211d3SBrian Norris 		if (likely(buf)) {
1611eab7fdc7SRay Jui 			brcmnand_soc_data_bus_prepare(ctrl->soc, false);
1612c26211d3SBrian Norris 
161327c5b17cSBrian Norris 			for (j = 0; j < FC_WORDS; j++, buf++)
161427c5b17cSBrian Norris 				*buf = brcmnand_read_fc(ctrl, j);
161527c5b17cSBrian Norris 
1616eab7fdc7SRay Jui 			brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
1617c26211d3SBrian Norris 		}
1618c26211d3SBrian Norris 
161927c5b17cSBrian Norris 		if (oob)
162027c5b17cSBrian Norris 			oob += read_oob_from_regs(ctrl, i, oob,
162127c5b17cSBrian Norris 					mtd->oobsize / trans,
162227c5b17cSBrian Norris 					host->hwcfg.sector_size_1k);
162327c5b17cSBrian Norris 
162427c5b17cSBrian Norris 		if (!ret) {
162527c5b17cSBrian Norris 			*err_addr = brcmnand_read_reg(ctrl,
162627c5b17cSBrian Norris 					BRCMNAND_UNCORR_ADDR) |
162727c5b17cSBrian Norris 				((u64)(brcmnand_read_reg(ctrl,
162827c5b17cSBrian Norris 						BRCMNAND_UNCORR_EXT_ADDR)
162927c5b17cSBrian Norris 					& 0xffff) << 32);
163027c5b17cSBrian Norris 			if (*err_addr)
163127c5b17cSBrian Norris 				ret = -EBADMSG;
163227c5b17cSBrian Norris 		}
163327c5b17cSBrian Norris 
163427c5b17cSBrian Norris 		if (!ret) {
163527c5b17cSBrian Norris 			*err_addr = brcmnand_read_reg(ctrl,
163627c5b17cSBrian Norris 					BRCMNAND_CORR_ADDR) |
163727c5b17cSBrian Norris 				((u64)(brcmnand_read_reg(ctrl,
163827c5b17cSBrian Norris 						BRCMNAND_CORR_EXT_ADDR)
163927c5b17cSBrian Norris 					& 0xffff) << 32);
164027c5b17cSBrian Norris 			if (*err_addr)
164127c5b17cSBrian Norris 				ret = -EUCLEAN;
164227c5b17cSBrian Norris 		}
164327c5b17cSBrian Norris 	}
164427c5b17cSBrian Norris 
164527c5b17cSBrian Norris 	return ret;
164627c5b17cSBrian Norris }
164727c5b17cSBrian Norris 
164802b88eeaSKamal Dasu /*
164902b88eeaSKamal Dasu  * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
165002b88eeaSKamal Dasu  * error
165102b88eeaSKamal Dasu  *
165202b88eeaSKamal Dasu  * Because the HW ECC signals an ECC error if an erase paged has even a single
165302b88eeaSKamal Dasu  * bitflip, we must check each ECC error to see if it is actually an erased
165402b88eeaSKamal Dasu  * page with bitflips, not a truly corrupted page.
165502b88eeaSKamal Dasu  *
165602b88eeaSKamal Dasu  * On a real error, return a negative error code (-EBADMSG for ECC error), and
165702b88eeaSKamal Dasu  * buf will contain raw data.
165802b88eeaSKamal Dasu  * Otherwise, buf gets filled with 0xffs and return the maximum number of
165902b88eeaSKamal Dasu  * bitflips-per-ECC-sector to the caller.
166002b88eeaSKamal Dasu  *
166102b88eeaSKamal Dasu  */
166202b88eeaSKamal Dasu static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
166302b88eeaSKamal Dasu 		  struct nand_chip *chip, void *buf, u64 addr)
166402b88eeaSKamal Dasu {
166502b88eeaSKamal Dasu 	int i, sas;
166602b88eeaSKamal Dasu 	void *oob = chip->oob_poi;
166702b88eeaSKamal Dasu 	int bitflips = 0;
166802b88eeaSKamal Dasu 	int page = addr >> chip->page_shift;
166902b88eeaSKamal Dasu 	int ret;
167002b88eeaSKamal Dasu 
1671eeab7174SBoris Brezillon 	if (!buf)
1672eeab7174SBoris Brezillon 		buf = nand_get_data_buf(chip);
167302b88eeaSKamal Dasu 
167402b88eeaSKamal Dasu 	sas = mtd->oobsize / chip->ecc.steps;
167502b88eeaSKamal Dasu 
167602b88eeaSKamal Dasu 	/* read without ecc for verification */
1677b9761687SBoris Brezillon 	ret = chip->ecc.read_page_raw(chip, buf, true, page);
167802b88eeaSKamal Dasu 	if (ret)
167902b88eeaSKamal Dasu 		return ret;
168002b88eeaSKamal Dasu 
168102b88eeaSKamal Dasu 	for (i = 0; i < chip->ecc.steps; i++, oob += sas) {
168202b88eeaSKamal Dasu 		ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size,
168302b88eeaSKamal Dasu 						  oob, sas, NULL, 0,
168402b88eeaSKamal Dasu 						  chip->ecc.strength);
168502b88eeaSKamal Dasu 		if (ret < 0)
168602b88eeaSKamal Dasu 			return ret;
168702b88eeaSKamal Dasu 
168802b88eeaSKamal Dasu 		bitflips = max(bitflips, ret);
168902b88eeaSKamal Dasu 	}
169002b88eeaSKamal Dasu 
169102b88eeaSKamal Dasu 	return bitflips;
169202b88eeaSKamal Dasu }
169302b88eeaSKamal Dasu 
169427c5b17cSBrian Norris static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
169527c5b17cSBrian Norris 			 u64 addr, unsigned int trans, u32 *buf, u8 *oob)
169627c5b17cSBrian Norris {
1697d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
169827c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
169927c5b17cSBrian Norris 	u64 err_addr = 0;
170027c5b17cSBrian Norris 	int err;
1701bc265323SKamal Dasu 	bool retry = true;
170227c5b17cSBrian Norris 
170327c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
170427c5b17cSBrian Norris 
1705bc265323SKamal Dasu try_dmaread:
170627c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
170727c5b17cSBrian Norris 
170827c5b17cSBrian Norris 	if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
170927c5b17cSBrian Norris 		err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES,
171027c5b17cSBrian Norris 					     CMD_PAGE_READ);
171127c5b17cSBrian Norris 		if (err) {
171227c5b17cSBrian Norris 			if (mtd_is_bitflip_or_eccerr(err))
171327c5b17cSBrian Norris 				err_addr = addr;
171427c5b17cSBrian Norris 			else
171527c5b17cSBrian Norris 				return -EIO;
171627c5b17cSBrian Norris 		}
171727c5b17cSBrian Norris 	} else {
171827c5b17cSBrian Norris 		if (oob)
171927c5b17cSBrian Norris 			memset(oob, 0x99, mtd->oobsize);
172027c5b17cSBrian Norris 
172127c5b17cSBrian Norris 		err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
172227c5b17cSBrian Norris 					       oob, &err_addr);
172327c5b17cSBrian Norris 	}
172427c5b17cSBrian Norris 
172527c5b17cSBrian Norris 	if (mtd_is_eccerr(err)) {
172602b88eeaSKamal Dasu 		/*
1727bc265323SKamal Dasu 		 * On controller version and 7.0, 7.1 , DMA read after a
1728bc265323SKamal Dasu 		 * prior PIO read that reported uncorrectable error,
1729bc265323SKamal Dasu 		 * the DMA engine captures this error following DMA read
1730bc265323SKamal Dasu 		 * cleared only on subsequent DMA read, so just retry once
1731bc265323SKamal Dasu 		 * to clear a possible false error reported for current DMA
1732bc265323SKamal Dasu 		 * read
1733bc265323SKamal Dasu 		 */
1734bc265323SKamal Dasu 		if ((ctrl->nand_version == 0x0700) ||
1735bc265323SKamal Dasu 		    (ctrl->nand_version == 0x0701)) {
1736bc265323SKamal Dasu 			if (retry) {
1737bc265323SKamal Dasu 				retry = false;
1738bc265323SKamal Dasu 				goto try_dmaread;
1739bc265323SKamal Dasu 			}
1740bc265323SKamal Dasu 		}
1741bc265323SKamal Dasu 
1742bc265323SKamal Dasu 		/*
174302b88eeaSKamal Dasu 		 * Controller version 7.2 has hw encoder to detect erased page
174402b88eeaSKamal Dasu 		 * bitflips, apply sw verification for older controllers only
174502b88eeaSKamal Dasu 		 */
174602b88eeaSKamal Dasu 		if (ctrl->nand_version < 0x0702) {
174702b88eeaSKamal Dasu 			err = brcmstb_nand_verify_erased_page(mtd, chip, buf,
174802b88eeaSKamal Dasu 							      addr);
174902b88eeaSKamal Dasu 			/* erased page bitflips corrected */
1750e44b9a9cSAlbert Hsieh 			if (err >= 0)
175102b88eeaSKamal Dasu 				return err;
175202b88eeaSKamal Dasu 		}
175302b88eeaSKamal Dasu 
175427c5b17cSBrian Norris 		dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
175527c5b17cSBrian Norris 			(unsigned long long)err_addr);
175627c5b17cSBrian Norris 		mtd->ecc_stats.failed++;
175727c5b17cSBrian Norris 		/* NAND layer expects zero on ECC errors */
175827c5b17cSBrian Norris 		return 0;
175927c5b17cSBrian Norris 	}
176027c5b17cSBrian Norris 
176127c5b17cSBrian Norris 	if (mtd_is_bitflip(err)) {
176227c5b17cSBrian Norris 		unsigned int corrected = brcmnand_count_corrected(ctrl);
176327c5b17cSBrian Norris 
176427c5b17cSBrian Norris 		dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
176527c5b17cSBrian Norris 			(unsigned long long)err_addr);
176627c5b17cSBrian Norris 		mtd->ecc_stats.corrected += corrected;
176727c5b17cSBrian Norris 		/* Always exceed the software-imposed threshold */
176827c5b17cSBrian Norris 		return max(mtd->bitflip_threshold, corrected);
176927c5b17cSBrian Norris 	}
177027c5b17cSBrian Norris 
177127c5b17cSBrian Norris 	return 0;
177227c5b17cSBrian Norris }
177327c5b17cSBrian Norris 
1774b9761687SBoris Brezillon static int brcmnand_read_page(struct nand_chip *chip, uint8_t *buf,
1775b9761687SBoris Brezillon 			      int oob_required, int page)
177627c5b17cSBrian Norris {
1777b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1778d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
177927c5b17cSBrian Norris 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
178027c5b17cSBrian Norris 
178125f815f6SBoris Brezillon 	nand_read_page_op(chip, page, 0, NULL, 0);
178225f815f6SBoris Brezillon 
178327c5b17cSBrian Norris 	return brcmnand_read(mtd, chip, host->last_addr,
178427c5b17cSBrian Norris 			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
178527c5b17cSBrian Norris }
178627c5b17cSBrian Norris 
1787b9761687SBoris Brezillon static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
1788b9761687SBoris Brezillon 				  int oob_required, int page)
178927c5b17cSBrian Norris {
1790d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
1791b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
179227c5b17cSBrian Norris 	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
179327c5b17cSBrian Norris 	int ret;
179427c5b17cSBrian Norris 
179525f815f6SBoris Brezillon 	nand_read_page_op(chip, page, 0, NULL, 0);
179625f815f6SBoris Brezillon 
179727c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
179827c5b17cSBrian Norris 	ret = brcmnand_read(mtd, chip, host->last_addr,
179927c5b17cSBrian Norris 			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
180027c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
180127c5b17cSBrian Norris 	return ret;
180227c5b17cSBrian Norris }
180327c5b17cSBrian Norris 
1804b9761687SBoris Brezillon static int brcmnand_read_oob(struct nand_chip *chip, int page)
180527c5b17cSBrian Norris {
1806b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1807b9761687SBoris Brezillon 
180827c5b17cSBrian Norris 	return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
180927c5b17cSBrian Norris 			mtd->writesize >> FC_SHIFT,
181027c5b17cSBrian Norris 			NULL, (u8 *)chip->oob_poi);
181127c5b17cSBrian Norris }
181227c5b17cSBrian Norris 
1813b9761687SBoris Brezillon static int brcmnand_read_oob_raw(struct nand_chip *chip, int page)
181427c5b17cSBrian Norris {
1815b9761687SBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1816d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
181727c5b17cSBrian Norris 
181827c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
181927c5b17cSBrian Norris 	brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
182027c5b17cSBrian Norris 		mtd->writesize >> FC_SHIFT,
182127c5b17cSBrian Norris 		NULL, (u8 *)chip->oob_poi);
182227c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
182327c5b17cSBrian Norris 	return 0;
182427c5b17cSBrian Norris }
182527c5b17cSBrian Norris 
182627c5b17cSBrian Norris static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
182727c5b17cSBrian Norris 			  u64 addr, const u32 *buf, u8 *oob)
182827c5b17cSBrian Norris {
1829d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
183027c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
183127c5b17cSBrian Norris 	unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
183227c5b17cSBrian Norris 	int status, ret = 0;
183327c5b17cSBrian Norris 
183427c5b17cSBrian Norris 	dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
183527c5b17cSBrian Norris 
18363f08b8baSAnup Patel 	if (unlikely((unsigned long)buf & 0x03)) {
183727c5b17cSBrian Norris 		dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
18383f08b8baSAnup Patel 		buf = (u32 *)((unsigned long)buf & ~0x03);
183927c5b17cSBrian Norris 	}
184027c5b17cSBrian Norris 
184127c5b17cSBrian Norris 	brcmnand_wp(mtd, 0);
184227c5b17cSBrian Norris 
184327c5b17cSBrian Norris 	for (i = 0; i < ctrl->max_oob; i += 4)
184427c5b17cSBrian Norris 		oob_reg_write(ctrl, i, 0xffffffff);
184527c5b17cSBrian Norris 
184627c5b17cSBrian Norris 	if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
184727c5b17cSBrian Norris 		if (brcmnand_dma_trans(host, addr, (u32 *)buf,
184827c5b17cSBrian Norris 					mtd->writesize, CMD_PROGRAM_PAGE))
184927c5b17cSBrian Norris 			ret = -EIO;
185027c5b17cSBrian Norris 		goto out;
185127c5b17cSBrian Norris 	}
185227c5b17cSBrian Norris 
185327c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
185427c5b17cSBrian Norris 			(host->cs << 16) | ((addr >> 32) & 0xffff));
185527c5b17cSBrian Norris 	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
185627c5b17cSBrian Norris 
185727c5b17cSBrian Norris 	for (i = 0; i < trans; i++, addr += FC_BYTES) {
185827c5b17cSBrian Norris 		/* full address MUST be set before populating FC */
185927c5b17cSBrian Norris 		brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
186027c5b17cSBrian Norris 				   lower_32_bits(addr));
186127c5b17cSBrian Norris 		(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
186227c5b17cSBrian Norris 
1863c26211d3SBrian Norris 		if (buf) {
1864eab7fdc7SRay Jui 			brcmnand_soc_data_bus_prepare(ctrl->soc, false);
1865c26211d3SBrian Norris 
186627c5b17cSBrian Norris 			for (j = 0; j < FC_WORDS; j++, buf++)
186727c5b17cSBrian Norris 				brcmnand_write_fc(ctrl, j, *buf);
1868c26211d3SBrian Norris 
1869eab7fdc7SRay Jui 			brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
1870c26211d3SBrian Norris 		} else if (oob) {
187127c5b17cSBrian Norris 			for (j = 0; j < FC_WORDS; j++)
187227c5b17cSBrian Norris 				brcmnand_write_fc(ctrl, j, 0xffffffff);
1873c26211d3SBrian Norris 		}
187427c5b17cSBrian Norris 
187527c5b17cSBrian Norris 		if (oob) {
187627c5b17cSBrian Norris 			oob += write_oob_to_regs(ctrl, i, oob,
187727c5b17cSBrian Norris 					mtd->oobsize / trans,
187827c5b17cSBrian Norris 					host->hwcfg.sector_size_1k);
187927c5b17cSBrian Norris 		}
188027c5b17cSBrian Norris 
188127c5b17cSBrian Norris 		/* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
188227c5b17cSBrian Norris 		brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
1883f1d46942SBoris Brezillon 		status = brcmnand_waitfunc(chip);
188427c5b17cSBrian Norris 
188527c5b17cSBrian Norris 		if (status & NAND_STATUS_FAIL) {
188627c5b17cSBrian Norris 			dev_info(ctrl->dev, "program failed at %llx\n",
188727c5b17cSBrian Norris 				(unsigned long long)addr);
188827c5b17cSBrian Norris 			ret = -EIO;
188927c5b17cSBrian Norris 			goto out;
189027c5b17cSBrian Norris 		}
189127c5b17cSBrian Norris 	}
189227c5b17cSBrian Norris out:
189327c5b17cSBrian Norris 	brcmnand_wp(mtd, 1);
189427c5b17cSBrian Norris 	return ret;
189527c5b17cSBrian Norris }
189627c5b17cSBrian Norris 
1897767eb6fbSBoris Brezillon static int brcmnand_write_page(struct nand_chip *chip, const uint8_t *buf,
1898767eb6fbSBoris Brezillon 			       int oob_required, int page)
189927c5b17cSBrian Norris {
1900767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1901d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
190227c5b17cSBrian Norris 	void *oob = oob_required ? chip->oob_poi : NULL;
190327c5b17cSBrian Norris 
190425f815f6SBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
190527c5b17cSBrian Norris 	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
190625f815f6SBoris Brezillon 
190725f815f6SBoris Brezillon 	return nand_prog_page_end_op(chip);
190827c5b17cSBrian Norris }
190927c5b17cSBrian Norris 
1910767eb6fbSBoris Brezillon static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
191145aaeff9SBoris BREZILLON 				   int oob_required, int page)
191227c5b17cSBrian Norris {
1913767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1914d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
191527c5b17cSBrian Norris 	void *oob = oob_required ? chip->oob_poi : NULL;
191627c5b17cSBrian Norris 
191725f815f6SBoris Brezillon 	nand_prog_page_begin_op(chip, page, 0, NULL, 0);
191827c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
191927c5b17cSBrian Norris 	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
192027c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
192125f815f6SBoris Brezillon 
192225f815f6SBoris Brezillon 	return nand_prog_page_end_op(chip);
192327c5b17cSBrian Norris }
192427c5b17cSBrian Norris 
1925767eb6fbSBoris Brezillon static int brcmnand_write_oob(struct nand_chip *chip, int page)
192627c5b17cSBrian Norris {
1927767eb6fbSBoris Brezillon 	return brcmnand_write(nand_to_mtd(chip), chip,
1928767eb6fbSBoris Brezillon 			      (u64)page << chip->page_shift, NULL,
1929767eb6fbSBoris Brezillon 			      chip->oob_poi);
193027c5b17cSBrian Norris }
193127c5b17cSBrian Norris 
1932767eb6fbSBoris Brezillon static int brcmnand_write_oob_raw(struct nand_chip *chip, int page)
193327c5b17cSBrian Norris {
1934767eb6fbSBoris Brezillon 	struct mtd_info *mtd = nand_to_mtd(chip);
1935d699ed25SBoris BREZILLON 	struct brcmnand_host *host = nand_get_controller_data(chip);
193627c5b17cSBrian Norris 	int ret;
193727c5b17cSBrian Norris 
193827c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 0);
193927c5b17cSBrian Norris 	ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
194027c5b17cSBrian Norris 				 (u8 *)chip->oob_poi);
194127c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
194227c5b17cSBrian Norris 
194327c5b17cSBrian Norris 	return ret;
194427c5b17cSBrian Norris }
194527c5b17cSBrian Norris 
194627c5b17cSBrian Norris /***********************************************************************
194727c5b17cSBrian Norris  * Per-CS setup (1 NAND device)
194827c5b17cSBrian Norris  ***********************************************************************/
194927c5b17cSBrian Norris 
195027c5b17cSBrian Norris static int brcmnand_set_cfg(struct brcmnand_host *host,
195127c5b17cSBrian Norris 			    struct brcmnand_cfg *cfg)
195227c5b17cSBrian Norris {
195327c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
195427c5b17cSBrian Norris 	struct nand_chip *chip = &host->chip;
195527c5b17cSBrian Norris 	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
195627c5b17cSBrian Norris 	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
195727c5b17cSBrian Norris 			BRCMNAND_CS_CFG_EXT);
195827c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
195927c5b17cSBrian Norris 			BRCMNAND_CS_ACC_CONTROL);
196027c5b17cSBrian Norris 	u8 block_size = 0, page_size = 0, device_size = 0;
196127c5b17cSBrian Norris 	u32 tmp;
196227c5b17cSBrian Norris 
196327c5b17cSBrian Norris 	if (ctrl->block_sizes) {
196427c5b17cSBrian Norris 		int i, found;
196527c5b17cSBrian Norris 
196627c5b17cSBrian Norris 		for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
196727c5b17cSBrian Norris 			if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
196827c5b17cSBrian Norris 				block_size = i;
196927c5b17cSBrian Norris 				found = 1;
197027c5b17cSBrian Norris 			}
197127c5b17cSBrian Norris 		if (!found) {
197227c5b17cSBrian Norris 			dev_warn(ctrl->dev, "invalid block size %u\n",
197327c5b17cSBrian Norris 					cfg->block_size);
197427c5b17cSBrian Norris 			return -EINVAL;
197527c5b17cSBrian Norris 		}
197627c5b17cSBrian Norris 	} else {
197727c5b17cSBrian Norris 		block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
197827c5b17cSBrian Norris 	}
197927c5b17cSBrian Norris 
198027c5b17cSBrian Norris 	if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
198127c5b17cSBrian Norris 				cfg->block_size > ctrl->max_block_size)) {
198227c5b17cSBrian Norris 		dev_warn(ctrl->dev, "invalid block size %u\n",
198327c5b17cSBrian Norris 				cfg->block_size);
198427c5b17cSBrian Norris 		block_size = 0;
198527c5b17cSBrian Norris 	}
198627c5b17cSBrian Norris 
198727c5b17cSBrian Norris 	if (ctrl->page_sizes) {
198827c5b17cSBrian Norris 		int i, found;
198927c5b17cSBrian Norris 
199027c5b17cSBrian Norris 		for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
199127c5b17cSBrian Norris 			if (ctrl->page_sizes[i] == cfg->page_size) {
199227c5b17cSBrian Norris 				page_size = i;
199327c5b17cSBrian Norris 				found = 1;
199427c5b17cSBrian Norris 			}
199527c5b17cSBrian Norris 		if (!found) {
199627c5b17cSBrian Norris 			dev_warn(ctrl->dev, "invalid page size %u\n",
199727c5b17cSBrian Norris 					cfg->page_size);
199827c5b17cSBrian Norris 			return -EINVAL;
199927c5b17cSBrian Norris 		}
200027c5b17cSBrian Norris 	} else {
200127c5b17cSBrian Norris 		page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
200227c5b17cSBrian Norris 	}
200327c5b17cSBrian Norris 
200427c5b17cSBrian Norris 	if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
200527c5b17cSBrian Norris 				cfg->page_size > ctrl->max_page_size)) {
200627c5b17cSBrian Norris 		dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
200727c5b17cSBrian Norris 		return -EINVAL;
200827c5b17cSBrian Norris 	}
200927c5b17cSBrian Norris 
201027c5b17cSBrian Norris 	if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
201127c5b17cSBrian Norris 		dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
201227c5b17cSBrian Norris 			(unsigned long long)cfg->device_size);
201327c5b17cSBrian Norris 		return -EINVAL;
201427c5b17cSBrian Norris 	}
201527c5b17cSBrian Norris 	device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
201627c5b17cSBrian Norris 
20173f06d2a9SBrian Norris 	tmp = (cfg->blk_adr_bytes << CFG_BLK_ADR_BYTES_SHIFT) |
20183f06d2a9SBrian Norris 		(cfg->col_adr_bytes << CFG_COL_ADR_BYTES_SHIFT) |
20193f06d2a9SBrian Norris 		(cfg->ful_adr_bytes << CFG_FUL_ADR_BYTES_SHIFT) |
20203f06d2a9SBrian Norris 		(!!(cfg->device_width == 16) << CFG_BUS_WIDTH_SHIFT) |
20213f06d2a9SBrian Norris 		(device_size << CFG_DEVICE_SIZE_SHIFT);
202227c5b17cSBrian Norris 	if (cfg_offs == cfg_ext_offs) {
20233f06d2a9SBrian Norris 		tmp |= (page_size << CFG_PAGE_SIZE_SHIFT) |
20243f06d2a9SBrian Norris 		       (block_size << CFG_BLK_SIZE_SHIFT);
202527c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_offs, tmp);
202627c5b17cSBrian Norris 	} else {
202727c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_offs, tmp);
20283f06d2a9SBrian Norris 		tmp = (page_size << CFG_EXT_PAGE_SIZE_SHIFT) |
20293f06d2a9SBrian Norris 		      (block_size << CFG_EXT_BLK_SIZE_SHIFT);
203027c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_ext_offs, tmp);
203127c5b17cSBrian Norris 	}
203227c5b17cSBrian Norris 
203327c5b17cSBrian Norris 	tmp = nand_readreg(ctrl, acc_control_offs);
203427c5b17cSBrian Norris 	tmp &= ~brcmnand_ecc_level_mask(ctrl);
203527c5b17cSBrian Norris 	tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
203627c5b17cSBrian Norris 	tmp &= ~brcmnand_spare_area_mask(ctrl);
203727c5b17cSBrian Norris 	tmp |= cfg->spare_area_size;
203827c5b17cSBrian Norris 	nand_writereg(ctrl, acc_control_offs, tmp);
203927c5b17cSBrian Norris 
204027c5b17cSBrian Norris 	brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
204127c5b17cSBrian Norris 
204227c5b17cSBrian Norris 	/* threshold = ceil(BCH-level * 0.75) */
204327c5b17cSBrian Norris 	brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
204427c5b17cSBrian Norris 
204527c5b17cSBrian Norris 	return 0;
204627c5b17cSBrian Norris }
204727c5b17cSBrian Norris 
2048decba6d4SFlorian Fainelli static void brcmnand_print_cfg(struct brcmnand_host *host,
2049decba6d4SFlorian Fainelli 			       char *buf, struct brcmnand_cfg *cfg)
205027c5b17cSBrian Norris {
205127c5b17cSBrian Norris 	buf += sprintf(buf,
205227c5b17cSBrian Norris 		"%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
205327c5b17cSBrian Norris 		(unsigned long long)cfg->device_size >> 20,
205427c5b17cSBrian Norris 		cfg->block_size >> 10,
205527c5b17cSBrian Norris 		cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
205627c5b17cSBrian Norris 		cfg->page_size >= 1024 ? "KiB" : "B",
205727c5b17cSBrian Norris 		cfg->spare_area_size, cfg->device_width);
205827c5b17cSBrian Norris 
205927c5b17cSBrian Norris 	/* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
2060decba6d4SFlorian Fainelli 	if (is_hamming_ecc(host->ctrl, cfg))
206127c5b17cSBrian Norris 		sprintf(buf, ", Hamming ECC");
206227c5b17cSBrian Norris 	else if (cfg->sector_size_1k)
206327c5b17cSBrian Norris 		sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
206427c5b17cSBrian Norris 	else
206580204124SHauke Mehrtens 		sprintf(buf, ", BCH-%u", cfg->ecc_level);
206627c5b17cSBrian Norris }
206727c5b17cSBrian Norris 
206827c5b17cSBrian Norris /*
206927c5b17cSBrian Norris  * Minimum number of bytes to address a page. Calculated as:
207027c5b17cSBrian Norris  *     roundup(log2(size / page-size) / 8)
207127c5b17cSBrian Norris  *
207227c5b17cSBrian Norris  * NB: the following does not "round up" for non-power-of-2 'size'; but this is
207327c5b17cSBrian Norris  *     OK because many other things will break if 'size' is irregular...
207427c5b17cSBrian Norris  */
207527c5b17cSBrian Norris static inline int get_blk_adr_bytes(u64 size, u32 writesize)
207627c5b17cSBrian Norris {
207727c5b17cSBrian Norris 	return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
207827c5b17cSBrian Norris }
207927c5b17cSBrian Norris 
208027c5b17cSBrian Norris static int brcmnand_setup_dev(struct brcmnand_host *host)
208127c5b17cSBrian Norris {
2082f1c4c999SBoris BREZILLON 	struct mtd_info *mtd = nand_to_mtd(&host->chip);
208327c5b17cSBrian Norris 	struct nand_chip *chip = &host->chip;
208427c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
208527c5b17cSBrian Norris 	struct brcmnand_cfg *cfg = &host->hwcfg;
208627c5b17cSBrian Norris 	char msg[128];
208727c5b17cSBrian Norris 	u32 offs, tmp, oob_sector;
208827c5b17cSBrian Norris 	int ret;
208927c5b17cSBrian Norris 
209027c5b17cSBrian Norris 	memset(cfg, 0, sizeof(*cfg));
209127c5b17cSBrian Norris 
209244ec23c9SBoris BREZILLON 	ret = of_property_read_u32(nand_get_flash_node(chip),
209361528d88SMarek Vasut 				   "brcm,nand-oob-sector-size",
209427c5b17cSBrian Norris 				   &oob_sector);
209527c5b17cSBrian Norris 	if (ret) {
209627c5b17cSBrian Norris 		/* Use detected size */
209727c5b17cSBrian Norris 		cfg->spare_area_size = mtd->oobsize /
209827c5b17cSBrian Norris 					(mtd->writesize >> FC_SHIFT);
209927c5b17cSBrian Norris 	} else {
210027c5b17cSBrian Norris 		cfg->spare_area_size = oob_sector;
210127c5b17cSBrian Norris 	}
210227c5b17cSBrian Norris 	if (cfg->spare_area_size > ctrl->max_oob)
210327c5b17cSBrian Norris 		cfg->spare_area_size = ctrl->max_oob;
210427c5b17cSBrian Norris 	/*
210527c5b17cSBrian Norris 	 * Set oobsize to be consistent with controller's spare_area_size, as
210627c5b17cSBrian Norris 	 * the rest is inaccessible.
210727c5b17cSBrian Norris 	 */
210827c5b17cSBrian Norris 	mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
210927c5b17cSBrian Norris 
211027c5b17cSBrian Norris 	cfg->device_size = mtd->size;
211127c5b17cSBrian Norris 	cfg->block_size = mtd->erasesize;
211227c5b17cSBrian Norris 	cfg->page_size = mtd->writesize;
211327c5b17cSBrian Norris 	cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
211427c5b17cSBrian Norris 	cfg->col_adr_bytes = 2;
211527c5b17cSBrian Norris 	cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
211627c5b17cSBrian Norris 
2117666b6568SBrian Norris 	if (chip->ecc.mode != NAND_ECC_HW) {
2118666b6568SBrian Norris 		dev_err(ctrl->dev, "only HW ECC supported; selected: %d\n",
2119666b6568SBrian Norris 			chip->ecc.mode);
2120666b6568SBrian Norris 		return -EINVAL;
2121666b6568SBrian Norris 	}
2122666b6568SBrian Norris 
2123666b6568SBrian Norris 	if (chip->ecc.algo == NAND_ECC_UNKNOWN) {
2124666b6568SBrian Norris 		if (chip->ecc.strength == 1 && chip->ecc.size == 512)
2125666b6568SBrian Norris 			/* Default to Hamming for 1-bit ECC, if unspecified */
2126666b6568SBrian Norris 			chip->ecc.algo = NAND_ECC_HAMMING;
2127666b6568SBrian Norris 		else
2128666b6568SBrian Norris 			/* Otherwise, BCH */
2129666b6568SBrian Norris 			chip->ecc.algo = NAND_ECC_BCH;
2130666b6568SBrian Norris 	}
2131666b6568SBrian Norris 
2132666b6568SBrian Norris 	if (chip->ecc.algo == NAND_ECC_HAMMING && (chip->ecc.strength != 1 ||
2133666b6568SBrian Norris 						   chip->ecc.size != 512)) {
2134666b6568SBrian Norris 		dev_err(ctrl->dev, "invalid Hamming params: %d bits per %d bytes\n",
2135666b6568SBrian Norris 			chip->ecc.strength, chip->ecc.size);
2136666b6568SBrian Norris 		return -EINVAL;
2137666b6568SBrian Norris 	}
2138666b6568SBrian Norris 
213927c5b17cSBrian Norris 	switch (chip->ecc.size) {
214027c5b17cSBrian Norris 	case 512:
2141666b6568SBrian Norris 		if (chip->ecc.algo == NAND_ECC_HAMMING)
214227c5b17cSBrian Norris 			cfg->ecc_level = 15;
214327c5b17cSBrian Norris 		else
214427c5b17cSBrian Norris 			cfg->ecc_level = chip->ecc.strength;
214527c5b17cSBrian Norris 		cfg->sector_size_1k = 0;
214627c5b17cSBrian Norris 		break;
214727c5b17cSBrian Norris 	case 1024:
214827c5b17cSBrian Norris 		if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
214927c5b17cSBrian Norris 			dev_err(ctrl->dev, "1KB sectors not supported\n");
215027c5b17cSBrian Norris 			return -EINVAL;
215127c5b17cSBrian Norris 		}
215227c5b17cSBrian Norris 		if (chip->ecc.strength & 0x1) {
215327c5b17cSBrian Norris 			dev_err(ctrl->dev,
215427c5b17cSBrian Norris 				"odd ECC not supported with 1KB sectors\n");
215527c5b17cSBrian Norris 			return -EINVAL;
215627c5b17cSBrian Norris 		}
215727c5b17cSBrian Norris 
215827c5b17cSBrian Norris 		cfg->ecc_level = chip->ecc.strength >> 1;
215927c5b17cSBrian Norris 		cfg->sector_size_1k = 1;
216027c5b17cSBrian Norris 		break;
216127c5b17cSBrian Norris 	default:
216227c5b17cSBrian Norris 		dev_err(ctrl->dev, "unsupported ECC size: %d\n",
216327c5b17cSBrian Norris 			chip->ecc.size);
216427c5b17cSBrian Norris 		return -EINVAL;
216527c5b17cSBrian Norris 	}
216627c5b17cSBrian Norris 
216727c5b17cSBrian Norris 	cfg->ful_adr_bytes = cfg->blk_adr_bytes;
216827c5b17cSBrian Norris 	if (mtd->writesize > 512)
216927c5b17cSBrian Norris 		cfg->ful_adr_bytes += cfg->col_adr_bytes;
217027c5b17cSBrian Norris 	else
217127c5b17cSBrian Norris 		cfg->ful_adr_bytes += 1;
217227c5b17cSBrian Norris 
217327c5b17cSBrian Norris 	ret = brcmnand_set_cfg(host, cfg);
217427c5b17cSBrian Norris 	if (ret)
217527c5b17cSBrian Norris 		return ret;
217627c5b17cSBrian Norris 
217727c5b17cSBrian Norris 	brcmnand_set_ecc_enabled(host, 1);
217827c5b17cSBrian Norris 
2179decba6d4SFlorian Fainelli 	brcmnand_print_cfg(host, msg, cfg);
218027c5b17cSBrian Norris 	dev_info(ctrl->dev, "detected %s\n", msg);
218127c5b17cSBrian Norris 
218227c5b17cSBrian Norris 	/* Configure ACC_CONTROL */
218327c5b17cSBrian Norris 	offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
218427c5b17cSBrian Norris 	tmp = nand_readreg(ctrl, offs);
218527c5b17cSBrian Norris 	tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
218627c5b17cSBrian Norris 	tmp &= ~ACC_CONTROL_RD_ERASED;
2187decba6d4SFlorian Fainelli 
2188decba6d4SFlorian Fainelli 	/* We need to turn on Read from erased paged protected by ECC */
2189decba6d4SFlorian Fainelli 	if (ctrl->nand_version >= 0x0702)
2190decba6d4SFlorian Fainelli 		tmp |= ACC_CONTROL_RD_ERASED;
219127c5b17cSBrian Norris 	tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
2192f953f0f8SKamal Dasu 	if (ctrl->features & BRCMNAND_HAS_PREFETCH)
219327c5b17cSBrian Norris 		tmp &= ~ACC_CONTROL_PREFETCH;
2194f953f0f8SKamal Dasu 
219527c5b17cSBrian Norris 	nand_writereg(ctrl, offs, tmp);
219627c5b17cSBrian Norris 
219727c5b17cSBrian Norris 	return 0;
219827c5b17cSBrian Norris }
219927c5b17cSBrian Norris 
22004918b905SMiquel Raynal static int brcmnand_attach_chip(struct nand_chip *chip)
22014918b905SMiquel Raynal {
22024918b905SMiquel Raynal 	struct mtd_info *mtd = nand_to_mtd(chip);
22034918b905SMiquel Raynal 	struct brcmnand_host *host = nand_get_controller_data(chip);
22044918b905SMiquel Raynal 	int ret;
22054918b905SMiquel Raynal 
22064918b905SMiquel Raynal 	chip->options |= NAND_NO_SUBPAGE_WRITE;
22074918b905SMiquel Raynal 	/*
22084918b905SMiquel Raynal 	 * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
22094918b905SMiquel Raynal 	 * to/from, and have nand_base pass us a bounce buffer instead, as
22104918b905SMiquel Raynal 	 * needed.
22114918b905SMiquel Raynal 	 */
22124918b905SMiquel Raynal 	chip->options |= NAND_USE_BOUNCE_BUFFER;
22134918b905SMiquel Raynal 
22144918b905SMiquel Raynal 	if (chip->bbt_options & NAND_BBT_USE_FLASH)
22154918b905SMiquel Raynal 		chip->bbt_options |= NAND_BBT_NO_OOB;
22164918b905SMiquel Raynal 
22174918b905SMiquel Raynal 	if (brcmnand_setup_dev(host))
22184918b905SMiquel Raynal 		return -ENXIO;
22194918b905SMiquel Raynal 
22204918b905SMiquel Raynal 	chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
22214918b905SMiquel Raynal 
22224918b905SMiquel Raynal 	/* only use our internal HW threshold */
22234918b905SMiquel Raynal 	mtd->bitflip_threshold = 1;
22244918b905SMiquel Raynal 
22254918b905SMiquel Raynal 	ret = brcmstb_choose_ecc_layout(host);
22264918b905SMiquel Raynal 
22274918b905SMiquel Raynal 	return ret;
22284918b905SMiquel Raynal }
22294918b905SMiquel Raynal 
22304918b905SMiquel Raynal static const struct nand_controller_ops brcmnand_controller_ops = {
22314918b905SMiquel Raynal 	.attach_chip = brcmnand_attach_chip,
22324918b905SMiquel Raynal };
22334918b905SMiquel Raynal 
2234d121b66dSBrian Norris static int brcmnand_init_cs(struct brcmnand_host *host, struct device_node *dn)
223527c5b17cSBrian Norris {
223627c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
223727c5b17cSBrian Norris 	struct platform_device *pdev = host->pdev;
223827c5b17cSBrian Norris 	struct mtd_info *mtd;
223927c5b17cSBrian Norris 	struct nand_chip *chip;
22405e65d48bSBrian Norris 	int ret;
22414d1ea982SAnup Patel 	u16 cfg_offs;
224227c5b17cSBrian Norris 
224327c5b17cSBrian Norris 	ret = of_property_read_u32(dn, "reg", &host->cs);
224427c5b17cSBrian Norris 	if (ret) {
224527c5b17cSBrian Norris 		dev_err(&pdev->dev, "can't get chip-select\n");
224627c5b17cSBrian Norris 		return -ENXIO;
224727c5b17cSBrian Norris 	}
224827c5b17cSBrian Norris 
2249f1c4c999SBoris BREZILLON 	mtd = nand_to_mtd(&host->chip);
225027c5b17cSBrian Norris 	chip = &host->chip;
225127c5b17cSBrian Norris 
225263752199SBrian Norris 	nand_set_flash_node(chip, dn);
2253d699ed25SBoris BREZILLON 	nand_set_controller_data(chip, host);
225427c5b17cSBrian Norris 	mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
225527c5b17cSBrian Norris 				   host->cs);
2256039b4377SFabio Estevam 	if (!mtd->name)
2257039b4377SFabio Estevam 		return -ENOMEM;
2258039b4377SFabio Estevam 
225927c5b17cSBrian Norris 	mtd->owner = THIS_MODULE;
226027c5b17cSBrian Norris 	mtd->dev.parent = &pdev->dev;
226127c5b17cSBrian Norris 
2262bf6065c6SBoris Brezillon 	chip->legacy.cmd_ctrl = brcmnand_cmd_ctrl;
2263bf6065c6SBoris Brezillon 	chip->legacy.cmdfunc = brcmnand_cmdfunc;
22648395b753SBoris Brezillon 	chip->legacy.waitfunc = brcmnand_waitfunc;
2265716bbbabSBoris Brezillon 	chip->legacy.read_byte = brcmnand_read_byte;
2266716bbbabSBoris Brezillon 	chip->legacy.read_buf = brcmnand_read_buf;
2267716bbbabSBoris Brezillon 	chip->legacy.write_buf = brcmnand_write_buf;
226827c5b17cSBrian Norris 
226927c5b17cSBrian Norris 	chip->ecc.mode = NAND_ECC_HW;
227027c5b17cSBrian Norris 	chip->ecc.read_page = brcmnand_read_page;
227127c5b17cSBrian Norris 	chip->ecc.write_page = brcmnand_write_page;
227227c5b17cSBrian Norris 	chip->ecc.read_page_raw = brcmnand_read_page_raw;
227327c5b17cSBrian Norris 	chip->ecc.write_page_raw = brcmnand_write_page_raw;
227427c5b17cSBrian Norris 	chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
227527c5b17cSBrian Norris 	chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
227627c5b17cSBrian Norris 	chip->ecc.read_oob = brcmnand_read_oob;
227727c5b17cSBrian Norris 	chip->ecc.write_oob = brcmnand_write_oob;
227827c5b17cSBrian Norris 
227927c5b17cSBrian Norris 	chip->controller = &ctrl->controller;
228027c5b17cSBrian Norris 
22814d1ea982SAnup Patel 	/*
22824d1ea982SAnup Patel 	 * The bootloader might have configured 16bit mode but
22834d1ea982SAnup Patel 	 * NAND READID command only works in 8bit mode. We force
22844d1ea982SAnup Patel 	 * 8bit mode here to ensure that NAND READID commands works.
22854d1ea982SAnup Patel 	 */
22864d1ea982SAnup Patel 	cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
22874d1ea982SAnup Patel 	nand_writereg(ctrl, cfg_offs,
22884d1ea982SAnup Patel 		      nand_readreg(ctrl, cfg_offs) & ~CFG_BUS_WIDTH);
22894d1ea982SAnup Patel 
229000ad378fSBoris Brezillon 	ret = nand_scan(chip, 1);
2291c25cca03SMasahiro Yamada 	if (ret)
2292c25cca03SMasahiro Yamada 		return ret;
229327c5b17cSBrian Norris 
22945826b880SMiquel Raynal 	ret = mtd_device_register(mtd, NULL, 0);
22955826b880SMiquel Raynal 	if (ret)
22965826b880SMiquel Raynal 		nand_cleanup(chip);
22975826b880SMiquel Raynal 
22985826b880SMiquel Raynal 	return ret;
229927c5b17cSBrian Norris }
230027c5b17cSBrian Norris 
230127c5b17cSBrian Norris static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
230227c5b17cSBrian Norris 					    int restore)
230327c5b17cSBrian Norris {
230427c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = host->ctrl;
230527c5b17cSBrian Norris 	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
230627c5b17cSBrian Norris 	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
230727c5b17cSBrian Norris 			BRCMNAND_CS_CFG_EXT);
230827c5b17cSBrian Norris 	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
230927c5b17cSBrian Norris 			BRCMNAND_CS_ACC_CONTROL);
231027c5b17cSBrian Norris 	u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
231127c5b17cSBrian Norris 	u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
231227c5b17cSBrian Norris 
231327c5b17cSBrian Norris 	if (restore) {
231427c5b17cSBrian Norris 		nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
231527c5b17cSBrian Norris 		if (cfg_offs != cfg_ext_offs)
231627c5b17cSBrian Norris 			nand_writereg(ctrl, cfg_ext_offs,
231727c5b17cSBrian Norris 				      host->hwcfg.config_ext);
231827c5b17cSBrian Norris 		nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
231927c5b17cSBrian Norris 		nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
232027c5b17cSBrian Norris 		nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
232127c5b17cSBrian Norris 	} else {
232227c5b17cSBrian Norris 		host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
232327c5b17cSBrian Norris 		if (cfg_offs != cfg_ext_offs)
232427c5b17cSBrian Norris 			host->hwcfg.config_ext =
232527c5b17cSBrian Norris 				nand_readreg(ctrl, cfg_ext_offs);
232627c5b17cSBrian Norris 		host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
232727c5b17cSBrian Norris 		host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
232827c5b17cSBrian Norris 		host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
232927c5b17cSBrian Norris 	}
233027c5b17cSBrian Norris }
233127c5b17cSBrian Norris 
233227c5b17cSBrian Norris static int brcmnand_suspend(struct device *dev)
233327c5b17cSBrian Norris {
233427c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
233527c5b17cSBrian Norris 	struct brcmnand_host *host;
233627c5b17cSBrian Norris 
233727c5b17cSBrian Norris 	list_for_each_entry(host, &ctrl->host_list, node)
233827c5b17cSBrian Norris 		brcmnand_save_restore_cs_config(host, 0);
233927c5b17cSBrian Norris 
234027c5b17cSBrian Norris 	ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
234127c5b17cSBrian Norris 	ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
234227c5b17cSBrian Norris 	ctrl->corr_stat_threshold =
234327c5b17cSBrian Norris 		brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
234427c5b17cSBrian Norris 
234527c5b17cSBrian Norris 	if (has_flash_dma(ctrl))
234627c5b17cSBrian Norris 		ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
234727c5b17cSBrian Norris 
234827c5b17cSBrian Norris 	return 0;
234927c5b17cSBrian Norris }
235027c5b17cSBrian Norris 
235127c5b17cSBrian Norris static int brcmnand_resume(struct device *dev)
235227c5b17cSBrian Norris {
235327c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
235427c5b17cSBrian Norris 	struct brcmnand_host *host;
235527c5b17cSBrian Norris 
235627c5b17cSBrian Norris 	if (has_flash_dma(ctrl)) {
235727c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
235827c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
235927c5b17cSBrian Norris 	}
236027c5b17cSBrian Norris 
236127c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
236227c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
236327c5b17cSBrian Norris 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
236427c5b17cSBrian Norris 			ctrl->corr_stat_threshold);
2365c26211d3SBrian Norris 	if (ctrl->soc) {
2366c26211d3SBrian Norris 		/* Clear/re-enable interrupt */
2367c26211d3SBrian Norris 		ctrl->soc->ctlrdy_ack(ctrl->soc);
2368c26211d3SBrian Norris 		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
2369c26211d3SBrian Norris 	}
237027c5b17cSBrian Norris 
237127c5b17cSBrian Norris 	list_for_each_entry(host, &ctrl->host_list, node) {
2372f1c4c999SBoris BREZILLON 		struct nand_chip *chip = &host->chip;
237327c5b17cSBrian Norris 
237427c5b17cSBrian Norris 		brcmnand_save_restore_cs_config(host, 1);
237527c5b17cSBrian Norris 
237627c5b17cSBrian Norris 		/* Reset the chip, required by some chips after power-up */
237797d90da8SBoris Brezillon 		nand_reset_op(chip);
237827c5b17cSBrian Norris 	}
237927c5b17cSBrian Norris 
238027c5b17cSBrian Norris 	return 0;
238127c5b17cSBrian Norris }
238227c5b17cSBrian Norris 
238327c5b17cSBrian Norris const struct dev_pm_ops brcmnand_pm_ops = {
238427c5b17cSBrian Norris 	.suspend		= brcmnand_suspend,
238527c5b17cSBrian Norris 	.resume			= brcmnand_resume,
238627c5b17cSBrian Norris };
238727c5b17cSBrian Norris EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
238827c5b17cSBrian Norris 
238927c5b17cSBrian Norris static const struct of_device_id brcmnand_of_match[] = {
239027c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v4.0" },
239127c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v5.0" },
239227c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v6.0" },
239327c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v6.1" },
2394269ecf03SFlorian Fainelli 	{ .compatible = "brcm,brcmnand-v6.2" },
239527c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v7.0" },
239627c5b17cSBrian Norris 	{ .compatible = "brcm,brcmnand-v7.1" },
2397decba6d4SFlorian Fainelli 	{ .compatible = "brcm,brcmnand-v7.2" },
239827c5b17cSBrian Norris 	{},
239927c5b17cSBrian Norris };
240027c5b17cSBrian Norris MODULE_DEVICE_TABLE(of, brcmnand_of_match);
240127c5b17cSBrian Norris 
240227c5b17cSBrian Norris /***********************************************************************
240327c5b17cSBrian Norris  * Platform driver setup (per controller)
240427c5b17cSBrian Norris  ***********************************************************************/
240527c5b17cSBrian Norris 
240627c5b17cSBrian Norris int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
240727c5b17cSBrian Norris {
240827c5b17cSBrian Norris 	struct device *dev = &pdev->dev;
240927c5b17cSBrian Norris 	struct device_node *dn = dev->of_node, *child;
2410bcb83a19SHauke Mehrtens 	struct brcmnand_controller *ctrl;
241127c5b17cSBrian Norris 	struct resource *res;
241227c5b17cSBrian Norris 	int ret;
241327c5b17cSBrian Norris 
241427c5b17cSBrian Norris 	/* We only support device-tree instantiation */
241527c5b17cSBrian Norris 	if (!dn)
241627c5b17cSBrian Norris 		return -ENODEV;
241727c5b17cSBrian Norris 
241827c5b17cSBrian Norris 	if (!of_match_node(brcmnand_of_match, dn))
241927c5b17cSBrian Norris 		return -ENODEV;
242027c5b17cSBrian Norris 
242127c5b17cSBrian Norris 	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
242227c5b17cSBrian Norris 	if (!ctrl)
242327c5b17cSBrian Norris 		return -ENOMEM;
242427c5b17cSBrian Norris 
242527c5b17cSBrian Norris 	dev_set_drvdata(dev, ctrl);
242627c5b17cSBrian Norris 	ctrl->dev = dev;
242727c5b17cSBrian Norris 
242827c5b17cSBrian Norris 	init_completion(&ctrl->done);
242927c5b17cSBrian Norris 	init_completion(&ctrl->dma_done);
24307da45139SMiquel Raynal 	nand_controller_init(&ctrl->controller);
24314918b905SMiquel Raynal 	ctrl->controller.ops = &brcmnand_controller_ops;
243227c5b17cSBrian Norris 	INIT_LIST_HEAD(&ctrl->host_list);
243327c5b17cSBrian Norris 
243427c5b17cSBrian Norris 	/* NAND register range */
243527c5b17cSBrian Norris 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
243627c5b17cSBrian Norris 	ctrl->nand_base = devm_ioremap_resource(dev, res);
243727c5b17cSBrian Norris 	if (IS_ERR(ctrl->nand_base))
243827c5b17cSBrian Norris 		return PTR_ERR(ctrl->nand_base);
243927c5b17cSBrian Norris 
24405c05bc00SSimon Arlott 	/* Enable clock before using NAND registers */
24415c05bc00SSimon Arlott 	ctrl->clk = devm_clk_get(dev, "nand");
24425c05bc00SSimon Arlott 	if (!IS_ERR(ctrl->clk)) {
24435c05bc00SSimon Arlott 		ret = clk_prepare_enable(ctrl->clk);
24445c05bc00SSimon Arlott 		if (ret)
24455c05bc00SSimon Arlott 			return ret;
24465c05bc00SSimon Arlott 	} else {
24475c05bc00SSimon Arlott 		ret = PTR_ERR(ctrl->clk);
24485c05bc00SSimon Arlott 		if (ret == -EPROBE_DEFER)
24495c05bc00SSimon Arlott 			return ret;
24505c05bc00SSimon Arlott 
24515c05bc00SSimon Arlott 		ctrl->clk = NULL;
24525c05bc00SSimon Arlott 	}
24535c05bc00SSimon Arlott 
245427c5b17cSBrian Norris 	/* Initialize NAND revision */
245527c5b17cSBrian Norris 	ret = brcmnand_revision_init(ctrl);
245627c5b17cSBrian Norris 	if (ret)
24575c05bc00SSimon Arlott 		goto err;
245827c5b17cSBrian Norris 
245927c5b17cSBrian Norris 	/*
246027c5b17cSBrian Norris 	 * Most chips have this cache at a fixed offset within 'nand' block.
246127c5b17cSBrian Norris 	 * Some must specify this region separately.
246227c5b17cSBrian Norris 	 */
246327c5b17cSBrian Norris 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
246427c5b17cSBrian Norris 	if (res) {
246527c5b17cSBrian Norris 		ctrl->nand_fc = devm_ioremap_resource(dev, res);
24665c05bc00SSimon Arlott 		if (IS_ERR(ctrl->nand_fc)) {
24675c05bc00SSimon Arlott 			ret = PTR_ERR(ctrl->nand_fc);
24685c05bc00SSimon Arlott 			goto err;
24695c05bc00SSimon Arlott 		}
247027c5b17cSBrian Norris 	} else {
247127c5b17cSBrian Norris 		ctrl->nand_fc = ctrl->nand_base +
247227c5b17cSBrian Norris 				ctrl->reg_offsets[BRCMNAND_FC_BASE];
247327c5b17cSBrian Norris 	}
247427c5b17cSBrian Norris 
247527c5b17cSBrian Norris 	/* FLASH_DMA */
247627c5b17cSBrian Norris 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
247727c5b17cSBrian Norris 	if (res) {
247827c5b17cSBrian Norris 		ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
24795c05bc00SSimon Arlott 		if (IS_ERR(ctrl->flash_dma_base)) {
24805c05bc00SSimon Arlott 			ret = PTR_ERR(ctrl->flash_dma_base);
24815c05bc00SSimon Arlott 			goto err;
24825c05bc00SSimon Arlott 		}
248327c5b17cSBrian Norris 
248427c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
248527c5b17cSBrian Norris 		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
248627c5b17cSBrian Norris 
248727c5b17cSBrian Norris 		/* Allocate descriptor(s) */
248827c5b17cSBrian Norris 		ctrl->dma_desc = dmam_alloc_coherent(dev,
248927c5b17cSBrian Norris 						     sizeof(*ctrl->dma_desc),
249027c5b17cSBrian Norris 						     &ctrl->dma_pa, GFP_KERNEL);
24915c05bc00SSimon Arlott 		if (!ctrl->dma_desc) {
24925c05bc00SSimon Arlott 			ret = -ENOMEM;
24935c05bc00SSimon Arlott 			goto err;
24945c05bc00SSimon Arlott 		}
249527c5b17cSBrian Norris 
249627c5b17cSBrian Norris 		ctrl->dma_irq = platform_get_irq(pdev, 1);
249727c5b17cSBrian Norris 		if ((int)ctrl->dma_irq < 0) {
249827c5b17cSBrian Norris 			dev_err(dev, "missing FLASH_DMA IRQ\n");
24995c05bc00SSimon Arlott 			ret = -ENODEV;
25005c05bc00SSimon Arlott 			goto err;
250127c5b17cSBrian Norris 		}
250227c5b17cSBrian Norris 
250327c5b17cSBrian Norris 		ret = devm_request_irq(dev, ctrl->dma_irq,
250427c5b17cSBrian Norris 				brcmnand_dma_irq, 0, DRV_NAME,
250527c5b17cSBrian Norris 				ctrl);
250627c5b17cSBrian Norris 		if (ret < 0) {
250727c5b17cSBrian Norris 			dev_err(dev, "can't allocate IRQ %d: error %d\n",
250827c5b17cSBrian Norris 					ctrl->dma_irq, ret);
25095c05bc00SSimon Arlott 			goto err;
251027c5b17cSBrian Norris 		}
251127c5b17cSBrian Norris 
251227c5b17cSBrian Norris 		dev_info(dev, "enabling FLASH_DMA\n");
251327c5b17cSBrian Norris 	}
251427c5b17cSBrian Norris 
251527c5b17cSBrian Norris 	/* Disable automatic device ID config, direct addressing */
251627c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
251727c5b17cSBrian Norris 			 CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
251827c5b17cSBrian Norris 	/* Disable XOR addressing */
251927c5b17cSBrian Norris 	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
252027c5b17cSBrian Norris 
252127c5b17cSBrian Norris 	if (ctrl->features & BRCMNAND_HAS_WP) {
252227c5b17cSBrian Norris 		/* Permanently disable write protection */
252327c5b17cSBrian Norris 		if (wp_on == 2)
252427c5b17cSBrian Norris 			brcmnand_set_wp(ctrl, false);
252527c5b17cSBrian Norris 	} else {
252627c5b17cSBrian Norris 		wp_on = 0;
252727c5b17cSBrian Norris 	}
252827c5b17cSBrian Norris 
252927c5b17cSBrian Norris 	/* IRQ */
253027c5b17cSBrian Norris 	ctrl->irq = platform_get_irq(pdev, 0);
253127c5b17cSBrian Norris 	if ((int)ctrl->irq < 0) {
253227c5b17cSBrian Norris 		dev_err(dev, "no IRQ defined\n");
25335c05bc00SSimon Arlott 		ret = -ENODEV;
25345c05bc00SSimon Arlott 		goto err;
253527c5b17cSBrian Norris 	}
253627c5b17cSBrian Norris 
2537c26211d3SBrian Norris 	/*
2538c26211d3SBrian Norris 	 * Some SoCs integrate this controller (e.g., its interrupt bits) in
2539c26211d3SBrian Norris 	 * interesting ways
2540c26211d3SBrian Norris 	 */
2541c26211d3SBrian Norris 	if (soc) {
2542c26211d3SBrian Norris 		ctrl->soc = soc;
2543c26211d3SBrian Norris 
2544c26211d3SBrian Norris 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
2545c26211d3SBrian Norris 				       DRV_NAME, ctrl);
2546c26211d3SBrian Norris 
2547c26211d3SBrian Norris 		/* Enable interrupt */
2548c26211d3SBrian Norris 		ctrl->soc->ctlrdy_ack(ctrl->soc);
2549c26211d3SBrian Norris 		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
2550c26211d3SBrian Norris 	} else {
2551c26211d3SBrian Norris 		/* Use standard interrupt infrastructure */
255227c5b17cSBrian Norris 		ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
255327c5b17cSBrian Norris 				       DRV_NAME, ctrl);
2554c26211d3SBrian Norris 	}
255527c5b17cSBrian Norris 	if (ret < 0) {
255627c5b17cSBrian Norris 		dev_err(dev, "can't allocate IRQ %d: error %d\n",
255727c5b17cSBrian Norris 			ctrl->irq, ret);
25585c05bc00SSimon Arlott 		goto err;
255927c5b17cSBrian Norris 	}
256027c5b17cSBrian Norris 
256127c5b17cSBrian Norris 	for_each_available_child_of_node(dn, child) {
256227c5b17cSBrian Norris 		if (of_device_is_compatible(child, "brcm,nandcs")) {
256327c5b17cSBrian Norris 			struct brcmnand_host *host;
256427c5b17cSBrian Norris 
256527c5b17cSBrian Norris 			host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
2566081976bcSJulia Lawall 			if (!host) {
2567081976bcSJulia Lawall 				of_node_put(child);
25685c05bc00SSimon Arlott 				ret = -ENOMEM;
25695c05bc00SSimon Arlott 				goto err;
2570081976bcSJulia Lawall 			}
257127c5b17cSBrian Norris 			host->pdev = pdev;
257227c5b17cSBrian Norris 			host->ctrl = ctrl;
257327c5b17cSBrian Norris 
2574d121b66dSBrian Norris 			ret = brcmnand_init_cs(host, child);
2575081976bcSJulia Lawall 			if (ret) {
2576081976bcSJulia Lawall 				devm_kfree(dev, host);
257727c5b17cSBrian Norris 				continue; /* Try all chip-selects */
2578081976bcSJulia Lawall 			}
257927c5b17cSBrian Norris 
258027c5b17cSBrian Norris 			list_add_tail(&host->node, &ctrl->host_list);
258127c5b17cSBrian Norris 		}
258227c5b17cSBrian Norris 	}
258327c5b17cSBrian Norris 
258427c5b17cSBrian Norris 	/* No chip-selects could initialize properly */
25855c05bc00SSimon Arlott 	if (list_empty(&ctrl->host_list)) {
25865c05bc00SSimon Arlott 		ret = -ENODEV;
25875c05bc00SSimon Arlott 		goto err;
25885c05bc00SSimon Arlott 	}
258927c5b17cSBrian Norris 
259027c5b17cSBrian Norris 	return 0;
25915c05bc00SSimon Arlott 
25925c05bc00SSimon Arlott err:
25935c05bc00SSimon Arlott 	clk_disable_unprepare(ctrl->clk);
25945c05bc00SSimon Arlott 	return ret;
25955c05bc00SSimon Arlott 
259627c5b17cSBrian Norris }
259727c5b17cSBrian Norris EXPORT_SYMBOL_GPL(brcmnand_probe);
259827c5b17cSBrian Norris 
259927c5b17cSBrian Norris int brcmnand_remove(struct platform_device *pdev)
260027c5b17cSBrian Norris {
260127c5b17cSBrian Norris 	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
260227c5b17cSBrian Norris 	struct brcmnand_host *host;
260327c5b17cSBrian Norris 
260427c5b17cSBrian Norris 	list_for_each_entry(host, &ctrl->host_list, node)
260559ac276fSBoris Brezillon 		nand_release(&host->chip);
260627c5b17cSBrian Norris 
26075c05bc00SSimon Arlott 	clk_disable_unprepare(ctrl->clk);
26085c05bc00SSimon Arlott 
260927c5b17cSBrian Norris 	dev_set_drvdata(&pdev->dev, NULL);
261027c5b17cSBrian Norris 
261127c5b17cSBrian Norris 	return 0;
261227c5b17cSBrian Norris }
261327c5b17cSBrian Norris EXPORT_SYMBOL_GPL(brcmnand_remove);
261427c5b17cSBrian Norris 
261527c5b17cSBrian Norris MODULE_LICENSE("GPL v2");
261627c5b17cSBrian Norris MODULE_AUTHOR("Kevin Cernekee");
261727c5b17cSBrian Norris MODULE_AUTHOR("Brian Norris");
261827c5b17cSBrian Norris MODULE_DESCRIPTION("NAND driver for Broadcom chips");
261927c5b17cSBrian Norris MODULE_ALIAS("platform:brcmnand");
2620