linux/drivers/mtd/nand/raw/brcmnand/brcmnand.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright © 2010-2015 Broadcom Corporation
 */

#include <linux/clk.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/platform_data/brcmnand.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/static_key.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 =;
module_param(wp_on, int, 0444);

/***********************************************************************
 * Definitions
 ***********************************************************************/

#define DRV_NAME

#define CMD_NULL
#define CMD_PAGE_READ
#define CMD_SPARE_AREA_READ
#define CMD_STATUS_READ
#define CMD_PROGRAM_PAGE
#define CMD_PROGRAM_SPARE_AREA
#define CMD_COPY_BACK
#define CMD_DEVICE_ID_READ
#define CMD_BLOCK_ERASE
#define CMD_FLASH_RESET
#define CMD_BLOCKS_LOCK
#define CMD_BLOCKS_LOCK_DOWN
#define CMD_BLOCKS_UNLOCK
#define CMD_READ_BLOCKS_LOCK_STATUS
#define CMD_PARAMETER_READ
#define CMD_PARAMETER_CHANGE_COL
#define CMD_LOW_LEVEL_OP

struct brcm_nand_dma_desc {} __packed;

/* Bitfields for brcm_nand_dma_desc::status_valid */
#define FLASH_DMA_ECC_ERROR
#define FLASH_DMA_CORR_ERROR

/* Bitfields for DMA_MODE */
#define FLASH_DMA_MODE_STOP_ON_ERROR
#define FLASH_DMA_MODE_MODE
#define FLASH_DMA_MODE_MASK

/* 512B flash cache in the NAND controller HW */
#define FC_SHIFT
#define FC_BYTES
#define FC_WORDS

#define BRCMNAND_MIN_PAGESIZE
#define BRCMNAND_MIN_BLOCKSIZE
#define BRCMNAND_MIN_DEVSIZE

#define NAND_CTRL_RDY
#define NAND_POLL_STATUS_TIMEOUT_MS

#define EDU_CMD_WRITE
#define EDU_CMD_READ
#define EDU_STATUS_ACTIVE
#define EDU_ERR_STATUS_ERRACK
#define EDU_DONE_MASK

#define EDU_CONFIG_MODE_NAND
#define EDU_CONFIG_SWAP_BYTE
#ifdef CONFIG_CPU_BIG_ENDIAN
#define EDU_CONFIG_SWAP_CFG
#else
#define EDU_CONFIG_SWAP_CFG
#endif

/* edu registers */
enum edu_reg {};

static const u16  edu_regs[] =;

/* flash_dma registers */
enum flash_dma_reg {};

/* flash_dma registers v0*/
static const u16 flash_dma_regs_v0[] =;

/* flash_dma registers v1*/
static const u16 flash_dma_regs_v1[] =;

/* flash_dma registers v4 */
static const u16 flash_dma_regs_v4[] =;

/* Controller feature flags */
enum {};

struct brcmnand_host;

static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key);

struct brcmnand_controller {};

struct brcmnand_cfg {};

struct brcmnand_host {};

enum brcmnand_reg {};

/* BRCMNAND v2.1-v2.2 */
static const u16 brcmnand_regs_v21[] =;

/* BRCMNAND v3.3-v4.0 */
static const u16 brcmnand_regs_v33[] =;

/* BRCMNAND v5.0 */
static const u16 brcmnand_regs_v50[] =;

/* BRCMNAND v6.0 - v7.1 */
static const u16 brcmnand_regs_v60[] =;

/* BRCMNAND v7.1 */
static const u16 brcmnand_regs_v71[] =;

/* BRCMNAND v7.2 */
static const u16 brcmnand_regs_v72[] =;

enum brcmnand_cs_reg {};

/* Per chip-select offsets for v7.1 */
static const u8 brcmnand_cs_offsets_v71[] =;

/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
static const u8 brcmnand_cs_offsets[] =;

/* Per chip-select offset for <= v5.0 on CS0 only */
static const u8 brcmnand_cs_offsets_cs0[] =;

/*
 * 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 {};

/* BRCMNAND_INTFC_STATUS */
enum {};

/***********************************************************************
 * NAND ACC CONTROL bitfield
 *
 * Some bits have remained constant throughout hardware revision, while
 * others have shifted around.
 ***********************************************************************/

/* Constant for all versions (where supported) */
enum {};

#define ACC_CONTROL_ECC_SHIFT
/* Only for v7.2 */
#define ACC_CONTROL_ECC_EXT_SHIFT

static int brcmnand_status(struct brcmnand_host *host);

static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
{}

static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
{}

static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
				 u32 val)
{}

static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
{}

static void brcmnand_flash_dma_revision_init(struct brcmnand_controller *ctrl)
{}

static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
		enum brcmnand_reg reg)
{}

static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
				      enum brcmnand_reg reg, u32 val)
{}

