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