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

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright © 2004 Texas Instruments, Jian Zhang <[email protected]>
 * Copyright © 2004 Micron Technology Inc.
 * Copyright © 2004 David Brownell
 */

#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand-ecc-sw-bch.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#include <linux/omap-dma.h>
#include <linux/iopoll.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_platform.h>

#include <linux/platform_data/elm.h>

#include <linux/omap-gpmc.h>
#include <linux/platform_data/mtd-nand-omap2.h>

#define DRIVER_NAME
#define OMAP_NAND_TIMEOUT_MS

#define NAND_Ecc_P1e
#define NAND_Ecc_P2e
#define NAND_Ecc_P4e
#define NAND_Ecc_P8e
#define NAND_Ecc_P16e
#define NAND_Ecc_P32e
#define NAND_Ecc_P64e
#define NAND_Ecc_P128e
#define NAND_Ecc_P256e
#define NAND_Ecc_P512e
#define NAND_Ecc_P1024e
#define NAND_Ecc_P2048e

#define NAND_Ecc_P1o
#define NAND_Ecc_P2o
#define NAND_Ecc_P4o
#define NAND_Ecc_P8o
#define NAND_Ecc_P16o
#define NAND_Ecc_P32o
#define NAND_Ecc_P64o
#define NAND_Ecc_P128o
#define NAND_Ecc_P256o
#define NAND_Ecc_P512o
#define NAND_Ecc_P1024o
#define NAND_Ecc_P2048o

#define TF(value)

#define P2048e(a)
#define P2048o(a)
#define P1e(a)
#define P1o(a)
#define P2e(a)
#define P2o(a)
#define P4e(a)
#define P4o(a)

#define P8e(a)
#define P8o(a)
#define P16e(a)
#define P16o(a)
#define P32e(a)
#define P32o(a)
#define P64e(a)
#define P64o(a)

#define P128e(a)
#define P128o(a)
#define P256e(a)
#define P256o(a)
#define P512e(a)
#define P512o(a)
#define P1024e(a)
#define P1024o(a)

#define P8e_s(a)
#define P8o_s(a)
#define P16e_s(a)
#define P16o_s(a)
#define P1e_s(a)
#define P1o_s(a)
#define P2e_s(a)
#define P2o_s(a)

#define P4e_s(a)
#define P4o_s(a)

#define PREFETCH_CONFIG1_CS_SHIFT
#define ECC_CONFIG_CS_SHIFT
#define CS_MASK
#define ENABLE_PREFETCH
#define DMA_MPU_MODE_SHIFT
#define ECCSIZE0_SHIFT
#define ECCSIZE1_SHIFT
#define ECC1RESULTSIZE
#define ECCCLEAR
#define ECC1
#define PREFETCH_FIFOTHRESHOLD_MAX
#define PREFETCH_FIFOTHRESHOLD(val)
#define PREFETCH_STATUS_COUNT(val)
#define PREFETCH_STATUS_FIFO_CNT(val)
#define STATUS_BUFF_EMPTY

#define SECTOR_BYTES
/* 4 bit padding to make byte aligned, 56 = 52 + 4 */
#define BCH4_BIT_PAD

/* GPMC ecc engine settings for read */
#define BCH_WRAPMODE_1
#define BCH8R_ECC_SIZE0
#define BCH8R_ECC_SIZE1
#define BCH4R_ECC_SIZE0
#define BCH4R_ECC_SIZE1

/* GPMC ecc engine settings for write */
#define BCH_WRAPMODE_6
#define BCH_ECC_SIZE0
#define BCH_ECC_SIZE1

#define BBM_LEN

static u_char bch16_vector[] =;
static u_char bch8_vector[] =;
static u_char bch4_vector[] =;

struct omap_nand_info {};

static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
{}

static void omap_nand_data_in(struct nand_chip *chip, void *buf,
			      unsigned int len, bool force_8bit);

static void omap_nand_data_out(struct nand_chip *chip,
			       const void *buf, unsigned int len,
			       bool force_8bit);

/**
 * omap_prefetch_enable - configures and starts prefetch transfer
 * @cs: cs (chip select) number
 * @fifo_th: fifo threshold to be used for read/ write
 * @dma_mode: dma mode enable (1) or disable (0)
 * @u32_count: number of bytes to be transferred
 * @is_write: prefetch read(0) or write post(1) mode
 * @info: NAND device structure containing platform data
 */
static int omap_prefetch_enable(int cs, int fifo_th, int dma_mode,
	unsigned int u32_count, int is_write, struct omap_nand_info *info)
{}

/*
 * omap_prefetch_reset - disables and stops the prefetch engine
 */
static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
{}

/**
 * omap_nand_data_in_pref - NAND data in using prefetch engine
 */
static void omap_nand_data_in_pref(struct nand_chip *chip, void *buf,
				   unsigned int len, bool force_8bit)
{}

/**
 * omap_nand_data_out_pref - NAND data out using Write Posting engine
 */
