summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/brcmnand/brcmnand.c
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@bootlin.com>2018-02-05 23:02:04 +0100
committerBoris Brezillon <boris.brezillon@bootlin.com>2018-02-16 10:09:34 +0100
commit93db446a424cee9387b532995e6b516667079555 (patch)
tree39c7900ae38d890fb971ea5fc6f194f7e66fa797 /drivers/mtd/nand/brcmnand/brcmnand.c
parent7b6afee7291802aa8c02aa918782033992caf641 (diff)
mtd: nand: move raw NAND related code to the raw/ subdir
As part of the process of sharing more code between different NAND based devices, we need to move all raw NAND related code to the raw/ subdirectory. Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Diffstat (limited to 'drivers/mtd/nand/brcmnand/brcmnand.c')
-rw-r--r--drivers/mtd/nand/brcmnand/brcmnand.c2620
1 files changed, 0 insertions, 2620 deletions
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c
deleted file mode 100644
index c28fd2bc1a84..000000000000
--- a/drivers/mtd/nand/brcmnand/brcmnand.c
+++ /dev/null
@@ -1,2620 +0,0 @@
-/*
- * Copyright © 2010-2015 Broadcom Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/clk.h>
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/dma-mapping.h>
-#include <linux/ioport.h>
-#include <linux/bug.h>
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-#include <linux/mm.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/log2.h>
-
-#include "brcmnand.h"
-
-/*
- * This flag controls if WP stays on between erase/write commands to mitigate
- * flash corruption due to power glitches. Values:
- * 0: NAND_WP is not used or not available
- * 1: NAND_WP is set by default, cleared for erase/write operations
- * 2: NAND_WP is always cleared
- */
-static int wp_on = 1;
-module_param(wp_on, int, 0444);
-
-/***********************************************************************
- * Definitions
- ***********************************************************************/
-
-#define DRV_NAME "brcmnand"
-
-#define CMD_NULL 0x00
-#define CMD_PAGE_READ 0x01
-#define CMD_SPARE_AREA_READ 0x02
-#define CMD_STATUS_READ 0x03
-#define CMD_PROGRAM_PAGE 0x04
-#define CMD_PROGRAM_SPARE_AREA 0x05
-#define CMD_COPY_BACK 0x06
-#define CMD_DEVICE_ID_READ 0x07
-#define CMD_BLOCK_ERASE 0x08
-#define CMD_FLASH_RESET 0x09
-#define CMD_BLOCKS_LOCK 0x0a
-#define CMD_BLOCKS_LOCK_DOWN 0x0b
-#define CMD_BLOCKS_UNLOCK 0x0c
-#define CMD_READ_BLOCKS_LOCK_STATUS 0x0d
-#define CMD_PARAMETER_READ 0x0e
-#define CMD_PARAMETER_CHANGE_COL 0x0f
-#define CMD_LOW_LEVEL_OP 0x10
-
-struct brcm_nand_dma_desc {
- u32 next_desc;
- u32 next_desc_ext;
- u32 cmd_irq;
- u32 dram_addr;
- u32 dram_addr_ext;
- u32 tfr_len;
- u32 total_len;
- u32 flash_addr;
- u32 flash_addr_ext;
- u32 cs;
- u32 pad2[5];
- u32 status_valid;
-} __packed;
-
-/* Bitfields for brcm_nand_dma_desc::status_valid */
-#define FLASH_DMA_ECC_ERROR (1 << 8)
-#define FLASH_DMA_CORR_ERROR (1 << 9)
-
-/* 512B flash cache in the NAND controller HW */
-#define FC_SHIFT 9U
-#define FC_BYTES 512U
-#define FC_WORDS (FC_BYTES >> 2)
-
-#define BRCMNAND_MIN_PAGESIZE 512
-#define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
-#define BRCMNAND_MIN_DEVSIZE (4ULL * 1024 * 1024)
-
-#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY)
-#define NAND_POLL_STATUS_TIMEOUT_MS 100
-
-/* Controller feature flags */
-enum {
- BRCMNAND_HAS_1K_SECTORS = BIT(0),
- BRCMNAND_HAS_PREFETCH = BIT(1),
- BRCMNAND_HAS_CACHE_MODE = BIT(2),
- BRCMNAND_HAS_WP = BIT(3),
-};
-
-struct brcmnand_controller {
- struct device *dev;
- struct nand_hw_control controller;
- void __iomem *nand_base;
- void __iomem *nand_fc; /* flash cache */
- void __iomem *flash_dma_base;
- unsigned int irq;
- unsigned int dma_irq;
- int nand_version;
-
- /* Some SoCs provide custom interrupt status register(s) */
- struct brcmnand_soc *soc;
-
- /* Some SoCs have a gateable clock for the controller */
- struct clk *clk;
-
- int cmd_pending;
- bool dma_pending;
- struct completion done;
- struct completion dma_done;
-
- /* List of NAND hosts (one for each chip-select) */
- struct list_head host_list;
-
- struct brcm_nand_dma_desc *dma_desc;
- dma_addr_t dma_pa;
-
- /* in-memory cache of the FLASH_CACHE, used only for some commands */
- u8 flash_cache[FC_BYTES];
-
- /* Controller revision details */
- const u16 *reg_offsets;
- unsigned int reg_spacing; /* between CS1, CS2, ... regs */
- const u8 *cs_offsets; /* within each chip-select */
- const u8 *cs0_offsets; /* within CS0, if different */
- unsigned int max_block_size;
- const unsigned int *block_sizes;
- unsigned int max_page_size;
- const unsigned int *page_sizes;
- unsigned int max_oob;
- u32 features;
-
- /* for low-power standby/resume only */
- u32 nand_cs_nand_select;
- u32 nand_cs_nand_xor;
- u32 corr_stat_threshold;
- u32 flash_dma_mode;
-};
-
-struct brcmnand_cfg {
- u64 device_size;
- unsigned int block_size;
- unsigned int page_size;
- unsigned int spare_area_size;
- unsigned int device_width;
- unsigned int col_adr_bytes;
- unsigned int blk_adr_bytes;
- unsigned int ful_adr_bytes;
- unsigned int sector_size_1k;
- unsigned int ecc_level;
- /* use for low-power standby/resume only */
- u32 acc_control;
- u32 config;
- u32 config_ext;
- u32 timing_1;
- u32 timing_2;
-};
-
-struct brcmnand_host {
- struct list_head node;
-
- struct nand_chip chip;
- struct platform_device *pdev;
- int cs;
-
- unsigned int last_cmd;
- unsigned int last_byte;
- u64 last_addr;
- struct brcmnand_cfg hwcfg;
- struct brcmnand_controller *ctrl;
-};
-
-enum brcmnand_reg {
- BRCMNAND_CMD_START = 0,
- BRCMNAND_CMD_EXT_ADDRESS,
- BRCMNAND_CMD_ADDRESS,
- BRCMNAND_INTFC_STATUS,
- BRCMNAND_CS_SELECT,
- BRCMNAND_CS_XOR,
- BRCMNAND_LL_OP,
- BRCMNAND_CS0_BASE,
- BRCMNAND_CS1_BASE, /* CS1 regs, if non-contiguous */
- BRCMNAND_CORR_THRESHOLD,
- BRCMNAND_CORR_THRESHOLD_EXT,
- BRCMNAND_UNCORR_COUNT,
- BRCMNAND_CORR_COUNT,
- BRCMNAND_CORR_EXT_ADDR,
- BRCMNAND_CORR_ADDR,
- BRCMNAND_UNCORR_EXT_ADDR,
- BRCMNAND_UNCORR_ADDR,
- BRCMNAND_SEMAPHORE,
- BRCMNAND_ID,
- BRCMNAND_ID_EXT,
- BRCMNAND_LL_RDATA,
- BRCMNAND_OOB_READ_BASE,
- BRCMNAND_OOB_READ_10_BASE, /* offset 0x10, if non-contiguous */
- BRCMNAND_OOB_WRITE_BASE,
- BRCMNAND_OOB_WRITE_10_BASE, /* offset 0x10, if non-contiguous */
- BRCMNAND_FC_BASE,
-};
-
-/* BRCMNAND v4.0 */
-static const u16 brcmnand_regs_v40[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x6c,
- [BRCMNAND_CS_SELECT] = 0x14,
- [BRCMNAND_CS_XOR] = 0x18,
- [BRCMNAND_LL_OP] = 0x178,
- [BRCMNAND_CS0_BASE] = 0x40,
- [BRCMNAND_CS1_BASE] = 0xd0,
- [BRCMNAND_CORR_THRESHOLD] = 0x84,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
- [BRCMNAND_UNCORR_COUNT] = 0,
- [BRCMNAND_CORR_COUNT] = 0,
- [BRCMNAND_CORR_EXT_ADDR] = 0x70,
- [BRCMNAND_CORR_ADDR] = 0x74,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
- [BRCMNAND_UNCORR_ADDR] = 0x7c,
- [BRCMNAND_SEMAPHORE] = 0x58,
- [BRCMNAND_ID] = 0x60,
- [BRCMNAND_ID_EXT] = 0x64,
- [BRCMNAND_LL_RDATA] = 0x17c,
- [BRCMNAND_OOB_READ_BASE] = 0x20,
- [BRCMNAND_OOB_READ_10_BASE] = 0x130,
- [BRCMNAND_OOB_WRITE_BASE] = 0x30,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x200,
-};
-
-/* BRCMNAND v5.0 */
-static const u16 brcmnand_regs_v50[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x6c,
- [BRCMNAND_CS_SELECT] = 0x14,
- [BRCMNAND_CS_XOR] = 0x18,
- [BRCMNAND_LL_OP] = 0x178,
- [BRCMNAND_CS0_BASE] = 0x40,
- [BRCMNAND_CS1_BASE] = 0xd0,
- [BRCMNAND_CORR_THRESHOLD] = 0x84,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0,
- [BRCMNAND_UNCORR_COUNT] = 0,
- [BRCMNAND_CORR_COUNT] = 0,
- [BRCMNAND_CORR_EXT_ADDR] = 0x70,
- [BRCMNAND_CORR_ADDR] = 0x74,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x78,
- [BRCMNAND_UNCORR_ADDR] = 0x7c,
- [BRCMNAND_SEMAPHORE] = 0x58,
- [BRCMNAND_ID] = 0x60,
- [BRCMNAND_ID_EXT] = 0x64,
- [BRCMNAND_LL_RDATA] = 0x17c,
- [BRCMNAND_OOB_READ_BASE] = 0x20,
- [BRCMNAND_OOB_READ_10_BASE] = 0x130,
- [BRCMNAND_OOB_WRITE_BASE] = 0x30,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0x140,
- [BRCMNAND_FC_BASE] = 0x200,
-};
-
-/* BRCMNAND v6.0 - v7.1 */
-static const u16 brcmnand_regs_v60[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x14,
- [BRCMNAND_CS_SELECT] = 0x18,
- [BRCMNAND_CS_XOR] = 0x1c,
- [BRCMNAND_LL_OP] = 0x20,
- [BRCMNAND_CS0_BASE] = 0x50,
- [BRCMNAND_CS1_BASE] = 0,
- [BRCMNAND_CORR_THRESHOLD] = 0xc0,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4,
- [BRCMNAND_UNCORR_COUNT] = 0xfc,
- [BRCMNAND_CORR_COUNT] = 0x100,
- [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
- [BRCMNAND_CORR_ADDR] = 0x110,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
- [BRCMNAND_UNCORR_ADDR] = 0x118,
- [BRCMNAND_SEMAPHORE] = 0x150,
- [BRCMNAND_ID] = 0x194,
- [BRCMNAND_ID_EXT] = 0x198,
- [BRCMNAND_LL_RDATA] = 0x19c,
- [BRCMNAND_OOB_READ_BASE] = 0x200,
- [BRCMNAND_OOB_READ_10_BASE] = 0,
- [BRCMNAND_OOB_WRITE_BASE] = 0x280,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x400,
-};
-
-/* BRCMNAND v7.1 */
-static const u16 brcmnand_regs_v71[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x14,
- [BRCMNAND_CS_SELECT] = 0x18,
- [BRCMNAND_CS_XOR] = 0x1c,
- [BRCMNAND_LL_OP] = 0x20,
- [BRCMNAND_CS0_BASE] = 0x50,
- [BRCMNAND_CS1_BASE] = 0,
- [BRCMNAND_CORR_THRESHOLD] = 0xdc,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
- [BRCMNAND_UNCORR_COUNT] = 0xfc,
- [BRCMNAND_CORR_COUNT] = 0x100,
- [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
- [BRCMNAND_CORR_ADDR] = 0x110,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
- [BRCMNAND_UNCORR_ADDR] = 0x118,
- [BRCMNAND_SEMAPHORE] = 0x150,
- [BRCMNAND_ID] = 0x194,
- [BRCMNAND_ID_EXT] = 0x198,
- [BRCMNAND_LL_RDATA] = 0x19c,
- [BRCMNAND_OOB_READ_BASE] = 0x200,
- [BRCMNAND_OOB_READ_10_BASE] = 0,
- [BRCMNAND_OOB_WRITE_BASE] = 0x280,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x400,
-};
-
-/* BRCMNAND v7.2 */
-static const u16 brcmnand_regs_v72[] = {
- [BRCMNAND_CMD_START] = 0x04,
- [BRCMNAND_CMD_EXT_ADDRESS] = 0x08,
- [BRCMNAND_CMD_ADDRESS] = 0x0c,
- [BRCMNAND_INTFC_STATUS] = 0x14,
- [BRCMNAND_CS_SELECT] = 0x18,
- [BRCMNAND_CS_XOR] = 0x1c,
- [BRCMNAND_LL_OP] = 0x20,
- [BRCMNAND_CS0_BASE] = 0x50,
- [BRCMNAND_CS1_BASE] = 0,
- [BRCMNAND_CORR_THRESHOLD] = 0xdc,
- [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0,
- [BRCMNAND_UNCORR_COUNT] = 0xfc,
- [BRCMNAND_CORR_COUNT] = 0x100,
- [BRCMNAND_CORR_EXT_ADDR] = 0x10c,
- [BRCMNAND_CORR_ADDR] = 0x110,
- [BRCMNAND_UNCORR_EXT_ADDR] = 0x114,
- [BRCMNAND_UNCORR_ADDR] = 0x118,
- [BRCMNAND_SEMAPHORE] = 0x150,
- [BRCMNAND_ID] = 0x194,
- [BRCMNAND_ID_EXT] = 0x198,
- [BRCMNAND_LL_RDATA] = 0x19c,
- [BRCMNAND_OOB_READ_BASE] = 0x200,
- [BRCMNAND_OOB_READ_10_BASE] = 0,
- [BRCMNAND_OOB_WRITE_BASE] = 0x400,
- [BRCMNAND_OOB_WRITE_10_BASE] = 0,
- [BRCMNAND_FC_BASE] = 0x600,
-};
-
-enum brcmnand_cs_reg {
- BRCMNAND_CS_CFG_EXT = 0,
- BRCMNAND_CS_CFG,
- BRCMNAND_CS_ACC_CONTROL,
- BRCMNAND_CS_TIMING1,
- BRCMNAND_CS_TIMING2,
-};
-
-/* Per chip-select offsets for v7.1 */
-static const u8 brcmnand_cs_offsets_v71[] = {
- [BRCMNAND_CS_ACC_CONTROL] = 0x00,
- [BRCMNAND_CS_CFG_EXT] = 0x04,
- [BRCMNAND_CS_CFG] = 0x08,
- [BRCMNAND_CS_TIMING1] = 0x0c,
- [BRCMNAND_CS_TIMING2] = 0x10,
-};
-
-/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
-static const u8 brcmnand_cs_offsets[] = {
- [BRCMNAND_CS_ACC_CONTROL] = 0x00,
- [BRCMNAND_CS_CFG_EXT] = 0x04,
- [BRCMNAND_CS_CFG] = 0x04,
- [BRCMNAND_CS_TIMING1] = 0x08,
- [BRCMNAND_CS_TIMING2] = 0x0c,
-};
-
-/* Per chip-select offset for <= v5.0 on CS0 only */
-static const u8 brcmnand_cs_offsets_cs0[] = {
- [BRCMNAND_CS_ACC_CONTROL] = 0x00,
- [BRCMNAND_CS_CFG_EXT] = 0x08,
- [BRCMNAND_CS_CFG] = 0x08,
- [BRCMNAND_CS_TIMING1] = 0x10,
- [BRCMNAND_CS_TIMING2] = 0x14,
-};
-
-/*
- * Bitfields for the CFG and CFG_EXT registers. Pre-v7.1 controllers only had
- * one config register, but once the bitfields overflowed, newer controllers
- * (v7.1 and newer) added a CFG_EXT register and shuffled a few fields around.
- */
-enum {
- CFG_BLK_ADR_BYTES_SHIFT = 8,
- CFG_COL_ADR_BYTES_SHIFT = 12,
- CFG_FUL_ADR_BYTES_SHIFT = 16,
- CFG_BUS_WIDTH_SHIFT = 23,
- CFG_BUS_WIDTH = BIT(CFG_BUS_WIDTH_SHIFT),
- CFG_DEVICE_SIZE_SHIFT = 24,
-
- /* Only for pre-v7.1 (with no CFG_EXT register) */
- CFG_PAGE_SIZE_SHIFT = 20,
- CFG_BLK_SIZE_SHIFT = 28,
-
- /* Only for v7.1+ (with CFG_EXT register) */
- CFG_EXT_PAGE_SIZE_SHIFT = 0,
- CFG_EXT_BLK_SIZE_SHIFT = 4,
-};
-
-/* BRCMNAND_INTFC_STATUS */
-enum {
- INTFC_FLASH_STATUS = GENMASK(7, 0),
-
- INTFC_ERASED = BIT(27),
- INTFC_OOB_VALID = BIT(28),
- INTFC_CACHE_VALID = BIT(29),
- INTFC_FLASH_READY = BIT(30),
- INTFC_CTLR_READY = BIT(31),
-};
-
-static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
-{
- return brcmnand_readl(ctrl->nand_base + offs);
-}
-
-static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
- u32 val)
-{
- brcmnand_writel(val, ctrl->nand_base + offs);
-}
-
-static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
-{
- static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
- static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
- static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
-
- ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
-
- /* Only support v4.0+? */
- if (ctrl->nand_version < 0x0400) {
- dev_err(ctrl->dev, "version %#x not supported\n",
- ctrl->nand_version);
- return -ENODEV;
- }
-
- /* Register offsets */
- if (ctrl->nand_version >= 0x0702)
- ctrl->reg_offsets = brcmnand_regs_v72;
- else if (ctrl->nand_version >= 0x0701)
- ctrl->reg_offsets = brcmnand_regs_v71;
- else if (ctrl->nand_version >= 0x0600)
- ctrl->reg_offsets = brcmnand_regs_v60;
- else if (ctrl->nand_version >= 0x0500)
- ctrl->reg_offsets = brcmnand_regs_v50;
- else if (ctrl->nand_version >= 0x0400)
- ctrl->reg_offsets = brcmnand_regs_v40;
-
- /* Chip-select stride */
- if (ctrl->nand_version >= 0x0701)
- ctrl->reg_spacing = 0x14;
- else
- ctrl->reg_spacing = 0x10;
-
- /* Per chip-select registers */
- if (ctrl->nand_version >= 0x0701) {
- ctrl->cs_offsets = brcmnand_cs_offsets_v71;
- } else {
- ctrl->cs_offsets = brcmnand_cs_offsets;
-
- /* v5.0 and earlier has a different CS0 offset layout */
- if (ctrl->nand_version <= 0x0500)
- ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
- }
-
- /* Page / block sizes */
- if (ctrl->nand_version >= 0x0701) {
- /* >= v7.1 use nice power-of-2 values! */
- ctrl->max_page_size = 16 * 1024;
- ctrl->max_block_size = 2 * 1024 * 1024;
- } else {
- ctrl->page_sizes = page_sizes;
- if (ctrl->nand_version >= 0x0600)
- ctrl->block_sizes = block_sizes_v6;
- else
- ctrl->block_sizes = block_sizes_v4;
-
- if (ctrl->nand_version < 0x0400) {
- ctrl->max_page_size = 4096;
- ctrl->max_block_size = 512 * 1024;
- }
- }
-
- /* Maximum spare area sector size (per 512B) */
- if (ctrl->nand_version >= 0x0702)
- ctrl->max_oob = 128;
- else if (ctrl->nand_version >= 0x0600)
- ctrl->max_oob = 64;
- else if (ctrl->nand_version >= 0x0500)
- ctrl->max_oob = 32;
- else
- ctrl->max_oob = 16;
-
- /* v6.0 and newer (except v6.1) have prefetch support */
- if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
- ctrl->features |= BRCMNAND_HAS_PREFETCH;
-
- /*
- * v6.x has cache mode, but it's implemented differently. Ignore it for
- * now.
- */
- if (ctrl->nand_version >= 0x0700)
- ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
-
- if (ctrl->nand_version >= 0x0500)
- ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
-
- if (ctrl->nand_version >= 0x0700)
- ctrl->features |= BRCMNAND_HAS_WP;
- else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
- ctrl->features |= BRCMNAND_HAS_WP;
-
- return 0;
-}
-
-static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
- enum brcmnand_reg reg)
-{
- u16 offs = ctrl->reg_offsets[reg];
-
- if (offs)
- return nand_readreg(ctrl, offs);
- else
- return 0;
-}
-
-static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
- enum brcmnand_reg reg, u32 val)
-{
- u16 offs = ctrl->reg_offsets[reg];
-
- if (offs)
- nand_writereg(ctrl, offs, val);
-}
-
-static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
- enum brcmnand_reg reg, u32 mask, unsigned
- int shift, u32 val)
-{
- u32 tmp = brcmnand_read_reg(ctrl, reg);
-
- tmp &= ~mask;
- tmp |= val << shift;
- brcmnand_write_reg(ctrl, reg, tmp);
-}
-
-static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
-{
- return __raw_readl(ctrl->nand_fc + word * 4);
-}
-
-static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
- int word, u32 val)
-{
- __raw_writel(val, ctrl->nand_fc + word * 4);
-}
-
-static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
- enum brcmnand_cs_reg reg)
-{
- u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
- u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
- u8 cs_offs;
-
- if (cs == 0 && ctrl->cs0_offsets)
- cs_offs = ctrl->cs0_offsets[reg];
- else
- cs_offs = ctrl->cs_offsets[reg];
-
- if (cs && offs_cs1)
- return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
-
- return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
-}
-
-static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version < 0x0600)
- return 1;
- return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
-}
-
-static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- unsigned int shift = 0, bits;
- enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
- int cs = host->cs;
-
- if (ctrl->nand_version >= 0x0702)
- bits = 7;
- else if (ctrl->nand_version >= 0x0600)
- bits = 6;
- else if (ctrl->nand_version >= 0x0500)
- bits = 5;
- else
- bits = 4;
-
- if (ctrl->nand_version >= 0x0702) {
- if (cs >= 4)
- reg = BRCMNAND_CORR_THRESHOLD_EXT;
- shift = (cs % 4) * bits;
- } else if (ctrl->nand_version >= 0x0600) {
- if (cs >= 5)
- reg = BRCMNAND_CORR_THRESHOLD_EXT;
- shift = (cs % 5) * bits;
- }
- brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
-}
-
-static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version < 0x0602)
- return 24;
- return 0;
-}
-
-/***********************************************************************
- * NAND ACC CONTROL bitfield
- *
- * Some bits have remained constant throughout hardware revision, while
- * others have shifted around.
- ***********************************************************************/
-
-/* Constant for all versions (where supported) */
-enum {
- /* See BRCMNAND_HAS_CACHE_MODE */
- ACC_CONTROL_CACHE_MODE = BIT(22),
-
- /* See BRCMNAND_HAS_PREFETCH */
- ACC_CONTROL_PREFETCH = BIT(23),
-
- ACC_CONTROL_PAGE_HIT = BIT(24),
- ACC_CONTROL_WR_PREEMPT = BIT(25),
- ACC_CONTROL_PARTIAL_PAGE = BIT(26),
- ACC_CONTROL_RD_ERASED = BIT(27),
- ACC_CONTROL_FAST_PGM_RDIN = BIT(28),
- ACC_CONTROL_WR_ECC = BIT(30),
- ACC_CONTROL_RD_ECC = BIT(31),
-};
-
-static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version >= 0x0702)
- return GENMASK(7, 0);
- else if (ctrl->nand_version >= 0x0600)
- return GENMASK(6, 0);
- else
- return GENMASK(5, 0);
-}
-
-#define NAND_ACC_CONTROL_ECC_SHIFT 16
-#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13
-
-static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
-{
- u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
-
- mask <<= NAND_ACC_CONTROL_ECC_SHIFT;
-
- /* v7.2 includes additional ECC levels */
- if (ctrl->nand_version >= 0x0702)
- mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT;
-
- return mask;
-}
-
-static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
- u32 acc_control = nand_readreg(ctrl, offs);
- u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
-
- if (en) {
- acc_control |= ecc_flags; /* enable RD/WR ECC */
- acc_control |= host->hwcfg.ecc_level
- << NAND_ACC_CONTROL_ECC_SHIFT;
- } else {
- acc_control &= ~ecc_flags; /* disable RD/WR ECC */
- acc_control &= ~brcmnand_ecc_level_mask(ctrl);
- }
-
- nand_writereg(ctrl, offs, acc_control);
-}
-
-static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
-{
- if (ctrl->nand_version >= 0x0702)
- return 9;
- else if (ctrl->nand_version >= 0x0600)
- return 7;
- else if (ctrl->nand_version >= 0x0500)
- return 6;
- else
- return -1;
-}
-
-static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- int shift = brcmnand_sector_1k_shift(ctrl);
- u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_ACC_CONTROL);
-
- if (shift < 0)
- return 0;
-
- return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
-}
-
-static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
-{
- struct brcmnand_controller *ctrl = host->ctrl;
- int shift = brcmnand_sector_1k_shift(ctrl);
- u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
- BRCMNAND_CS_ACC_CONTROL);
- u32 tmp;
-
- if (shift < 0)
- return;
-
- tmp = nand_readreg(ctrl, acc_control_offs);
- tmp &= ~(1 << shift);
- tmp |= (!!val) << shift;
- nand_writereg(ctrl, acc_control_offs, tmp);
-}
-
-/***********************************************************************
- * CS_NAND_SELECT
- ***********************************************************************/
-
-enum {
- CS_SELECT_NAND_WP = BIT(29),
- CS_SELECT_AUTO_DEVICE_ID_CFG = BIT(30),
-};
-
-static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
- u32 mask, u32 expected_val,
- unsigned long timeout_ms)
-{
- unsigned long limit;
- u32 val;
-
- if (!timeout_ms)
- timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
-
- limit = jiffies + msecs_to_jiffies(timeout_ms);
- do {
- val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
- if ((val & mask) == expected_val)
- return 0;
-
- cpu_relax();
- } while (time_after(limit, jiffies));
-
- dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
- expected_val, val & mask);
-
- return -ETIMEDOUT;
-}
-
-static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
-{
- u32 val = en ? CS_SELECT_NAND_WP : 0;
-
- brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
-}
-
-/***********************************************************************
- * Flash DMA
- ***********************************************************************/
-
-enum flash_dma_reg {
- FLASH_DMA_REVISION = 0x00,
- FLASH_DMA_FIRST_DESC = 0x04,
- FLASH_DMA_FIRST_DESC_EXT = 0x08,
- FLASH_DMA_CTRL = 0x0c,
- FLASH_DMA_MODE = 0x10,
- FLASH_DMA_STATUS = 0x14,
- FLASH_DMA_INTERRUPT_DESC = 0x18,
- FLASH_DMA_INTERRUPT_DESC_EXT = 0x1c,
- FLASH_DMA_ERROR_STATUS = 0x20,
- FLASH_DMA_CURRENT_DESC = 0x24,
- FLASH_DMA_CURRENT_DESC_EXT = 0x28,
-};
-
-static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
-{
- return ctrl->flash_dma_base;
-}
-
-static inline bool flash_dma_buf_ok(const void *buf)
-{
- return buf && !is_vmalloc_addr(buf) &&
- likely(IS_ALIGNED((uintptr_t)buf, 4));
-}
-
-static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
- u32 val)
-{
- brcmnand_writel(val, ctrl->flash_dma_base + offs);
-}
-
-static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
-{
- return brcmnand_readl(ctrl->flash_dma_base + offs);
-}
-
-/* Low-level operation types: command, address, write, or read */
-enum brcmnand_llop_type {
- LL_OP_CMD,
- LL_OP_ADDR,
- LL_OP_WR,
- LL_OP_RD,
-};
-
-/***********************************************************************
- * Internal support functions
- ***********************************************************************/
-
-static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
- struct brcmnand_cfg *cfg)
-{
- if (ctrl->nand_version <= 0x0701)
- return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
- cfg->ecc_level == 15;
- else
- return cfg->sector_size_1k == 0 && ((cfg->spare_area_size == 16 &&
- cfg->ecc_level == 15) ||
- (cfg->spare_area_size == 28 && cfg->ecc_level == 16));
-}
-
-/*
- * Set mtd->ooblayout to the appropriate mtd_ooblayout_ops given
- * the layout/configuration.
- * Returns -ERRCODE on failure.
- */
-static int brcmnand_hamming_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors)
- return -ERANGE;
-
- oobregion->offset = (section * sas) + 6;
- oobregion->length = 3;
-
- return 0;
-}
-
-static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors * 2)
- return -ERANGE;
-
- oobregion->offset = (section / 2) * sas;
-
- if (section & 1) {
- oobregion->offset += 9;
- oobregion->length = 7;
- } else {
- oobregion->length = 6;
-
- /* First sector of each page may have BBI */
- if (!section) {
- /*
- * Small-page NAND use byte 6 for BBI while large-page
- * NAND use byte 0.
- */
- if (cfg->page_size > 512)
- oobregion->offset++;
- oobregion->length--;
- }
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops = {
- .ecc = brcmnand_hamming_ooblayout_ecc,
- .free = brcmnand_hamming_ooblayout_free,
-};
-
-static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors)
- return -ERANGE;
-
- oobregion->offset = (section * (sas + 1)) - chip->ecc.bytes;
- oobregion->length = chip->ecc.bytes;
-
- return 0;
-}
-
-static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
- int sectors = cfg->page_size / (512 << cfg->sector_size_1k);
-
- if (section >= sectors)
- return -ERANGE;
-
- if (sas <= chip->ecc.bytes)
- return 0;
-
- oobregion->offset = section * sas;
- oobregion->length = sas - chip->ecc.bytes;
-
- if (!section) {
- oobregion->offset++;
- oobregion->length--;
- }
-
- return 0;
-}
-
-static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
- struct mtd_oob_region *oobregion)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_cfg *cfg = &host->hwcfg;
- int sas = cfg->spare_area_size << cfg->sector_size_1k;
-
- if (section > 1 || sas - chip->ecc.bytes < 6 ||
- (section && sas - chip->ecc.bytes == 6))
- return -ERANGE;
-
- if (!section) {
- oobregion->offset = 0;
- oobregion->length = 5;
- } else {
- oobregion->offset = 6;
- oobregion->length = sas - chip->ecc.bytes - 6;
- }
-
- return 0;
-}
-
-static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops = {
- .ecc = brcmnand_bch_ooblayout_ecc,
- .free = brcmnand_bch_ooblayout_free_lp,
-};
-
-static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops = {
- .ecc = brcmnand_bch_ooblayout_ecc,
- .free = brcmnand_bch_ooblayout_free_sp,
-};
-
-static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
-{
- struct brcmnand_cfg *p = &host->hwcfg;
- struct mtd_info *mtd = nand_to_mtd(&host->chip);
- struct nand_ecc_ctrl *ecc = &host->chip.ecc;
- unsigned int ecc_level = p->ecc_level;
- int sas = p->spare_area_size << p->sector_size_1k;
- int sectors = p->page_size / (512 << p->sector_size_1k);
-
- if (p->sector_size_1k)
- ecc_level <<= 1;
-
- if (is_hamming_ecc(host->ctrl, p)) {
- ecc->bytes = 3 * sectors;
- mtd_set_ooblayout(mtd, &brcmnand_hamming_ooblayout_ops);
- return 0;
- }
-
- /*
- * CONTROLLER_VERSION:
- * < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
- * >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
- * But we will just be conservative.
- */
- ecc->bytes = DIV_ROUND_UP(ecc_level * 14, 8);
- if (p->page_size == 512)
- mtd_set_ooblayout(mtd, &brcmnand_bch_sp_ooblayout_ops);
- else
- mtd_set_ooblayout(mtd, &brcmnand_bch_lp_ooblayout_ops);
-
- if (ecc->bytes >= sas) {
- dev_err(&host->pdev->dev,
- "error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
- ecc->bytes, sas);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static void brcmnand_wp(struct mtd_info *mtd, int wp)
-{
- struct nand_chip *chip = mtd_to_nand(mtd);
- struct brcmnand_host *host = nand_get_controller_data(chip);
- struct brcmnand_controller *ctrl = host->ctrl;
-
- if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
- static int old_wp = -1;
- int ret;
-
- if (old_wp != wp) {
- dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
- old_wp = wp;
- }
-
- /*
- * make sure ctrl/flash ready before and after
- * changing state of #WP pin
- */
- ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
- NAND_STATUS_READY,
- NAND_CTRL_RDY |
- NAND_STATUS_READY, 0);
- if (ret)
- return;
-
- brcmnand_set_wp(ctrl, wp);
- nand_status_op(chip, NULL);
- /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
- ret = bcmnand_ctrl_poll_status(ctrl,
- NAND_CTRL_RDY |
- NAND_STATUS_READY |
- NAND_STATUS_WP,
- NAND_CTRL_RDY |
- NAND_STATUS_READY |
- (wp ? 0 : NAND_STATUS_WP), 0);
-
- if (ret)
- dev_err_ratelimited(&host->pdev->dev,
- "nand #WP expected %s\n",
- wp ? "on" : "off");
- }
-}
-
-/* Helper functions for reading and writing OOB registers */
-static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
-{
- u16 offset0, offset10, reg_offs;
-
- offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
- offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
-
- if (offs >= ctrl->max_oob)
- return 0x77;
-
- if (offs >= 16 && offset10)
- reg_offs = offset10 + ((offs - 0x10) & ~0x03);
- else
- reg_offs = offset0 + (offs & ~0x03);
-
- return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
-}
-
-static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
- u32 data)
-{
- u16 offset0, offset10, reg_offs;
-
- offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
- offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
-
- if (offs >= ctrl->max_oob)
- return;
-
- if (offs >= 16 && offset10)
- reg_offs = offset10 + ((offs - 0x10) & ~0x03);
- else
- reg_offs = offset0 + (offs & ~0x03);
-
- nand_writereg(ctrl, reg_offs, data);
-}
-
-/*
- * read_oob_from_regs - read data from OOB registers