static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
				    enum brcmnand_reg reg, u32 mask, unsigned
				    int shift, u32 val)
{}

static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
{}

static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
				     int word, u32 val)
{}

static inline void edu_writel(struct brcmnand_controller *ctrl,
			      enum edu_reg reg, u32 val)
{}

static inline u32 edu_readl(struct brcmnand_controller *ctrl,
			    enum edu_reg reg)
{}

static inline void brcmnand_read_data_bus(struct brcmnand_controller *ctrl,
					  void __iomem *flash_cache, u32 *buffer, int fc_words)
{}

static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl)
{}

static u64 brcmnand_get_uncorrecc_addr(struct brcmnand_controller *ctrl)
{}

static u64 brcmnand_get_correcc_addr(struct brcmnand_controller *ctrl)
{}

static void brcmnand_set_cmd_addr(struct mtd_info *mtd, u64 addr)
{}

static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
				     enum brcmnand_cs_reg reg)
{}

static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
{}

static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
{}

static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
{}

static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
{}

static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
{}

static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
{}

static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
{}

static bool brcmnand_get_sector_size_1k(struct brcmnand_host *host)
{}

static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
{}

static int brcmnand_get_spare_size(struct brcmnand_host *host)
{}

static void brcmnand_get_ecc_settings(struct brcmnand_host *host, struct nand_chip *chip)
{}

/***********************************************************************
 * CS_NAND_SELECT
 ***********************************************************************/

enum {};

static int bcmnand_ctrl_poll_status(struct brcmnand_host *host,
				    u32 mask, u32 expected_val,
				    unsigned long timeout_ms)
{}

static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
{}

/***********************************************************************
 * Flash DMA
 ***********************************************************************/

static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
{}

static inline bool has_edu(struct brcmnand_controller *ctrl)
{}

static inline bool use_dma(struct brcmnand_controller *ctrl)
{}

static inline void disable_ctrl_irqs(struct brcmnand_controller *ctrl)
{}

static inline bool flash_dma_buf_ok(const void *buf)
{}

static inline void flash_dma_writel(struct brcmnand_controller *ctrl,
				    enum flash_dma_reg dma_reg, u32 val)
{}

static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl,
				  enum flash_dma_reg dma_reg)
{}

/* Low-level operation types: command, address, write, or read */
enum brcmnand_llop_type {};

/***********************************************************************
 * Internal support functions
 ***********************************************************************/

static inline bool is_hamming_ecc(struct brcmnand_controller *ctrl,
				  struct brcmnand_cfg *cfg)
{}

/*
 * 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)
{}

static int brcmnand_hamming_ooblayout_free(struct mtd_info *mtd, int section,
					   struct mtd_oob_region *oobregion)
{}

static const struct mtd_ooblayout_ops brcmnand_hamming_ooblayout_ops =;

static int brcmnand_bch_ooblayout_ecc(struct mtd_info *mtd, int section,
				      struct mtd_oob_region *oobregion)
{}

static int brcmnand_bch_ooblayout_free_lp(struct mtd_info *mtd, int section,
					  struct mtd_oob_region *oobregion)
{}

static int brcmnand_bch_ooblayout_free_sp(struct mtd_info *mtd, int section,
					  struct mtd_oob_region *oobregion)
{}

static const struct mtd_ooblayout_ops brcmnand_bch_lp_ooblayout_ops =;

static const struct mtd_ooblayout_ops brcmnand_bch_sp_ooblayout_ops =;

static int brcmstb_choose_ecc_layout(struct brcmnand_host *host)
{}

static void brcmnand_wp(struct mtd_info *mtd, int wp)
{}

/* Helper functions for reading and writing OOB registers */
static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
{}

static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
				 u32 data)
{}

/*
 * read_oob_from_regs - read data from OOB registers
 * @ctrl: NAND controller
 * @i: sub-page sector index
 * @oob: buffer to read to
 * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
 * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
 */
static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
			      int sas, int sector_1k)
{}

/*
 * write_oob_to_regs - write data to OOB registers
 * @i: sub-page sector index
 * @oob: buffer to write from
 * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
 * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
 */
static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
			     const u8 *oob, int sas, int sector_1k)
{}

static void brcmnand_edu_init(struct brcmnand_controller *ctrl)
{}

/* edu irq */
static irqreturn_t brcmnand_edu_irq(int irq, void *data)
{}

static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
{}

/* Handle SoC-specific interrupt hardware */
static irqreturn_t brcmnand_irq(int irq, void *data)
{}

static irqreturn_t brcmnand_dma_irq(int irq, void *data)
{}

static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
{}

static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip)
{}

static int brcmnand_waitfunc(struct nand_chip *chip)
{}

static int brcmnand_status(struct brcmnand_host *host)
{}