static void omap_nand_data_out_pref(struct nand_chip *chip,
				    const void *buf, unsigned int len,
				    bool force_8bit)
{}

/*
 * omap_nand_dma_callback: callback on the completion of dma transfer
 * @data: pointer to completion data structure
 */
static void omap_nand_dma_callback(void *data)
{}

/*
 * omap_nand_dma_transfer: configure and start dma transfer
 * @chip: nand chip structure
 * @addr: virtual address in RAM of source/destination
 * @len: number of data bytes to be transferred
 * @is_write: flag for read/write operation
 */
static inline int omap_nand_dma_transfer(struct nand_chip *chip,
					 const void *addr, unsigned int len,
					 int is_write)
{}

/**
 * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch
 */
static void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf,
				       unsigned int len, bool force_8bit)
{}

/**
 * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting
 */
static void omap_nand_data_out_dma_pref(struct nand_chip *chip,
					const void *buf, unsigned int len,
					bool force_8bit)
{}

/*
 * omap_nand_irq - GPMC irq handler
 * @this_irq: gpmc irq number
 * @dev: omap_nand_info structure pointer is passed here
 */
static irqreturn_t omap_nand_irq(int this_irq, void *dev)
{}

/*
 * omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ
 */
static void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf,
				       unsigned int len, bool force_8bit)
{}

/*
 * omap_nand_data_out_irq_pref - NAND out using write posting and IRQ
 */
static void omap_nand_data_out_irq_pref(struct nand_chip *chip,
					const void *buf, unsigned int len,
					bool force_8bit)
{}

/**
 * gen_true_ecc - This function will generate true ECC value
 * @ecc_buf: buffer to store ecc code
 *
 * This generated true ECC value can be used when correcting
 * data read from NAND flash memory core
 */
static void gen_true_ecc(u8 *ecc_buf)
{}

/**
 * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data
 * @ecc_data1:  ecc code from nand spare area
 * @ecc_data2:  ecc code from hardware register obtained from hardware ecc
 * @page_data:  page data
 *
 * This function compares two ECC's and indicates if there is an error.
 * If the error can be corrected it will be corrected to the buffer.
 * If there is no error, %0 is returned. If there is an error but it
 * was corrected, %1 is returned. Otherwise, %-1 is returned.
 */
static int omap_compare_ecc(u8 *ecc_data1,	/* read from NAND memory */
			    u8 *ecc_data2,	/* read from register */
			    u8 *page_data)
{}

/**
 * omap_correct_data - Compares the ECC read with HW generated ECC
 * @chip: NAND chip object
 * @dat: page data
 * @read_ecc: ecc read from nand flash
 * @calc_ecc: ecc read from HW ECC registers
 *
 * Compares the ecc read from nand spare area with ECC registers values
 * and if ECC's mismatched, it will call 'omap_compare_ecc' for error
 * detection and correction. If there are no errors, %0 is returned. If
 * there were errors and all of the errors were corrected, the number of
 * corrected errors is returned. If uncorrectable errors exist, %-1 is
 * returned.
 */
static int omap_correct_data(struct nand_chip *chip, u_char *dat,
			     u_char *read_ecc, u_char *calc_ecc)
{}

/**
 * omap_calculate_ecc - Generate non-inverted ECC bytes.
 * @chip: NAND chip object
 * @dat: The pointer to data on which ecc is computed
 * @ecc_code: The ecc_code buffer
 *
 * Using noninverted ECC can be considered ugly since writing a blank
 * page ie. padding will clear the ECC bytes. This is no problem as long
 * nobody is trying to write data on the seemingly unused page. Reading
 * an erased page will produce an ECC mismatch between generated and read
 * ECC bytes that has to be dealt with separately.
 */
static int omap_calculate_ecc(struct nand_chip *chip, const u_char *dat,
			      u_char *ecc_code)
{}

/**
 * omap_enable_hwecc - This function enables the hardware ecc functionality
 * @chip: NAND chip object
 * @mode: Read/Write mode
 */
static void omap_enable_hwecc(struct nand_chip *chip, int mode)
{}

/**
 * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
 * @chip: NAND chip object
 * @mode: Read/Write mode
 *
 * When using BCH with SW correction (i.e. no ELM), sector size is set
 * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode
 * for both reading and writing with:
 * eccsize0 = 0  (no additional protected byte in spare area)
 * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
 */
static void __maybe_unused omap_enable_hwecc_bch(struct nand_chip *chip,
						 int mode)
{}

static u8  bch4_polynomial[] =;
static u8  bch8_polynomial[] =;

/**
 * _omap_calculate_ecc_bch - Generate ECC bytes for one sector
 * @mtd:	MTD device structure
 * @dat:	The pointer to data on which ecc is computed
 * @ecc_calc:	The ecc_code buffer
 * @i:		The sector number (for a multi sector page)
 *
 * Support calculating of BCH4/8/16 ECC vectors for one sector
 * within a page. Sector number is in @i.
 */