static int brcmnand_reset(struct brcmnand_host *host)
{}

enum {};

static int brcmnand_low_level_op(struct brcmnand_host *host,
				 enum brcmnand_llop_type type, u32 data,
				 bool last_op)
{}

/*
 *  Kick EDU engine
 */
static int brcmnand_edu_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
			      u8 *oob, u32 len, u8 cmd)
{}

/*
 * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
 * following ahead of time:
 *  - Is this descriptor the beginning or end of a linked list?
 *  - What is the (DMA) address of the next descriptor in the linked list?
 */
static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
				  struct brcm_nand_dma_desc *desc, u64 addr,
				  dma_addr_t buf, u32 len, u8 dma_cmd,
				  bool begin, bool end,
				  dma_addr_t next_desc)
{}

/*
 * Kick the FLASH_DMA engine, with a given DMA descriptor
 */
static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
{}

static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
			      u8 *oob, u32 len, u8 dma_cmd)
{}

/*
 * Assumes proper CS is already set
 */
static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
				u64 addr, unsigned int trans, u32 *buf,
				u8 *oob, u64 *err_addr)
{}

/*
 * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC
 * error
 *
 * Because the HW ECC signals an ECC error if an erase paged has even a single
 * bitflip, we must check each ECC error to see if it is actually an erased
 * page with bitflips, not a truly corrupted page.
 *
 * On a real error, return a negative error code (-EBADMSG for ECC error), and
 * buf will contain raw data.
 * Otherwise, buf gets filled with 0xffs and return the maximum number of
 * bitflips-per-ECC-sector to the caller.
 *
 */
static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
		  struct nand_chip *chip, void *buf, u64 addr)
{}

static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
			 u64 addr, unsigned int trans, u32 *buf, u8 *oob)
{}

static int brcmnand_read_page(struct nand_chip *chip, uint8_t *buf,
			      int oob_required, int page)
{}

static int brcmnand_read_page_raw(struct nand_chip *chip, uint8_t *buf,
				  int oob_required, int page)
{}

static int brcmnand_read_oob(struct nand_chip *chip, int page)
{}

static int brcmnand_read_oob_raw(struct nand_chip *chip, int page)
{}

static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
			  u64 addr, const u32 *buf, u8 *oob)
{}

static int brcmnand_write_page(struct nand_chip *chip, const uint8_t *buf,
			       int oob_required, int page)
{}

static int brcmnand_write_page_raw(struct nand_chip *chip, const uint8_t *buf,
				   int oob_required, int page)
{}

static int brcmnand_write_oob(struct nand_chip *chip, int page)
{}

static int brcmnand_write_oob_raw(struct nand_chip *chip, int page)
{}

static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
		const struct nand_operation *op)
{}

static int brcmnand_op_is_status(const struct nand_operation *op)
{}

static int brcmnand_op_is_reset(const struct nand_operation *op)
{}

static int brcmnand_exec_op(struct nand_chip *chip,
			    const struct nand_operation *op,
			    bool check_only)
{}

/***********************************************************************
 * Per-CS setup (1 NAND device)
 ***********************************************************************/

static int brcmnand_set_cfg(struct brcmnand_host *host,
			    struct brcmnand_cfg *cfg)
{}

static void brcmnand_print_cfg(struct brcmnand_host *host,
			       char *buf, struct brcmnand_cfg *cfg)
{}

/*
 * Minimum number of bytes to address a page. Calculated as:
 *     roundup(log2(size / page-size) / 8)
 *
 * NB: the following does not "round up" for non-power-of-2 'size'; but this is
 *     OK because many other things will break if 'size' is irregular...
 */
static inline int get_blk_adr_bytes(u64 size, u32 writesize)
{}

static int brcmnand_setup_dev(struct brcmnand_host *host)
{}

static int brcmnand_attach_chip(struct nand_chip *chip)
{}

static const struct nand_controller_ops brcmnand_controller_ops =;

static int brcmnand_init_cs(struct brcmnand_host *host,
			    const char * const *part_probe_types)
{}

static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
					    int restore)
{}

static int brcmnand_suspend(struct device *dev)
{}

static int brcmnand_resume(struct device *dev)
{}

const struct dev_pm_ops brcmnand_pm_ops =;
EXPORT_SYMBOL_GPL();

static const struct of_device_id __maybe_unused brcmnand_of_match[] =;
MODULE_DEVICE_TABLE(of, brcmnand_of_match);

/***********************************************************************
 * Platform driver setup (per controller)
 ***********************************************************************/
static int brcmnand_edu_setup(struct platform_device *pdev)
{}

int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
{}
EXPORT_SYMBOL_GPL();

void brcmnand_remove(struct platform_device *pdev)
{}
EXPORT_SYMBOL_GPL();

MODULE_LICENSE();
MODULE_AUTHOR();
MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_ALIAS();