static int _omap_calculate_ecc_bch(struct mtd_info *mtd,
				   const u_char *dat, u_char *ecc_calc, int i)
{}

/**
 * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction
 * @chip:	NAND chip object
 * @dat:	The pointer to data on which ecc is computed
 * @ecc_calc:	Buffer storing the calculated ECC bytes
 *
 * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used
 * when SW based correction is required as ECC is required for one sector
 * at a time.
 */
static int omap_calculate_ecc_bch_sw(struct nand_chip *chip,
				     const u_char *dat, u_char *ecc_calc)
{}

/**
 * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors
 * @mtd:	MTD device structure
 * @dat:	The pointer to data on which ecc is computed
 * @ecc_calc:	Buffer storing the calculated ECC bytes
 *
 * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go.
 */
static int omap_calculate_ecc_bch_multi(struct mtd_info *mtd,
					const u_char *dat, u_char *ecc_calc)
{}

/**
 * erased_sector_bitflips - count bit flips
 * @data:	data sector buffer
 * @oob:	oob buffer
 * @info:	omap_nand_info
 *
 * Check the bit flips in erased page falls below correctable level.
 * If falls below, report the page as erased with correctable bit
 * flip, else report as uncorrectable page.
 */
static int erased_sector_bitflips(u_char *data, u_char *oob,
		struct omap_nand_info *info)
{}

/**
 * omap_elm_correct_data - corrects page data area in case error reported
 * @chip:	NAND chip object
 * @data:	page data
 * @read_ecc:	ecc read from nand flash
 * @calc_ecc:	ecc read from HW ECC registers
 *
 * Calculated ecc vector reported as zero in case of non-error pages.
 * In case of non-zero ecc vector, first filter out erased-pages, and
 * then process data via ELM to detect bit-flips.
 */
static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
				 u_char *read_ecc, u_char *calc_ecc)
{}

/**
 * omap_write_page_bch - BCH ecc based write page function for entire page
 * @chip:		nand chip info structure
 * @buf:		data buffer
 * @oob_required:	must write chip->oob_poi to OOB
 * @page:		page
 *
 * Custom write page method evolved to support multi sector writing in one shot
 */
static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
			       int oob_required, int page)
{}

/**
 * omap_write_subpage_bch - BCH hardware ECC based subpage write
 * @chip:	nand chip info structure
 * @offset:	column address of subpage within the page
 * @data_len:	data length
 * @buf:	data buffer
 * @oob_required: must write chip->oob_poi to OOB
 * @page: page number to write
 *
 * OMAP optimized subpage write method.
 */
static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
				  u32 data_len, const u8 *buf,
				  int oob_required, int page)
{}

/**
 * omap_read_page_bch - BCH ecc based page read function for entire page
 * @chip:		nand chip info structure
 * @buf:		buffer to store read data
 * @oob_required:	caller requires OOB data read to chip->oob_poi
 * @page:		page number to read
 *
 * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module
 * used for error correction.
 * Custom method evolved to support ELM error correction & multi sector
 * reading. On reading page data area is read along with OOB data with
 * ecc engine enabled. ecc vector updated after read of OOB data.
 * For non error pages ecc vector reported as zero.
 */
static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
			      int oob_required, int page)
{}

/**
 * is_elm_present - checks for presence of ELM module by scanning DT nodes
 * @info: NAND device structure containing platform data
 * @elm_node: ELM's DT node
 */
static bool is_elm_present(struct omap_nand_info *info,
			   struct device_node *elm_node)
{}

static bool omap2_nand_ecc_check(struct omap_nand_info *info)
{}

static const char * const nand_xfer_types[] =;

static int omap_get_dt_info(struct device *dev, struct omap_nand_info *info)
{}

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

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

static const struct mtd_ooblayout_ops omap_ooblayout_ops =;

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

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

static const struct mtd_ooblayout_ops omap_sw_ooblayout_ops =;

static int omap_nand_attach_chip(struct nand_chip *chip)
{}

static void omap_nand_data_in(struct nand_chip *chip, void *buf,
			      unsigned int len, bool force_8bit)
{}

static void omap_nand_data_out(struct nand_chip *chip,
			       const void *buf, unsigned int len,
			       bool force_8bit)
{}

static int omap_nand_exec_instr(struct nand_chip *chip,
				const struct nand_op_instr *instr)
{}

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

static const struct nand_controller_ops omap_nand_controller_ops =;

/* Shared among all NAND instances to synchronize access to the ECC Engine */
static struct nand_controller omap_gpmc_controller;
static bool omap_gpmc_controller_initialized;

static int omap_nand_probe(struct platform_device *pdev)
{}

static void omap_nand_remove(struct platform_device *pdev)
{}

/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
MODULE_DEVICE_TABLE(of, omap_nand_ids);

static struct platform_driver omap_nand_driver =;

module_platform_driver();

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