162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com> 462306a36Sopenharmony_ci * Copyright © 2004 Micron Technology Inc. 562306a36Sopenharmony_ci * Copyright © 2004 David Brownell 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/platform_device.h> 962306a36Sopenharmony_ci#include <linux/dmaengine.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/jiffies.h> 1662306a36Sopenharmony_ci#include <linux/sched.h> 1762306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1862306a36Sopenharmony_ci#include <linux/mtd/nand-ecc-sw-bch.h> 1962306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 2062306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 2162306a36Sopenharmony_ci#include <linux/omap-dma.h> 2262306a36Sopenharmony_ci#include <linux/iopoll.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/of_platform.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/platform_data/elm.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/omap-gpmc.h> 3062306a36Sopenharmony_ci#include <linux/platform_data/mtd-nand-omap2.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DRIVER_NAME "omap2-nand" 3362306a36Sopenharmony_ci#define OMAP_NAND_TIMEOUT_MS 5000 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define NAND_Ecc_P1e (1 << 0) 3662306a36Sopenharmony_ci#define NAND_Ecc_P2e (1 << 1) 3762306a36Sopenharmony_ci#define NAND_Ecc_P4e (1 << 2) 3862306a36Sopenharmony_ci#define NAND_Ecc_P8e (1 << 3) 3962306a36Sopenharmony_ci#define NAND_Ecc_P16e (1 << 4) 4062306a36Sopenharmony_ci#define NAND_Ecc_P32e (1 << 5) 4162306a36Sopenharmony_ci#define NAND_Ecc_P64e (1 << 6) 4262306a36Sopenharmony_ci#define NAND_Ecc_P128e (1 << 7) 4362306a36Sopenharmony_ci#define NAND_Ecc_P256e (1 << 8) 4462306a36Sopenharmony_ci#define NAND_Ecc_P512e (1 << 9) 4562306a36Sopenharmony_ci#define NAND_Ecc_P1024e (1 << 10) 4662306a36Sopenharmony_ci#define NAND_Ecc_P2048e (1 << 11) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define NAND_Ecc_P1o (1 << 16) 4962306a36Sopenharmony_ci#define NAND_Ecc_P2o (1 << 17) 5062306a36Sopenharmony_ci#define NAND_Ecc_P4o (1 << 18) 5162306a36Sopenharmony_ci#define NAND_Ecc_P8o (1 << 19) 5262306a36Sopenharmony_ci#define NAND_Ecc_P16o (1 << 20) 5362306a36Sopenharmony_ci#define NAND_Ecc_P32o (1 << 21) 5462306a36Sopenharmony_ci#define NAND_Ecc_P64o (1 << 22) 5562306a36Sopenharmony_ci#define NAND_Ecc_P128o (1 << 23) 5662306a36Sopenharmony_ci#define NAND_Ecc_P256o (1 << 24) 5762306a36Sopenharmony_ci#define NAND_Ecc_P512o (1 << 25) 5862306a36Sopenharmony_ci#define NAND_Ecc_P1024o (1 << 26) 5962306a36Sopenharmony_ci#define NAND_Ecc_P2048o (1 << 27) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define TF(value) (value ? 1 : 0) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) 6462306a36Sopenharmony_ci#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) 6562306a36Sopenharmony_ci#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) 6662306a36Sopenharmony_ci#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) 6762306a36Sopenharmony_ci#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) 6862306a36Sopenharmony_ci#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) 6962306a36Sopenharmony_ci#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) 7062306a36Sopenharmony_ci#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) 7362306a36Sopenharmony_ci#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) 7462306a36Sopenharmony_ci#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) 7562306a36Sopenharmony_ci#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) 7662306a36Sopenharmony_ci#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) 7762306a36Sopenharmony_ci#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) 7862306a36Sopenharmony_ci#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) 7962306a36Sopenharmony_ci#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) 8262306a36Sopenharmony_ci#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) 8362306a36Sopenharmony_ci#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) 8462306a36Sopenharmony_ci#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) 8562306a36Sopenharmony_ci#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) 8662306a36Sopenharmony_ci#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) 8762306a36Sopenharmony_ci#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) 8862306a36Sopenharmony_ci#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) 9162306a36Sopenharmony_ci#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) 9262306a36Sopenharmony_ci#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) 9362306a36Sopenharmony_ci#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) 9462306a36Sopenharmony_ci#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) 9562306a36Sopenharmony_ci#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) 9662306a36Sopenharmony_ci#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) 9762306a36Sopenharmony_ci#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) 10062306a36Sopenharmony_ci#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define PREFETCH_CONFIG1_CS_SHIFT 24 10362306a36Sopenharmony_ci#define ECC_CONFIG_CS_SHIFT 1 10462306a36Sopenharmony_ci#define CS_MASK 0x7 10562306a36Sopenharmony_ci#define ENABLE_PREFETCH (0x1 << 7) 10662306a36Sopenharmony_ci#define DMA_MPU_MODE_SHIFT 2 10762306a36Sopenharmony_ci#define ECCSIZE0_SHIFT 12 10862306a36Sopenharmony_ci#define ECCSIZE1_SHIFT 22 10962306a36Sopenharmony_ci#define ECC1RESULTSIZE 0x1 11062306a36Sopenharmony_ci#define ECCCLEAR 0x100 11162306a36Sopenharmony_ci#define ECC1 0x1 11262306a36Sopenharmony_ci#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 11362306a36Sopenharmony_ci#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) 11462306a36Sopenharmony_ci#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) 11562306a36Sopenharmony_ci#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) 11662306a36Sopenharmony_ci#define STATUS_BUFF_EMPTY 0x00000001 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define SECTOR_BYTES 512 11962306a36Sopenharmony_ci/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ 12062306a36Sopenharmony_ci#define BCH4_BIT_PAD 4 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci/* GPMC ecc engine settings for read */ 12362306a36Sopenharmony_ci#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ 12462306a36Sopenharmony_ci#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ 12562306a36Sopenharmony_ci#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ 12662306a36Sopenharmony_ci#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ 12762306a36Sopenharmony_ci#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* GPMC ecc engine settings for write */ 13062306a36Sopenharmony_ci#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ 13162306a36Sopenharmony_ci#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ 13262306a36Sopenharmony_ci#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define BBM_LEN 2 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, 13762306a36Sopenharmony_ci 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, 13862306a36Sopenharmony_ci 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, 13962306a36Sopenharmony_ci 0x07, 0x0e}; 14062306a36Sopenharmony_cistatic u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, 14162306a36Sopenharmony_ci 0xac, 0x6b, 0xff, 0x99, 0x7b}; 14262306a36Sopenharmony_cistatic u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistruct omap_nand_info { 14562306a36Sopenharmony_ci struct nand_chip nand; 14662306a36Sopenharmony_ci struct platform_device *pdev; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci int gpmc_cs; 14962306a36Sopenharmony_ci bool dev_ready; 15062306a36Sopenharmony_ci enum nand_io xfer_type; 15162306a36Sopenharmony_ci enum omap_ecc ecc_opt; 15262306a36Sopenharmony_ci struct device_node *elm_of_node; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci unsigned long phys_base; 15562306a36Sopenharmony_ci struct completion comp; 15662306a36Sopenharmony_ci struct dma_chan *dma; 15762306a36Sopenharmony_ci int gpmc_irq_fifo; 15862306a36Sopenharmony_ci int gpmc_irq_count; 15962306a36Sopenharmony_ci enum { 16062306a36Sopenharmony_ci OMAP_NAND_IO_READ = 0, /* read */ 16162306a36Sopenharmony_ci OMAP_NAND_IO_WRITE, /* write */ 16262306a36Sopenharmony_ci } iomode; 16362306a36Sopenharmony_ci u_char *buf; 16462306a36Sopenharmony_ci int buf_len; 16562306a36Sopenharmony_ci /* Interface to GPMC */ 16662306a36Sopenharmony_ci void __iomem *fifo; 16762306a36Sopenharmony_ci struct gpmc_nand_regs reg; 16862306a36Sopenharmony_ci struct gpmc_nand_ops *ops; 16962306a36Sopenharmony_ci bool flash_bbt; 17062306a36Sopenharmony_ci /* fields specific for BCHx_HW ECC scheme */ 17162306a36Sopenharmony_ci struct device *elm_dev; 17262306a36Sopenharmony_ci /* NAND ready gpio */ 17362306a36Sopenharmony_ci struct gpio_desc *ready_gpiod; 17462306a36Sopenharmony_ci unsigned int neccpg; 17562306a36Sopenharmony_ci unsigned int nsteps_per_eccpg; 17662306a36Sopenharmony_ci unsigned int eccpg_size; 17762306a36Sopenharmony_ci unsigned int eccpg_bytes; 17862306a36Sopenharmony_ci void (*data_in)(struct nand_chip *chip, void *buf, 17962306a36Sopenharmony_ci unsigned int len, bool force_8bit); 18062306a36Sopenharmony_ci void (*data_out)(struct nand_chip *chip, 18162306a36Sopenharmony_ci const void *buf, unsigned int len, 18262306a36Sopenharmony_ci bool force_8bit); 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void omap_nand_data_in(struct nand_chip *chip, void *buf, 19162306a36Sopenharmony_ci unsigned int len, bool force_8bit); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic void omap_nand_data_out(struct nand_chip *chip, 19462306a36Sopenharmony_ci const void *buf, unsigned int len, 19562306a36Sopenharmony_ci bool force_8bit); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/** 19862306a36Sopenharmony_ci * omap_prefetch_enable - configures and starts prefetch transfer 19962306a36Sopenharmony_ci * @cs: cs (chip select) number 20062306a36Sopenharmony_ci * @fifo_th: fifo threshold to be used for read/ write 20162306a36Sopenharmony_ci * @dma_mode: dma mode enable (1) or disable (0) 20262306a36Sopenharmony_ci * @u32_count: number of bytes to be transferred 20362306a36Sopenharmony_ci * @is_write: prefetch read(0) or write post(1) mode 20462306a36Sopenharmony_ci * @info: NAND device structure containing platform data 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, 20762306a36Sopenharmony_ci unsigned int u32_count, int is_write, struct omap_nand_info *info) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci u32 val; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) 21262306a36Sopenharmony_ci return -1; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (readl(info->reg.gpmc_prefetch_control)) 21562306a36Sopenharmony_ci return -EBUSY; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Set the amount of bytes to be prefetched */ 21862306a36Sopenharmony_ci writel(u32_count, info->reg.gpmc_prefetch_config2); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* Set dma/mpu mode, the prefetch read / post write and 22162306a36Sopenharmony_ci * enable the engine. Set which cs is has requested for. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | 22462306a36Sopenharmony_ci PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | 22562306a36Sopenharmony_ci (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1)); 22662306a36Sopenharmony_ci writel(val, info->reg.gpmc_prefetch_config1); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Start the prefetch engine */ 22962306a36Sopenharmony_ci writel(0x1, info->reg.gpmc_prefetch_control); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci/* 23562306a36Sopenharmony_ci * omap_prefetch_reset - disables and stops the prefetch engine 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_cistatic int omap_prefetch_reset(int cs, struct omap_nand_info *info) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci u32 config1; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* check if the same module/cs is trying to reset */ 24262306a36Sopenharmony_ci config1 = readl(info->reg.gpmc_prefetch_config1); 24362306a36Sopenharmony_ci if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Stop the PFPW engine */ 24762306a36Sopenharmony_ci writel(0x0, info->reg.gpmc_prefetch_control); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Reset/disable the PFPW engine */ 25062306a36Sopenharmony_ci writel(0x0, info->reg.gpmc_prefetch_config1); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * omap_nand_data_in_pref - NAND data in using prefetch engine 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic void omap_nand_data_in_pref(struct nand_chip *chip, void *buf, 25962306a36Sopenharmony_ci unsigned int len, bool force_8bit) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 26262306a36Sopenharmony_ci uint32_t r_count = 0; 26362306a36Sopenharmony_ci int ret = 0; 26462306a36Sopenharmony_ci u32 *p = (u32 *)buf; 26562306a36Sopenharmony_ci unsigned int pref_len; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (force_8bit) { 26862306a36Sopenharmony_ci omap_nand_data_in(chip, buf, len, force_8bit); 26962306a36Sopenharmony_ci return; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* read 32-bit words using prefetch and remaining bytes normally */ 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* configure and start prefetch transfer */ 27562306a36Sopenharmony_ci pref_len = len - (len & 3); 27662306a36Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 27762306a36Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info); 27862306a36Sopenharmony_ci if (ret) { 27962306a36Sopenharmony_ci /* prefetch engine is busy, use CPU copy method */ 28062306a36Sopenharmony_ci omap_nand_data_in(chip, buf, len, false); 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci r_count = readl(info->reg.gpmc_prefetch_status); 28462306a36Sopenharmony_ci r_count = PREFETCH_STATUS_FIFO_CNT(r_count); 28562306a36Sopenharmony_ci r_count = r_count >> 2; 28662306a36Sopenharmony_ci ioread32_rep(info->fifo, p, r_count); 28762306a36Sopenharmony_ci p += r_count; 28862306a36Sopenharmony_ci pref_len -= r_count << 2; 28962306a36Sopenharmony_ci } while (pref_len); 29062306a36Sopenharmony_ci /* disable and stop the Prefetch engine */ 29162306a36Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 29262306a36Sopenharmony_ci /* fetch any remaining bytes */ 29362306a36Sopenharmony_ci if (len & 3) 29462306a36Sopenharmony_ci omap_nand_data_in(chip, p, len & 3, false); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/** 29962306a36Sopenharmony_ci * omap_nand_data_out_pref - NAND data out using Write Posting engine 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic void omap_nand_data_out_pref(struct nand_chip *chip, 30262306a36Sopenharmony_ci const void *buf, unsigned int len, 30362306a36Sopenharmony_ci bool force_8bit) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 30662306a36Sopenharmony_ci uint32_t w_count = 0; 30762306a36Sopenharmony_ci int i = 0, ret = 0; 30862306a36Sopenharmony_ci u16 *p = (u16 *)buf; 30962306a36Sopenharmony_ci unsigned long tim, limit; 31062306a36Sopenharmony_ci u32 val; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (force_8bit) { 31362306a36Sopenharmony_ci omap_nand_data_out(chip, buf, len, force_8bit); 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* take care of subpage writes */ 31862306a36Sopenharmony_ci if (len % 2 != 0) { 31962306a36Sopenharmony_ci writeb(*(u8 *)buf, info->fifo); 32062306a36Sopenharmony_ci p = (u16 *)(buf + 1); 32162306a36Sopenharmony_ci len--; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* configure and start prefetch transfer */ 32562306a36Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 32662306a36Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); 32762306a36Sopenharmony_ci if (ret) { 32862306a36Sopenharmony_ci /* write posting engine is busy, use CPU copy method */ 32962306a36Sopenharmony_ci omap_nand_data_out(chip, buf, len, false); 33062306a36Sopenharmony_ci } else { 33162306a36Sopenharmony_ci while (len) { 33262306a36Sopenharmony_ci w_count = readl(info->reg.gpmc_prefetch_status); 33362306a36Sopenharmony_ci w_count = PREFETCH_STATUS_FIFO_CNT(w_count); 33462306a36Sopenharmony_ci w_count = w_count >> 1; 33562306a36Sopenharmony_ci for (i = 0; (i < w_count) && len; i++, len -= 2) 33662306a36Sopenharmony_ci iowrite16(*p++, info->fifo); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci /* wait for data to flushed-out before reset the prefetch */ 33962306a36Sopenharmony_ci tim = 0; 34062306a36Sopenharmony_ci limit = (loops_per_jiffy * 34162306a36Sopenharmony_ci msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); 34262306a36Sopenharmony_ci do { 34362306a36Sopenharmony_ci cpu_relax(); 34462306a36Sopenharmony_ci val = readl(info->reg.gpmc_prefetch_status); 34562306a36Sopenharmony_ci val = PREFETCH_STATUS_COUNT(val); 34662306a36Sopenharmony_ci } while (val && (tim++ < limit)); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* disable and stop the PFPW engine */ 34962306a36Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/* 35462306a36Sopenharmony_ci * omap_nand_dma_callback: callback on the completion of dma transfer 35562306a36Sopenharmony_ci * @data: pointer to completion data structure 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic void omap_nand_dma_callback(void *data) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci complete((struct completion *) data); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * omap_nand_dma_transfer: configure and start dma transfer 36462306a36Sopenharmony_ci * @chip: nand chip structure 36562306a36Sopenharmony_ci * @addr: virtual address in RAM of source/destination 36662306a36Sopenharmony_ci * @len: number of data bytes to be transferred 36762306a36Sopenharmony_ci * @is_write: flag for read/write operation 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_cistatic inline int omap_nand_dma_transfer(struct nand_chip *chip, 37062306a36Sopenharmony_ci const void *addr, unsigned int len, 37162306a36Sopenharmony_ci int is_write) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 37462306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 37562306a36Sopenharmony_ci enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : 37662306a36Sopenharmony_ci DMA_FROM_DEVICE; 37762306a36Sopenharmony_ci struct scatterlist sg; 37862306a36Sopenharmony_ci unsigned long tim, limit; 37962306a36Sopenharmony_ci unsigned n; 38062306a36Sopenharmony_ci int ret; 38162306a36Sopenharmony_ci u32 val; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (!virt_addr_valid(addr)) 38462306a36Sopenharmony_ci goto out_copy; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci sg_init_one(&sg, addr, len); 38762306a36Sopenharmony_ci n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); 38862306a36Sopenharmony_ci if (n == 0) { 38962306a36Sopenharmony_ci dev_err(&info->pdev->dev, 39062306a36Sopenharmony_ci "Couldn't DMA map a %d byte buffer\n", len); 39162306a36Sopenharmony_ci goto out_copy; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci tx = dmaengine_prep_slave_sg(info->dma, &sg, n, 39562306a36Sopenharmony_ci is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, 39662306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 39762306a36Sopenharmony_ci if (!tx) 39862306a36Sopenharmony_ci goto out_copy_unmap; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci tx->callback = omap_nand_dma_callback; 40162306a36Sopenharmony_ci tx->callback_param = &info->comp; 40262306a36Sopenharmony_ci dmaengine_submit(tx); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci init_completion(&info->comp); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* setup and start DMA using dma_addr */ 40762306a36Sopenharmony_ci dma_async_issue_pending(info->dma); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* configure and start prefetch transfer */ 41062306a36Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 41162306a36Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); 41262306a36Sopenharmony_ci if (ret) 41362306a36Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 41462306a36Sopenharmony_ci goto out_copy_unmap; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci wait_for_completion(&info->comp); 41762306a36Sopenharmony_ci tim = 0; 41862306a36Sopenharmony_ci limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci do { 42162306a36Sopenharmony_ci cpu_relax(); 42262306a36Sopenharmony_ci val = readl(info->reg.gpmc_prefetch_status); 42362306a36Sopenharmony_ci val = PREFETCH_STATUS_COUNT(val); 42462306a36Sopenharmony_ci } while (val && (tim++ < limit)); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* disable and stop the PFPW engine */ 42762306a36Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ciout_copy_unmap: 43362306a36Sopenharmony_ci dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); 43462306a36Sopenharmony_ciout_copy: 43562306a36Sopenharmony_ci is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false) 43662306a36Sopenharmony_ci : omap_nand_data_out(chip, addr, len, false); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci/** 44262306a36Sopenharmony_ci * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_cistatic void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf, 44562306a36Sopenharmony_ci unsigned int len, bool force_8bit) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (force_8bit) { 45062306a36Sopenharmony_ci omap_nand_data_in(chip, buf, len, force_8bit); 45162306a36Sopenharmony_ci return; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (len <= mtd->oobsize) 45562306a36Sopenharmony_ci omap_nand_data_in_pref(chip, buf, len, false); 45662306a36Sopenharmony_ci else 45762306a36Sopenharmony_ci /* start transfer in DMA mode */ 45862306a36Sopenharmony_ci omap_nand_dma_transfer(chip, buf, len, 0x0); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/** 46262306a36Sopenharmony_ci * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_cistatic void omap_nand_data_out_dma_pref(struct nand_chip *chip, 46562306a36Sopenharmony_ci const void *buf, unsigned int len, 46662306a36Sopenharmony_ci bool force_8bit) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (force_8bit) { 47162306a36Sopenharmony_ci omap_nand_data_out(chip, buf, len, force_8bit); 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (len <= mtd->oobsize) 47662306a36Sopenharmony_ci omap_nand_data_out_pref(chip, buf, len, false); 47762306a36Sopenharmony_ci else 47862306a36Sopenharmony_ci /* start transfer in DMA mode */ 47962306a36Sopenharmony_ci omap_nand_dma_transfer(chip, buf, len, 0x1); 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* 48362306a36Sopenharmony_ci * omap_nand_irq - GPMC irq handler 48462306a36Sopenharmony_ci * @this_irq: gpmc irq number 48562306a36Sopenharmony_ci * @dev: omap_nand_info structure pointer is passed here 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_cistatic irqreturn_t omap_nand_irq(int this_irq, void *dev) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct omap_nand_info *info = (struct omap_nand_info *) dev; 49062306a36Sopenharmony_ci u32 bytes; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci bytes = readl(info->reg.gpmc_prefetch_status); 49362306a36Sopenharmony_ci bytes = PREFETCH_STATUS_FIFO_CNT(bytes); 49462306a36Sopenharmony_ci bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ 49562306a36Sopenharmony_ci if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ 49662306a36Sopenharmony_ci if (this_irq == info->gpmc_irq_count) 49762306a36Sopenharmony_ci goto done; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (info->buf_len && (info->buf_len < bytes)) 50062306a36Sopenharmony_ci bytes = info->buf_len; 50162306a36Sopenharmony_ci else if (!info->buf_len) 50262306a36Sopenharmony_ci bytes = 0; 50362306a36Sopenharmony_ci iowrite32_rep(info->fifo, (u32 *)info->buf, 50462306a36Sopenharmony_ci bytes >> 2); 50562306a36Sopenharmony_ci info->buf = info->buf + bytes; 50662306a36Sopenharmony_ci info->buf_len -= bytes; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci } else { 50962306a36Sopenharmony_ci ioread32_rep(info->fifo, (u32 *)info->buf, 51062306a36Sopenharmony_ci bytes >> 2); 51162306a36Sopenharmony_ci info->buf = info->buf + bytes; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (this_irq == info->gpmc_irq_count) 51462306a36Sopenharmony_ci goto done; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return IRQ_HANDLED; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cidone: 52062306a36Sopenharmony_ci complete(&info->comp); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci disable_irq_nosync(info->gpmc_irq_fifo); 52362306a36Sopenharmony_ci disable_irq_nosync(info->gpmc_irq_count); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return IRQ_HANDLED; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci/* 52962306a36Sopenharmony_ci * omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_cistatic void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf, 53262306a36Sopenharmony_ci unsigned int len, bool force_8bit) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 53562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&info->nand); 53662306a36Sopenharmony_ci int ret = 0; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (len <= mtd->oobsize || force_8bit) { 53962306a36Sopenharmony_ci omap_nand_data_in(chip, buf, len, force_8bit); 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci info->iomode = OMAP_NAND_IO_READ; 54462306a36Sopenharmony_ci info->buf = buf; 54562306a36Sopenharmony_ci init_completion(&info->comp); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* configure and start prefetch transfer */ 54862306a36Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 54962306a36Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); 55062306a36Sopenharmony_ci if (ret) { 55162306a36Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 55262306a36Sopenharmony_ci omap_nand_data_in(chip, buf, len, false); 55362306a36Sopenharmony_ci return; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci info->buf_len = len; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci enable_irq(info->gpmc_irq_count); 55962306a36Sopenharmony_ci enable_irq(info->gpmc_irq_fifo); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* waiting for read to complete */ 56262306a36Sopenharmony_ci wait_for_completion(&info->comp); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* disable and stop the PFPW engine */ 56562306a36Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 56662306a36Sopenharmony_ci return; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/* 57062306a36Sopenharmony_ci * omap_nand_data_out_irq_pref - NAND out using write posting and IRQ 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_cistatic void omap_nand_data_out_irq_pref(struct nand_chip *chip, 57362306a36Sopenharmony_ci const void *buf, unsigned int len, 57462306a36Sopenharmony_ci bool force_8bit) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 57762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&info->nand); 57862306a36Sopenharmony_ci int ret = 0; 57962306a36Sopenharmony_ci unsigned long tim, limit; 58062306a36Sopenharmony_ci u32 val; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (len <= mtd->oobsize || force_8bit) { 58362306a36Sopenharmony_ci omap_nand_data_out(chip, buf, len, force_8bit); 58462306a36Sopenharmony_ci return; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci info->iomode = OMAP_NAND_IO_WRITE; 58862306a36Sopenharmony_ci info->buf = (u_char *) buf; 58962306a36Sopenharmony_ci init_completion(&info->comp); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* configure and start prefetch transfer : size=24 */ 59262306a36Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 59362306a36Sopenharmony_ci (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); 59462306a36Sopenharmony_ci if (ret) { 59562306a36Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 59662306a36Sopenharmony_ci omap_nand_data_out(chip, buf, len, false); 59762306a36Sopenharmony_ci return; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci info->buf_len = len; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci enable_irq(info->gpmc_irq_count); 60362306a36Sopenharmony_ci enable_irq(info->gpmc_irq_fifo); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* waiting for write to complete */ 60662306a36Sopenharmony_ci wait_for_completion(&info->comp); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* wait for data to flushed-out before reset the prefetch */ 60962306a36Sopenharmony_ci tim = 0; 61062306a36Sopenharmony_ci limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); 61162306a36Sopenharmony_ci do { 61262306a36Sopenharmony_ci val = readl(info->reg.gpmc_prefetch_status); 61362306a36Sopenharmony_ci val = PREFETCH_STATUS_COUNT(val); 61462306a36Sopenharmony_ci cpu_relax(); 61562306a36Sopenharmony_ci } while (val && (tim++ < limit)); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* disable and stop the PFPW engine */ 61862306a36Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 61962306a36Sopenharmony_ci return; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci/** 62362306a36Sopenharmony_ci * gen_true_ecc - This function will generate true ECC value 62462306a36Sopenharmony_ci * @ecc_buf: buffer to store ecc code 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * This generated true ECC value can be used when correcting 62762306a36Sopenharmony_ci * data read from NAND flash memory core 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_cistatic void gen_true_ecc(u8 *ecc_buf) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | 63262306a36Sopenharmony_ci ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | 63562306a36Sopenharmony_ci P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); 63662306a36Sopenharmony_ci ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | 63762306a36Sopenharmony_ci P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); 63862306a36Sopenharmony_ci ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | 63962306a36Sopenharmony_ci P1e(tmp) | P2048o(tmp) | P2048e(tmp)); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data 64462306a36Sopenharmony_ci * @ecc_data1: ecc code from nand spare area 64562306a36Sopenharmony_ci * @ecc_data2: ecc code from hardware register obtained from hardware ecc 64662306a36Sopenharmony_ci * @page_data: page data 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * This function compares two ECC's and indicates if there is an error. 64962306a36Sopenharmony_ci * If the error can be corrected it will be corrected to the buffer. 65062306a36Sopenharmony_ci * If there is no error, %0 is returned. If there is an error but it 65162306a36Sopenharmony_ci * was corrected, %1 is returned. Otherwise, %-1 is returned. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_cistatic int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ 65462306a36Sopenharmony_ci u8 *ecc_data2, /* read from register */ 65562306a36Sopenharmony_ci u8 *page_data) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci uint i; 65862306a36Sopenharmony_ci u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; 65962306a36Sopenharmony_ci u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; 66062306a36Sopenharmony_ci u8 ecc_bit[24]; 66162306a36Sopenharmony_ci u8 ecc_sum = 0; 66262306a36Sopenharmony_ci u8 find_bit = 0; 66362306a36Sopenharmony_ci uint find_byte = 0; 66462306a36Sopenharmony_ci int isEccFF; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci gen_true_ecc(ecc_data1); 66962306a36Sopenharmony_ci gen_true_ecc(ecc_data2); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for (i = 0; i <= 2; i++) { 67262306a36Sopenharmony_ci *(ecc_data1 + i) = ~(*(ecc_data1 + i)); 67362306a36Sopenharmony_ci *(ecc_data2 + i) = ~(*(ecc_data2 + i)); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 67762306a36Sopenharmony_ci tmp0_bit[i] = *ecc_data1 % 2; 67862306a36Sopenharmony_ci *ecc_data1 = *ecc_data1 / 2; 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 68262306a36Sopenharmony_ci tmp1_bit[i] = *(ecc_data1 + 1) % 2; 68362306a36Sopenharmony_ci *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 68762306a36Sopenharmony_ci tmp2_bit[i] = *(ecc_data1 + 2) % 2; 68862306a36Sopenharmony_ci *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 69262306a36Sopenharmony_ci comp0_bit[i] = *ecc_data2 % 2; 69362306a36Sopenharmony_ci *ecc_data2 = *ecc_data2 / 2; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 69762306a36Sopenharmony_ci comp1_bit[i] = *(ecc_data2 + 1) % 2; 69862306a36Sopenharmony_ci *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 70262306a36Sopenharmony_ci comp2_bit[i] = *(ecc_data2 + 2) % 2; 70362306a36Sopenharmony_ci *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 70762306a36Sopenharmony_ci ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci for (i = 0; i < 8; i++) 71062306a36Sopenharmony_ci ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci for (i = 0; i < 8; i++) 71362306a36Sopenharmony_ci ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; 71662306a36Sopenharmony_ci ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci for (i = 0; i < 24; i++) 71962306a36Sopenharmony_ci ecc_sum += ecc_bit[i]; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci switch (ecc_sum) { 72262306a36Sopenharmony_ci case 0: 72362306a36Sopenharmony_ci /* Not reached because this function is not called if 72462306a36Sopenharmony_ci * ECC values are equal 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci case 1: 72962306a36Sopenharmony_ci /* Uncorrectable error */ 73062306a36Sopenharmony_ci pr_debug("ECC UNCORRECTED_ERROR 1\n"); 73162306a36Sopenharmony_ci return -EBADMSG; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci case 11: 73462306a36Sopenharmony_ci /* UN-Correctable error */ 73562306a36Sopenharmony_ci pr_debug("ECC UNCORRECTED_ERROR B\n"); 73662306a36Sopenharmony_ci return -EBADMSG; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci case 12: 73962306a36Sopenharmony_ci /* Correctable error */ 74062306a36Sopenharmony_ci find_byte = (ecc_bit[23] << 8) + 74162306a36Sopenharmony_ci (ecc_bit[21] << 7) + 74262306a36Sopenharmony_ci (ecc_bit[19] << 6) + 74362306a36Sopenharmony_ci (ecc_bit[17] << 5) + 74462306a36Sopenharmony_ci (ecc_bit[15] << 4) + 74562306a36Sopenharmony_ci (ecc_bit[13] << 3) + 74662306a36Sopenharmony_ci (ecc_bit[11] << 2) + 74762306a36Sopenharmony_ci (ecc_bit[9] << 1) + 74862306a36Sopenharmony_ci ecc_bit[7]; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci pr_debug("Correcting single bit ECC error at offset: " 75362306a36Sopenharmony_ci "%d, bit: %d\n", find_byte, find_bit); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci page_data[find_byte] ^= (1 << find_bit); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 1; 75862306a36Sopenharmony_ci default: 75962306a36Sopenharmony_ci if (isEccFF) { 76062306a36Sopenharmony_ci if (ecc_data2[0] == 0 && 76162306a36Sopenharmony_ci ecc_data2[1] == 0 && 76262306a36Sopenharmony_ci ecc_data2[2] == 0) 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci pr_debug("UNCORRECTED_ERROR default\n"); 76662306a36Sopenharmony_ci return -EBADMSG; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/** 77162306a36Sopenharmony_ci * omap_correct_data - Compares the ECC read with HW generated ECC 77262306a36Sopenharmony_ci * @chip: NAND chip object 77362306a36Sopenharmony_ci * @dat: page data 77462306a36Sopenharmony_ci * @read_ecc: ecc read from nand flash 77562306a36Sopenharmony_ci * @calc_ecc: ecc read from HW ECC registers 77662306a36Sopenharmony_ci * 77762306a36Sopenharmony_ci * Compares the ecc read from nand spare area with ECC registers values 77862306a36Sopenharmony_ci * and if ECC's mismatched, it will call 'omap_compare_ecc' for error 77962306a36Sopenharmony_ci * detection and correction. If there are no errors, %0 is returned. If 78062306a36Sopenharmony_ci * there were errors and all of the errors were corrected, the number of 78162306a36Sopenharmony_ci * corrected errors is returned. If uncorrectable errors exist, %-1 is 78262306a36Sopenharmony_ci * returned. 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_cistatic int omap_correct_data(struct nand_chip *chip, u_char *dat, 78562306a36Sopenharmony_ci u_char *read_ecc, u_char *calc_ecc) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 78862306a36Sopenharmony_ci int blockCnt = 0, i = 0, ret = 0; 78962306a36Sopenharmony_ci int stat = 0; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Ex NAND_ECC_HW12_2048 */ 79262306a36Sopenharmony_ci if (info->nand.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST && 79362306a36Sopenharmony_ci info->nand.ecc.size == 2048) 79462306a36Sopenharmony_ci blockCnt = 4; 79562306a36Sopenharmony_ci else 79662306a36Sopenharmony_ci blockCnt = 1; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci for (i = 0; i < blockCnt; i++) { 79962306a36Sopenharmony_ci if (memcmp(read_ecc, calc_ecc, 3) != 0) { 80062306a36Sopenharmony_ci ret = omap_compare_ecc(read_ecc, calc_ecc, dat); 80162306a36Sopenharmony_ci if (ret < 0) 80262306a36Sopenharmony_ci return ret; 80362306a36Sopenharmony_ci /* keep track of the number of corrected errors */ 80462306a36Sopenharmony_ci stat += ret; 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci read_ecc += 3; 80762306a36Sopenharmony_ci calc_ecc += 3; 80862306a36Sopenharmony_ci dat += 512; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci return stat; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/** 81462306a36Sopenharmony_ci * omap_calculate_ecc - Generate non-inverted ECC bytes. 81562306a36Sopenharmony_ci * @chip: NAND chip object 81662306a36Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 81762306a36Sopenharmony_ci * @ecc_code: The ecc_code buffer 81862306a36Sopenharmony_ci * 81962306a36Sopenharmony_ci * Using noninverted ECC can be considered ugly since writing a blank 82062306a36Sopenharmony_ci * page ie. padding will clear the ECC bytes. This is no problem as long 82162306a36Sopenharmony_ci * nobody is trying to write data on the seemingly unused page. Reading 82262306a36Sopenharmony_ci * an erased page will produce an ECC mismatch between generated and read 82362306a36Sopenharmony_ci * ECC bytes that has to be dealt with separately. 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_cistatic int omap_calculate_ecc(struct nand_chip *chip, const u_char *dat, 82662306a36Sopenharmony_ci u_char *ecc_code) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 82962306a36Sopenharmony_ci u32 val; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci val = readl(info->reg.gpmc_ecc_config); 83262306a36Sopenharmony_ci if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) 83362306a36Sopenharmony_ci return -EINVAL; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* read ecc result */ 83662306a36Sopenharmony_ci val = readl(info->reg.gpmc_ecc1_result); 83762306a36Sopenharmony_ci *ecc_code++ = val; /* P128e, ..., P1e */ 83862306a36Sopenharmony_ci *ecc_code++ = val >> 16; /* P128o, ..., P1o */ 83962306a36Sopenharmony_ci /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ 84062306a36Sopenharmony_ci *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci return 0; 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/** 84662306a36Sopenharmony_ci * omap_enable_hwecc - This function enables the hardware ecc functionality 84762306a36Sopenharmony_ci * @chip: NAND chip object 84862306a36Sopenharmony_ci * @mode: Read/Write mode 84962306a36Sopenharmony_ci */ 85062306a36Sopenharmony_cistatic void omap_enable_hwecc(struct nand_chip *chip, int mode) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 85362306a36Sopenharmony_ci unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; 85462306a36Sopenharmony_ci u32 val; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* clear ecc and enable bits */ 85762306a36Sopenharmony_ci val = ECCCLEAR | ECC1; 85862306a36Sopenharmony_ci writel(val, info->reg.gpmc_ecc_control); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* program ecc and result sizes */ 86162306a36Sopenharmony_ci val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) | 86262306a36Sopenharmony_ci ECC1RESULTSIZE); 86362306a36Sopenharmony_ci writel(val, info->reg.gpmc_ecc_size_config); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci switch (mode) { 86662306a36Sopenharmony_ci case NAND_ECC_READ: 86762306a36Sopenharmony_ci case NAND_ECC_WRITE: 86862306a36Sopenharmony_ci writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case NAND_ECC_READSYN: 87162306a36Sopenharmony_ci writel(ECCCLEAR, info->reg.gpmc_ecc_control); 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci default: 87462306a36Sopenharmony_ci dev_info(&info->pdev->dev, 87562306a36Sopenharmony_ci "error: unrecognized Mode[%d]!\n", mode); 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ 88062306a36Sopenharmony_ci val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); 88162306a36Sopenharmony_ci writel(val, info->reg.gpmc_ecc_config); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/** 88562306a36Sopenharmony_ci * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation 88662306a36Sopenharmony_ci * @chip: NAND chip object 88762306a36Sopenharmony_ci * @mode: Read/Write mode 88862306a36Sopenharmony_ci * 88962306a36Sopenharmony_ci * When using BCH with SW correction (i.e. no ELM), sector size is set 89062306a36Sopenharmony_ci * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode 89162306a36Sopenharmony_ci * for both reading and writing with: 89262306a36Sopenharmony_ci * eccsize0 = 0 (no additional protected byte in spare area) 89362306a36Sopenharmony_ci * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) 89462306a36Sopenharmony_ci */ 89562306a36Sopenharmony_cistatic void __maybe_unused omap_enable_hwecc_bch(struct nand_chip *chip, 89662306a36Sopenharmony_ci int mode) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci unsigned int bch_type; 89962306a36Sopenharmony_ci unsigned int dev_width, nsectors; 90062306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 90162306a36Sopenharmony_ci enum omap_ecc ecc_opt = info->ecc_opt; 90262306a36Sopenharmony_ci u32 val, wr_mode; 90362306a36Sopenharmony_ci unsigned int ecc_size1, ecc_size0; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci /* GPMC configurations for calculating ECC */ 90662306a36Sopenharmony_ci switch (ecc_opt) { 90762306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 90862306a36Sopenharmony_ci bch_type = 0; 90962306a36Sopenharmony_ci nsectors = 1; 91062306a36Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 91162306a36Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 91262306a36Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 91362306a36Sopenharmony_ci break; 91462306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 91562306a36Sopenharmony_ci bch_type = 0; 91662306a36Sopenharmony_ci nsectors = chip->ecc.steps; 91762306a36Sopenharmony_ci if (mode == NAND_ECC_READ) { 91862306a36Sopenharmony_ci wr_mode = BCH_WRAPMODE_1; 91962306a36Sopenharmony_ci ecc_size0 = BCH4R_ECC_SIZE0; 92062306a36Sopenharmony_ci ecc_size1 = BCH4R_ECC_SIZE1; 92162306a36Sopenharmony_ci } else { 92262306a36Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 92362306a36Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 92462306a36Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci break; 92762306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 92862306a36Sopenharmony_ci bch_type = 1; 92962306a36Sopenharmony_ci nsectors = 1; 93062306a36Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 93162306a36Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 93262306a36Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 93562306a36Sopenharmony_ci bch_type = 1; 93662306a36Sopenharmony_ci nsectors = chip->ecc.steps; 93762306a36Sopenharmony_ci if (mode == NAND_ECC_READ) { 93862306a36Sopenharmony_ci wr_mode = BCH_WRAPMODE_1; 93962306a36Sopenharmony_ci ecc_size0 = BCH8R_ECC_SIZE0; 94062306a36Sopenharmony_ci ecc_size1 = BCH8R_ECC_SIZE1; 94162306a36Sopenharmony_ci } else { 94262306a36Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 94362306a36Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 94462306a36Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 94862306a36Sopenharmony_ci bch_type = 0x2; 94962306a36Sopenharmony_ci nsectors = chip->ecc.steps; 95062306a36Sopenharmony_ci if (mode == NAND_ECC_READ) { 95162306a36Sopenharmony_ci wr_mode = 0x01; 95262306a36Sopenharmony_ci ecc_size0 = 52; /* ECC bits in nibbles per sector */ 95362306a36Sopenharmony_ci ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ 95462306a36Sopenharmony_ci } else { 95562306a36Sopenharmony_ci wr_mode = 0x01; 95662306a36Sopenharmony_ci ecc_size0 = 0; /* extra bits in nibbles per sector */ 95762306a36Sopenharmony_ci ecc_size1 = 52; /* OOB bits in nibbles per sector */ 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci default: 96162306a36Sopenharmony_ci return; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci writel(ECC1, info->reg.gpmc_ecc_control); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* Configure ecc size for BCH */ 96762306a36Sopenharmony_ci val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); 96862306a36Sopenharmony_ci writel(val, info->reg.gpmc_ecc_size_config); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* BCH configuration */ 97362306a36Sopenharmony_ci val = ((1 << 16) | /* enable BCH */ 97462306a36Sopenharmony_ci (bch_type << 12) | /* BCH4/BCH8/BCH16 */ 97562306a36Sopenharmony_ci (wr_mode << 8) | /* wrap mode */ 97662306a36Sopenharmony_ci (dev_width << 7) | /* bus width */ 97762306a36Sopenharmony_ci (((nsectors-1) & 0x7) << 4) | /* number of sectors */ 97862306a36Sopenharmony_ci (info->gpmc_cs << 1) | /* ECC CS */ 97962306a36Sopenharmony_ci (0x1)); /* enable ECC */ 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci writel(val, info->reg.gpmc_ecc_config); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Clear ecc and enable bits */ 98462306a36Sopenharmony_ci writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; 98862306a36Sopenharmony_cistatic u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, 98962306a36Sopenharmony_ci 0x97, 0x79, 0xe5, 0x24, 0xb5}; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/** 99262306a36Sopenharmony_ci * _omap_calculate_ecc_bch - Generate ECC bytes for one sector 99362306a36Sopenharmony_ci * @mtd: MTD device structure 99462306a36Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 99562306a36Sopenharmony_ci * @ecc_calc: The ecc_code buffer 99662306a36Sopenharmony_ci * @i: The sector number (for a multi sector page) 99762306a36Sopenharmony_ci * 99862306a36Sopenharmony_ci * Support calculating of BCH4/8/16 ECC vectors for one sector 99962306a36Sopenharmony_ci * within a page. Sector number is in @i. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_cistatic int _omap_calculate_ecc_bch(struct mtd_info *mtd, 100262306a36Sopenharmony_ci const u_char *dat, u_char *ecc_calc, int i) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 100562306a36Sopenharmony_ci int eccbytes = info->nand.ecc.bytes; 100662306a36Sopenharmony_ci struct gpmc_nand_regs *gpmc_regs = &info->reg; 100762306a36Sopenharmony_ci u8 *ecc_code; 100862306a36Sopenharmony_ci unsigned long bch_val1, bch_val2, bch_val3, bch_val4; 100962306a36Sopenharmony_ci u32 val; 101062306a36Sopenharmony_ci int j; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci ecc_code = ecc_calc; 101362306a36Sopenharmony_ci switch (info->ecc_opt) { 101462306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 101562306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 101662306a36Sopenharmony_ci bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); 101762306a36Sopenharmony_ci bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); 101862306a36Sopenharmony_ci bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); 101962306a36Sopenharmony_ci bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); 102062306a36Sopenharmony_ci *ecc_code++ = (bch_val4 & 0xFF); 102162306a36Sopenharmony_ci *ecc_code++ = ((bch_val3 >> 24) & 0xFF); 102262306a36Sopenharmony_ci *ecc_code++ = ((bch_val3 >> 16) & 0xFF); 102362306a36Sopenharmony_ci *ecc_code++ = ((bch_val3 >> 8) & 0xFF); 102462306a36Sopenharmony_ci *ecc_code++ = (bch_val3 & 0xFF); 102562306a36Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 24) & 0xFF); 102662306a36Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 16) & 0xFF); 102762306a36Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 8) & 0xFF); 102862306a36Sopenharmony_ci *ecc_code++ = (bch_val2 & 0xFF); 102962306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 24) & 0xFF); 103062306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 16) & 0xFF); 103162306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 8) & 0xFF); 103262306a36Sopenharmony_ci *ecc_code++ = (bch_val1 & 0xFF); 103362306a36Sopenharmony_ci break; 103462306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 103562306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 103662306a36Sopenharmony_ci bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); 103762306a36Sopenharmony_ci bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); 103862306a36Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 12) & 0xFF); 103962306a36Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 4) & 0xFF); 104062306a36Sopenharmony_ci *ecc_code++ = ((bch_val2 & 0xF) << 4) | 104162306a36Sopenharmony_ci ((bch_val1 >> 28) & 0xF); 104262306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 20) & 0xFF); 104362306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 12) & 0xFF); 104462306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 4) & 0xFF); 104562306a36Sopenharmony_ci *ecc_code++ = ((bch_val1 & 0xF) << 4); 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 104862306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result6[i]); 104962306a36Sopenharmony_ci ecc_code[0] = ((val >> 8) & 0xFF); 105062306a36Sopenharmony_ci ecc_code[1] = ((val >> 0) & 0xFF); 105162306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result5[i]); 105262306a36Sopenharmony_ci ecc_code[2] = ((val >> 24) & 0xFF); 105362306a36Sopenharmony_ci ecc_code[3] = ((val >> 16) & 0xFF); 105462306a36Sopenharmony_ci ecc_code[4] = ((val >> 8) & 0xFF); 105562306a36Sopenharmony_ci ecc_code[5] = ((val >> 0) & 0xFF); 105662306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result4[i]); 105762306a36Sopenharmony_ci ecc_code[6] = ((val >> 24) & 0xFF); 105862306a36Sopenharmony_ci ecc_code[7] = ((val >> 16) & 0xFF); 105962306a36Sopenharmony_ci ecc_code[8] = ((val >> 8) & 0xFF); 106062306a36Sopenharmony_ci ecc_code[9] = ((val >> 0) & 0xFF); 106162306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result3[i]); 106262306a36Sopenharmony_ci ecc_code[10] = ((val >> 24) & 0xFF); 106362306a36Sopenharmony_ci ecc_code[11] = ((val >> 16) & 0xFF); 106462306a36Sopenharmony_ci ecc_code[12] = ((val >> 8) & 0xFF); 106562306a36Sopenharmony_ci ecc_code[13] = ((val >> 0) & 0xFF); 106662306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result2[i]); 106762306a36Sopenharmony_ci ecc_code[14] = ((val >> 24) & 0xFF); 106862306a36Sopenharmony_ci ecc_code[15] = ((val >> 16) & 0xFF); 106962306a36Sopenharmony_ci ecc_code[16] = ((val >> 8) & 0xFF); 107062306a36Sopenharmony_ci ecc_code[17] = ((val >> 0) & 0xFF); 107162306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result1[i]); 107262306a36Sopenharmony_ci ecc_code[18] = ((val >> 24) & 0xFF); 107362306a36Sopenharmony_ci ecc_code[19] = ((val >> 16) & 0xFF); 107462306a36Sopenharmony_ci ecc_code[20] = ((val >> 8) & 0xFF); 107562306a36Sopenharmony_ci ecc_code[21] = ((val >> 0) & 0xFF); 107662306a36Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result0[i]); 107762306a36Sopenharmony_ci ecc_code[22] = ((val >> 24) & 0xFF); 107862306a36Sopenharmony_ci ecc_code[23] = ((val >> 16) & 0xFF); 107962306a36Sopenharmony_ci ecc_code[24] = ((val >> 8) & 0xFF); 108062306a36Sopenharmony_ci ecc_code[25] = ((val >> 0) & 0xFF); 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci default: 108362306a36Sopenharmony_ci return -EINVAL; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* ECC scheme specific syndrome customizations */ 108762306a36Sopenharmony_ci switch (info->ecc_opt) { 108862306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 108962306a36Sopenharmony_ci /* Add constant polynomial to remainder, so that 109062306a36Sopenharmony_ci * ECC of blank pages results in 0x0 on reading back 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci for (j = 0; j < eccbytes; j++) 109362306a36Sopenharmony_ci ecc_calc[j] ^= bch4_polynomial[j]; 109462306a36Sopenharmony_ci break; 109562306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 109662306a36Sopenharmony_ci /* Set 8th ECC byte as 0x0 for ROM compatibility */ 109762306a36Sopenharmony_ci ecc_calc[eccbytes - 1] = 0x0; 109862306a36Sopenharmony_ci break; 109962306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 110062306a36Sopenharmony_ci /* Add constant polynomial to remainder, so that 110162306a36Sopenharmony_ci * ECC of blank pages results in 0x0 on reading back 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci for (j = 0; j < eccbytes; j++) 110462306a36Sopenharmony_ci ecc_calc[j] ^= bch8_polynomial[j]; 110562306a36Sopenharmony_ci break; 110662306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 110762306a36Sopenharmony_ci /* Set 14th ECC byte as 0x0 for ROM compatibility */ 110862306a36Sopenharmony_ci ecc_calc[eccbytes - 1] = 0x0; 110962306a36Sopenharmony_ci break; 111062306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 111162306a36Sopenharmony_ci break; 111262306a36Sopenharmony_ci default: 111362306a36Sopenharmony_ci return -EINVAL; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci/** 112062306a36Sopenharmony_ci * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction 112162306a36Sopenharmony_ci * @chip: NAND chip object 112262306a36Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 112362306a36Sopenharmony_ci * @ecc_calc: Buffer storing the calculated ECC bytes 112462306a36Sopenharmony_ci * 112562306a36Sopenharmony_ci * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used 112662306a36Sopenharmony_ci * when SW based correction is required as ECC is required for one sector 112762306a36Sopenharmony_ci * at a time. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_cistatic int omap_calculate_ecc_bch_sw(struct nand_chip *chip, 113062306a36Sopenharmony_ci const u_char *dat, u_char *ecc_calc) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci return _omap_calculate_ecc_bch(nand_to_mtd(chip), dat, ecc_calc, 0); 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci/** 113662306a36Sopenharmony_ci * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors 113762306a36Sopenharmony_ci * @mtd: MTD device structure 113862306a36Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 113962306a36Sopenharmony_ci * @ecc_calc: Buffer storing the calculated ECC bytes 114062306a36Sopenharmony_ci * 114162306a36Sopenharmony_ci * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. 114262306a36Sopenharmony_ci */ 114362306a36Sopenharmony_cistatic int omap_calculate_ecc_bch_multi(struct mtd_info *mtd, 114462306a36Sopenharmony_ci const u_char *dat, u_char *ecc_calc) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 114762306a36Sopenharmony_ci int eccbytes = info->nand.ecc.bytes; 114862306a36Sopenharmony_ci unsigned long nsectors; 114962306a36Sopenharmony_ci int i, ret; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; 115262306a36Sopenharmony_ci for (i = 0; i < nsectors; i++) { 115362306a36Sopenharmony_ci ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); 115462306a36Sopenharmony_ci if (ret) 115562306a36Sopenharmony_ci return ret; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci ecc_calc += eccbytes; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci return 0; 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci/** 116462306a36Sopenharmony_ci * erased_sector_bitflips - count bit flips 116562306a36Sopenharmony_ci * @data: data sector buffer 116662306a36Sopenharmony_ci * @oob: oob buffer 116762306a36Sopenharmony_ci * @info: omap_nand_info 116862306a36Sopenharmony_ci * 116962306a36Sopenharmony_ci * Check the bit flips in erased page falls below correctable level. 117062306a36Sopenharmony_ci * If falls below, report the page as erased with correctable bit 117162306a36Sopenharmony_ci * flip, else report as uncorrectable page. 117262306a36Sopenharmony_ci */ 117362306a36Sopenharmony_cistatic int erased_sector_bitflips(u_char *data, u_char *oob, 117462306a36Sopenharmony_ci struct omap_nand_info *info) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci int flip_bits = 0, i; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci for (i = 0; i < info->nand.ecc.size; i++) { 117962306a36Sopenharmony_ci flip_bits += hweight8(~data[i]); 118062306a36Sopenharmony_ci if (flip_bits > info->nand.ecc.strength) 118162306a36Sopenharmony_ci return 0; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci for (i = 0; i < info->nand.ecc.bytes - 1; i++) { 118562306a36Sopenharmony_ci flip_bits += hweight8(~oob[i]); 118662306a36Sopenharmony_ci if (flip_bits > info->nand.ecc.strength) 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* 119162306a36Sopenharmony_ci * Bit flips falls in correctable level. 119262306a36Sopenharmony_ci * Fill data area with 0xFF 119362306a36Sopenharmony_ci */ 119462306a36Sopenharmony_ci if (flip_bits) { 119562306a36Sopenharmony_ci memset(data, 0xFF, info->nand.ecc.size); 119662306a36Sopenharmony_ci memset(oob, 0xFF, info->nand.ecc.bytes); 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return flip_bits; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci/** 120362306a36Sopenharmony_ci * omap_elm_correct_data - corrects page data area in case error reported 120462306a36Sopenharmony_ci * @chip: NAND chip object 120562306a36Sopenharmony_ci * @data: page data 120662306a36Sopenharmony_ci * @read_ecc: ecc read from nand flash 120762306a36Sopenharmony_ci * @calc_ecc: ecc read from HW ECC registers 120862306a36Sopenharmony_ci * 120962306a36Sopenharmony_ci * Calculated ecc vector reported as zero in case of non-error pages. 121062306a36Sopenharmony_ci * In case of non-zero ecc vector, first filter out erased-pages, and 121162306a36Sopenharmony_ci * then process data via ELM to detect bit-flips. 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_cistatic int omap_elm_correct_data(struct nand_chip *chip, u_char *data, 121462306a36Sopenharmony_ci u_char *read_ecc, u_char *calc_ecc) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 121762306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &info->nand.ecc; 121862306a36Sopenharmony_ci int eccsteps = info->nsteps_per_eccpg; 121962306a36Sopenharmony_ci int i , j, stat = 0; 122062306a36Sopenharmony_ci int eccflag, actual_eccbytes; 122162306a36Sopenharmony_ci struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; 122262306a36Sopenharmony_ci u_char *ecc_vec = calc_ecc; 122362306a36Sopenharmony_ci u_char *spare_ecc = read_ecc; 122462306a36Sopenharmony_ci u_char *erased_ecc_vec; 122562306a36Sopenharmony_ci u_char *buf; 122662306a36Sopenharmony_ci int bitflip_count; 122762306a36Sopenharmony_ci bool is_error_reported = false; 122862306a36Sopenharmony_ci u32 bit_pos, byte_pos, error_max, pos; 122962306a36Sopenharmony_ci int err; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci switch (info->ecc_opt) { 123262306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 123362306a36Sopenharmony_ci /* omit 7th ECC byte reserved for ROM code compatibility */ 123462306a36Sopenharmony_ci actual_eccbytes = ecc->bytes - 1; 123562306a36Sopenharmony_ci erased_ecc_vec = bch4_vector; 123662306a36Sopenharmony_ci break; 123762306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 123862306a36Sopenharmony_ci /* omit 14th ECC byte reserved for ROM code compatibility */ 123962306a36Sopenharmony_ci actual_eccbytes = ecc->bytes - 1; 124062306a36Sopenharmony_ci erased_ecc_vec = bch8_vector; 124162306a36Sopenharmony_ci break; 124262306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 124362306a36Sopenharmony_ci actual_eccbytes = ecc->bytes; 124462306a36Sopenharmony_ci erased_ecc_vec = bch16_vector; 124562306a36Sopenharmony_ci break; 124662306a36Sopenharmony_ci default: 124762306a36Sopenharmony_ci dev_err(&info->pdev->dev, "invalid driver configuration\n"); 124862306a36Sopenharmony_ci return -EINVAL; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* Initialize elm error vector to zero */ 125262306a36Sopenharmony_ci memset(err_vec, 0, sizeof(err_vec)); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci for (i = 0; i < eccsteps ; i++) { 125562306a36Sopenharmony_ci eccflag = 0; /* initialize eccflag */ 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* 125862306a36Sopenharmony_ci * Check any error reported, 125962306a36Sopenharmony_ci * In case of error, non zero ecc reported. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_ci for (j = 0; j < actual_eccbytes; j++) { 126262306a36Sopenharmony_ci if (calc_ecc[j] != 0) { 126362306a36Sopenharmony_ci eccflag = 1; /* non zero ecc, error present */ 126462306a36Sopenharmony_ci break; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (eccflag == 1) { 126962306a36Sopenharmony_ci if (memcmp(calc_ecc, erased_ecc_vec, 127062306a36Sopenharmony_ci actual_eccbytes) == 0) { 127162306a36Sopenharmony_ci /* 127262306a36Sopenharmony_ci * calc_ecc[] matches pattern for ECC(all 0xff) 127362306a36Sopenharmony_ci * so this is definitely an erased-page 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_ci } else { 127662306a36Sopenharmony_ci buf = &data[info->nand.ecc.size * i]; 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * count number of 0-bits in read_buf. 127962306a36Sopenharmony_ci * This check can be removed once a similar 128062306a36Sopenharmony_ci * check is introduced in generic NAND driver 128162306a36Sopenharmony_ci */ 128262306a36Sopenharmony_ci bitflip_count = erased_sector_bitflips( 128362306a36Sopenharmony_ci buf, read_ecc, info); 128462306a36Sopenharmony_ci if (bitflip_count) { 128562306a36Sopenharmony_ci /* 128662306a36Sopenharmony_ci * number of 0-bits within ECC limits 128762306a36Sopenharmony_ci * So this may be an erased-page 128862306a36Sopenharmony_ci */ 128962306a36Sopenharmony_ci stat += bitflip_count; 129062306a36Sopenharmony_ci } else { 129162306a36Sopenharmony_ci /* 129262306a36Sopenharmony_ci * Too many 0-bits. It may be a 129362306a36Sopenharmony_ci * - programmed-page, OR 129462306a36Sopenharmony_ci * - erased-page with many bit-flips 129562306a36Sopenharmony_ci * So this page requires check by ELM 129662306a36Sopenharmony_ci */ 129762306a36Sopenharmony_ci err_vec[i].error_reported = true; 129862306a36Sopenharmony_ci is_error_reported = true; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci } 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* Update the ecc vector */ 130462306a36Sopenharmony_ci calc_ecc += ecc->bytes; 130562306a36Sopenharmony_ci read_ecc += ecc->bytes; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci /* Check if any error reported */ 130962306a36Sopenharmony_ci if (!is_error_reported) 131062306a36Sopenharmony_ci return stat; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* Decode BCH error using ELM module */ 131362306a36Sopenharmony_ci elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci err = 0; 131662306a36Sopenharmony_ci for (i = 0; i < eccsteps; i++) { 131762306a36Sopenharmony_ci if (err_vec[i].error_uncorrectable) { 131862306a36Sopenharmony_ci dev_err(&info->pdev->dev, 131962306a36Sopenharmony_ci "uncorrectable bit-flips found\n"); 132062306a36Sopenharmony_ci err = -EBADMSG; 132162306a36Sopenharmony_ci } else if (err_vec[i].error_reported) { 132262306a36Sopenharmony_ci for (j = 0; j < err_vec[i].error_count; j++) { 132362306a36Sopenharmony_ci switch (info->ecc_opt) { 132462306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 132562306a36Sopenharmony_ci /* Add 4 bits to take care of padding */ 132662306a36Sopenharmony_ci pos = err_vec[i].error_loc[j] + 132762306a36Sopenharmony_ci BCH4_BIT_PAD; 132862306a36Sopenharmony_ci break; 132962306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 133062306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 133162306a36Sopenharmony_ci pos = err_vec[i].error_loc[j]; 133262306a36Sopenharmony_ci break; 133362306a36Sopenharmony_ci default: 133462306a36Sopenharmony_ci return -EINVAL; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci error_max = (ecc->size + actual_eccbytes) * 8; 133762306a36Sopenharmony_ci /* Calculate bit position of error */ 133862306a36Sopenharmony_ci bit_pos = pos % 8; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* Calculate byte position of error */ 134162306a36Sopenharmony_ci byte_pos = (error_max - pos - 1) / 8; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (pos < error_max) { 134462306a36Sopenharmony_ci if (byte_pos < 512) { 134562306a36Sopenharmony_ci pr_debug("bitflip@dat[%d]=%x\n", 134662306a36Sopenharmony_ci byte_pos, data[byte_pos]); 134762306a36Sopenharmony_ci data[byte_pos] ^= 1 << bit_pos; 134862306a36Sopenharmony_ci } else { 134962306a36Sopenharmony_ci pr_debug("bitflip@oob[%d]=%x\n", 135062306a36Sopenharmony_ci (byte_pos - 512), 135162306a36Sopenharmony_ci spare_ecc[byte_pos - 512]); 135262306a36Sopenharmony_ci spare_ecc[byte_pos - 512] ^= 135362306a36Sopenharmony_ci 1 << bit_pos; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci } else { 135662306a36Sopenharmony_ci dev_err(&info->pdev->dev, 135762306a36Sopenharmony_ci "invalid bit-flip @ %d:%d\n", 135862306a36Sopenharmony_ci byte_pos, bit_pos); 135962306a36Sopenharmony_ci err = -EBADMSG; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Update number of correctable errors */ 136562306a36Sopenharmony_ci stat = max_t(unsigned int, stat, err_vec[i].error_count); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* Update page data with sector size */ 136862306a36Sopenharmony_ci data += ecc->size; 136962306a36Sopenharmony_ci spare_ecc += ecc->bytes; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci return (err) ? err : stat; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci/** 137662306a36Sopenharmony_ci * omap_write_page_bch - BCH ecc based write page function for entire page 137762306a36Sopenharmony_ci * @chip: nand chip info structure 137862306a36Sopenharmony_ci * @buf: data buffer 137962306a36Sopenharmony_ci * @oob_required: must write chip->oob_poi to OOB 138062306a36Sopenharmony_ci * @page: page 138162306a36Sopenharmony_ci * 138262306a36Sopenharmony_ci * Custom write page method evolved to support multi sector writing in one shot 138362306a36Sopenharmony_ci */ 138462306a36Sopenharmony_cistatic int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf, 138562306a36Sopenharmony_ci int oob_required, int page) 138662306a36Sopenharmony_ci{ 138762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 138862306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 138962306a36Sopenharmony_ci uint8_t *ecc_calc = chip->ecc.calc_buf; 139062306a36Sopenharmony_ci unsigned int eccpg; 139162306a36Sopenharmony_ci int ret; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); 139462306a36Sopenharmony_ci if (ret) 139562306a36Sopenharmony_ci return ret; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci for (eccpg = 0; eccpg < info->neccpg; eccpg++) { 139862306a36Sopenharmony_ci /* Enable GPMC ecc engine */ 139962306a36Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_WRITE); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* Write data */ 140262306a36Sopenharmony_ci info->data_out(chip, buf + (eccpg * info->eccpg_size), 140362306a36Sopenharmony_ci info->eccpg_size, false); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* Update ecc vector from GPMC result registers */ 140662306a36Sopenharmony_ci ret = omap_calculate_ecc_bch_multi(mtd, 140762306a36Sopenharmony_ci buf + (eccpg * info->eccpg_size), 140862306a36Sopenharmony_ci ecc_calc); 140962306a36Sopenharmony_ci if (ret) 141062306a36Sopenharmony_ci return ret; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, 141362306a36Sopenharmony_ci chip->oob_poi, 141462306a36Sopenharmony_ci eccpg * info->eccpg_bytes, 141562306a36Sopenharmony_ci info->eccpg_bytes); 141662306a36Sopenharmony_ci if (ret) 141762306a36Sopenharmony_ci return ret; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* Write ecc vector to OOB area */ 142162306a36Sopenharmony_ci info->data_out(chip, chip->oob_poi, mtd->oobsize, false); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci/** 142762306a36Sopenharmony_ci * omap_write_subpage_bch - BCH hardware ECC based subpage write 142862306a36Sopenharmony_ci * @chip: nand chip info structure 142962306a36Sopenharmony_ci * @offset: column address of subpage within the page 143062306a36Sopenharmony_ci * @data_len: data length 143162306a36Sopenharmony_ci * @buf: data buffer 143262306a36Sopenharmony_ci * @oob_required: must write chip->oob_poi to OOB 143362306a36Sopenharmony_ci * @page: page number to write 143462306a36Sopenharmony_ci * 143562306a36Sopenharmony_ci * OMAP optimized subpage write method. 143662306a36Sopenharmony_ci */ 143762306a36Sopenharmony_cistatic int omap_write_subpage_bch(struct nand_chip *chip, u32 offset, 143862306a36Sopenharmony_ci u32 data_len, const u8 *buf, 143962306a36Sopenharmony_ci int oob_required, int page) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 144262306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 144362306a36Sopenharmony_ci u8 *ecc_calc = chip->ecc.calc_buf; 144462306a36Sopenharmony_ci int ecc_size = chip->ecc.size; 144562306a36Sopenharmony_ci int ecc_bytes = chip->ecc.bytes; 144662306a36Sopenharmony_ci u32 start_step = offset / ecc_size; 144762306a36Sopenharmony_ci u32 end_step = (offset + data_len - 1) / ecc_size; 144862306a36Sopenharmony_ci unsigned int eccpg; 144962306a36Sopenharmony_ci int step, ret = 0; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* 145262306a36Sopenharmony_ci * Write entire page at one go as it would be optimal 145362306a36Sopenharmony_ci * as ECC is calculated by hardware. 145462306a36Sopenharmony_ci * ECC is calculated for all subpages but we choose 145562306a36Sopenharmony_ci * only what we want. 145662306a36Sopenharmony_ci */ 145762306a36Sopenharmony_ci ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); 145862306a36Sopenharmony_ci if (ret) 145962306a36Sopenharmony_ci return ret; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci for (eccpg = 0; eccpg < info->neccpg; eccpg++) { 146262306a36Sopenharmony_ci /* Enable GPMC ECC engine */ 146362306a36Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_WRITE); 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* Write data */ 146662306a36Sopenharmony_ci info->data_out(chip, buf + (eccpg * info->eccpg_size), 146762306a36Sopenharmony_ci info->eccpg_size, false); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci for (step = 0; step < info->nsteps_per_eccpg; step++) { 147062306a36Sopenharmony_ci unsigned int base_step = eccpg * info->nsteps_per_eccpg; 147162306a36Sopenharmony_ci const u8 *bufoffs = buf + (eccpg * info->eccpg_size); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci /* Mask ECC of un-touched subpages with 0xFFs */ 147462306a36Sopenharmony_ci if ((step + base_step) < start_step || 147562306a36Sopenharmony_ci (step + base_step) > end_step) 147662306a36Sopenharmony_ci memset(ecc_calc + (step * ecc_bytes), 0xff, 147762306a36Sopenharmony_ci ecc_bytes); 147862306a36Sopenharmony_ci else 147962306a36Sopenharmony_ci ret = _omap_calculate_ecc_bch(mtd, 148062306a36Sopenharmony_ci bufoffs + (step * ecc_size), 148162306a36Sopenharmony_ci ecc_calc + (step * ecc_bytes), 148262306a36Sopenharmony_ci step); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (ret) 148562306a36Sopenharmony_ci return ret; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* 148962306a36Sopenharmony_ci * Copy the calculated ECC for the whole page including the 149062306a36Sopenharmony_ci * masked values (0xFF) corresponding to unwritten subpages. 149162306a36Sopenharmony_ci */ 149262306a36Sopenharmony_ci ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 149362306a36Sopenharmony_ci eccpg * info->eccpg_bytes, 149462306a36Sopenharmony_ci info->eccpg_bytes); 149562306a36Sopenharmony_ci if (ret) 149662306a36Sopenharmony_ci return ret; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci /* write OOB buffer to NAND device */ 150062306a36Sopenharmony_ci info->data_out(chip, chip->oob_poi, mtd->oobsize, false); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci/** 150662306a36Sopenharmony_ci * omap_read_page_bch - BCH ecc based page read function for entire page 150762306a36Sopenharmony_ci * @chip: nand chip info structure 150862306a36Sopenharmony_ci * @buf: buffer to store read data 150962306a36Sopenharmony_ci * @oob_required: caller requires OOB data read to chip->oob_poi 151062306a36Sopenharmony_ci * @page: page number to read 151162306a36Sopenharmony_ci * 151262306a36Sopenharmony_ci * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module 151362306a36Sopenharmony_ci * used for error correction. 151462306a36Sopenharmony_ci * Custom method evolved to support ELM error correction & multi sector 151562306a36Sopenharmony_ci * reading. On reading page data area is read along with OOB data with 151662306a36Sopenharmony_ci * ecc engine enabled. ecc vector updated after read of OOB data. 151762306a36Sopenharmony_ci * For non error pages ecc vector reported as zero. 151862306a36Sopenharmony_ci */ 151962306a36Sopenharmony_cistatic int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf, 152062306a36Sopenharmony_ci int oob_required, int page) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 152362306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 152462306a36Sopenharmony_ci uint8_t *ecc_calc = chip->ecc.calc_buf; 152562306a36Sopenharmony_ci uint8_t *ecc_code = chip->ecc.code_buf; 152662306a36Sopenharmony_ci unsigned int max_bitflips = 0, eccpg; 152762306a36Sopenharmony_ci int stat, ret; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, NULL, 0); 153062306a36Sopenharmony_ci if (ret) 153162306a36Sopenharmony_ci return ret; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci for (eccpg = 0; eccpg < info->neccpg; eccpg++) { 153462306a36Sopenharmony_ci /* Enable GPMC ecc engine */ 153562306a36Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_READ); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci /* Read data */ 153862306a36Sopenharmony_ci ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size, 153962306a36Sopenharmony_ci buf + (eccpg * info->eccpg_size), 154062306a36Sopenharmony_ci info->eccpg_size, false); 154162306a36Sopenharmony_ci if (ret) 154262306a36Sopenharmony_ci return ret; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* Read oob bytes */ 154562306a36Sopenharmony_ci ret = nand_change_read_column_op(chip, 154662306a36Sopenharmony_ci mtd->writesize + BBM_LEN + 154762306a36Sopenharmony_ci (eccpg * info->eccpg_bytes), 154862306a36Sopenharmony_ci chip->oob_poi + BBM_LEN + 154962306a36Sopenharmony_ci (eccpg * info->eccpg_bytes), 155062306a36Sopenharmony_ci info->eccpg_bytes, false); 155162306a36Sopenharmony_ci if (ret) 155262306a36Sopenharmony_ci return ret; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* Calculate ecc bytes */ 155562306a36Sopenharmony_ci ret = omap_calculate_ecc_bch_multi(mtd, 155662306a36Sopenharmony_ci buf + (eccpg * info->eccpg_size), 155762306a36Sopenharmony_ci ecc_calc); 155862306a36Sopenharmony_ci if (ret) 155962306a36Sopenharmony_ci return ret; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, 156262306a36Sopenharmony_ci chip->oob_poi, 156362306a36Sopenharmony_ci eccpg * info->eccpg_bytes, 156462306a36Sopenharmony_ci info->eccpg_bytes); 156562306a36Sopenharmony_ci if (ret) 156662306a36Sopenharmony_ci return ret; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci stat = chip->ecc.correct(chip, 156962306a36Sopenharmony_ci buf + (eccpg * info->eccpg_size), 157062306a36Sopenharmony_ci ecc_code, ecc_calc); 157162306a36Sopenharmony_ci if (stat < 0) { 157262306a36Sopenharmony_ci mtd->ecc_stats.failed++; 157362306a36Sopenharmony_ci } else { 157462306a36Sopenharmony_ci mtd->ecc_stats.corrected += stat; 157562306a36Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, stat); 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci return max_bitflips; 158062306a36Sopenharmony_ci} 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci/** 158362306a36Sopenharmony_ci * is_elm_present - checks for presence of ELM module by scanning DT nodes 158462306a36Sopenharmony_ci * @info: NAND device structure containing platform data 158562306a36Sopenharmony_ci * @elm_node: ELM's DT node 158662306a36Sopenharmony_ci */ 158762306a36Sopenharmony_cistatic bool is_elm_present(struct omap_nand_info *info, 158862306a36Sopenharmony_ci struct device_node *elm_node) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct platform_device *pdev; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci /* check whether elm-id is passed via DT */ 159362306a36Sopenharmony_ci if (!elm_node) { 159462306a36Sopenharmony_ci dev_err(&info->pdev->dev, "ELM devicetree node not found\n"); 159562306a36Sopenharmony_ci return false; 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci pdev = of_find_device_by_node(elm_node); 159862306a36Sopenharmony_ci /* check whether ELM device is registered */ 159962306a36Sopenharmony_ci if (!pdev) { 160062306a36Sopenharmony_ci dev_err(&info->pdev->dev, "ELM device not found\n"); 160162306a36Sopenharmony_ci return false; 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci /* ELM module available, now configure it */ 160462306a36Sopenharmony_ci info->elm_dev = &pdev->dev; 160562306a36Sopenharmony_ci return true; 160662306a36Sopenharmony_ci} 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic bool omap2_nand_ecc_check(struct omap_nand_info *info) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci switch (info->ecc_opt) { 161362306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 161462306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 161562306a36Sopenharmony_ci ecc_needs_omap_bch = false; 161662306a36Sopenharmony_ci ecc_needs_bch = true; 161762306a36Sopenharmony_ci ecc_needs_elm = false; 161862306a36Sopenharmony_ci break; 161962306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 162062306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 162162306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 162262306a36Sopenharmony_ci ecc_needs_omap_bch = true; 162362306a36Sopenharmony_ci ecc_needs_bch = false; 162462306a36Sopenharmony_ci ecc_needs_elm = true; 162562306a36Sopenharmony_ci break; 162662306a36Sopenharmony_ci default: 162762306a36Sopenharmony_ci ecc_needs_omap_bch = false; 162862306a36Sopenharmony_ci ecc_needs_bch = false; 162962306a36Sopenharmony_ci ecc_needs_elm = false; 163062306a36Sopenharmony_ci break; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { 163462306a36Sopenharmony_ci dev_err(&info->pdev->dev, 163562306a36Sopenharmony_ci "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); 163662306a36Sopenharmony_ci return false; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) { 163962306a36Sopenharmony_ci dev_err(&info->pdev->dev, 164062306a36Sopenharmony_ci "CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); 164162306a36Sopenharmony_ci return false; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) { 164462306a36Sopenharmony_ci dev_err(&info->pdev->dev, "ELM not available\n"); 164562306a36Sopenharmony_ci return false; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci return true; 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic const char * const nand_xfer_types[] = { 165262306a36Sopenharmony_ci [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", 165362306a36Sopenharmony_ci [NAND_OMAP_POLLED] = "polled", 165462306a36Sopenharmony_ci [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", 165562306a36Sopenharmony_ci [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", 165662306a36Sopenharmony_ci}; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) 165962306a36Sopenharmony_ci{ 166062306a36Sopenharmony_ci struct device_node *child = dev->of_node; 166162306a36Sopenharmony_ci int i; 166262306a36Sopenharmony_ci const char *s; 166362306a36Sopenharmony_ci u32 cs; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci if (of_property_read_u32(child, "reg", &cs) < 0) { 166662306a36Sopenharmony_ci dev_err(dev, "reg not found in DT\n"); 166762306a36Sopenharmony_ci return -EINVAL; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci info->gpmc_cs = cs; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci /* detect availability of ELM module. Won't be present pre-OMAP4 */ 167362306a36Sopenharmony_ci info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); 167462306a36Sopenharmony_ci if (!info->elm_of_node) { 167562306a36Sopenharmony_ci info->elm_of_node = of_parse_phandle(child, "elm_id", 0); 167662306a36Sopenharmony_ci if (!info->elm_of_node) 167762306a36Sopenharmony_ci dev_dbg(dev, "ti,elm-id not in DT\n"); 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci /* select ecc-scheme for NAND */ 168162306a36Sopenharmony_ci if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { 168262306a36Sopenharmony_ci dev_err(dev, "ti,nand-ecc-opt not found\n"); 168362306a36Sopenharmony_ci return -EINVAL; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (!strcmp(s, "sw")) { 168762306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; 168862306a36Sopenharmony_ci } else if (!strcmp(s, "ham1") || 168962306a36Sopenharmony_ci !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { 169062306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; 169162306a36Sopenharmony_ci } else if (!strcmp(s, "bch4")) { 169262306a36Sopenharmony_ci if (info->elm_of_node) 169362306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; 169462306a36Sopenharmony_ci else 169562306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; 169662306a36Sopenharmony_ci } else if (!strcmp(s, "bch8")) { 169762306a36Sopenharmony_ci if (info->elm_of_node) 169862306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; 169962306a36Sopenharmony_ci else 170062306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; 170162306a36Sopenharmony_ci } else if (!strcmp(s, "bch16")) { 170262306a36Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; 170362306a36Sopenharmony_ci } else { 170462306a36Sopenharmony_ci dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); 170562306a36Sopenharmony_ci return -EINVAL; 170662306a36Sopenharmony_ci } 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* select data transfer mode */ 170962306a36Sopenharmony_ci if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { 171062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { 171162306a36Sopenharmony_ci if (!strcasecmp(s, nand_xfer_types[i])) { 171262306a36Sopenharmony_ci info->xfer_type = i; 171362306a36Sopenharmony_ci return 0; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); 171862306a36Sopenharmony_ci return -EINVAL; 171962306a36Sopenharmony_ci } 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci return 0; 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic int omap_ooblayout_ecc(struct mtd_info *mtd, int section, 172562306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 172662306a36Sopenharmony_ci{ 172762306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 172862306a36Sopenharmony_ci struct nand_chip *chip = &info->nand; 172962306a36Sopenharmony_ci int off = BBM_LEN; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && 173262306a36Sopenharmony_ci !(chip->options & NAND_BUSWIDTH_16)) 173362306a36Sopenharmony_ci off = 1; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (section) 173662306a36Sopenharmony_ci return -ERANGE; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci oobregion->offset = off; 173962306a36Sopenharmony_ci oobregion->length = chip->ecc.total; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci return 0; 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cistatic int omap_ooblayout_free(struct mtd_info *mtd, int section, 174562306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 174862306a36Sopenharmony_ci struct nand_chip *chip = &info->nand; 174962306a36Sopenharmony_ci int off = BBM_LEN; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && 175262306a36Sopenharmony_ci !(chip->options & NAND_BUSWIDTH_16)) 175362306a36Sopenharmony_ci off = 1; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (section) 175662306a36Sopenharmony_ci return -ERANGE; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci off += chip->ecc.total; 175962306a36Sopenharmony_ci if (off >= mtd->oobsize) 176062306a36Sopenharmony_ci return -ERANGE; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci oobregion->offset = off; 176362306a36Sopenharmony_ci oobregion->length = mtd->oobsize - off; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci return 0; 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops omap_ooblayout_ops = { 176962306a36Sopenharmony_ci .ecc = omap_ooblayout_ecc, 177062306a36Sopenharmony_ci .free = omap_ooblayout_free, 177162306a36Sopenharmony_ci}; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_cistatic int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, 177462306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci struct nand_device *nand = mtd_to_nanddev(mtd); 177762306a36Sopenharmony_ci unsigned int nsteps = nanddev_get_ecc_nsteps(nand); 177862306a36Sopenharmony_ci unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); 177962306a36Sopenharmony_ci int off = BBM_LEN; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci if (section >= nsteps) 178262306a36Sopenharmony_ci return -ERANGE; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci /* 178562306a36Sopenharmony_ci * When SW correction is employed, one OMAP specific marker byte is 178662306a36Sopenharmony_ci * reserved after each ECC step. 178762306a36Sopenharmony_ci */ 178862306a36Sopenharmony_ci oobregion->offset = off + (section * (ecc_bytes + 1)); 178962306a36Sopenharmony_ci oobregion->length = ecc_bytes; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci return 0; 179262306a36Sopenharmony_ci} 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_cistatic int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, 179562306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci struct nand_device *nand = mtd_to_nanddev(mtd); 179862306a36Sopenharmony_ci unsigned int nsteps = nanddev_get_ecc_nsteps(nand); 179962306a36Sopenharmony_ci unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand); 180062306a36Sopenharmony_ci int off = BBM_LEN; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci if (section) 180362306a36Sopenharmony_ci return -ERANGE; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci /* 180662306a36Sopenharmony_ci * When SW correction is employed, one OMAP specific marker byte is 180762306a36Sopenharmony_ci * reserved after each ECC step. 180862306a36Sopenharmony_ci */ 180962306a36Sopenharmony_ci off += ((ecc_bytes + 1) * nsteps); 181062306a36Sopenharmony_ci if (off >= mtd->oobsize) 181162306a36Sopenharmony_ci return -ERANGE; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci oobregion->offset = off; 181462306a36Sopenharmony_ci oobregion->length = mtd->oobsize - off; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci return 0; 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { 182062306a36Sopenharmony_ci .ecc = omap_sw_ooblayout_ecc, 182162306a36Sopenharmony_ci .free = omap_sw_ooblayout_free, 182262306a36Sopenharmony_ci}; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cistatic int omap_nand_attach_chip(struct nand_chip *chip) 182562306a36Sopenharmony_ci{ 182662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 182762306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 182862306a36Sopenharmony_ci struct device *dev = &info->pdev->dev; 182962306a36Sopenharmony_ci int min_oobbytes = BBM_LEN; 183062306a36Sopenharmony_ci int elm_bch_strength = -1; 183162306a36Sopenharmony_ci int oobbytes_per_step; 183262306a36Sopenharmony_ci dma_cap_mask_t mask; 183362306a36Sopenharmony_ci int err; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci if (chip->bbt_options & NAND_BBT_USE_FLASH) 183662306a36Sopenharmony_ci chip->bbt_options |= NAND_BBT_NO_OOB; 183762306a36Sopenharmony_ci else 183862306a36Sopenharmony_ci chip->options |= NAND_SKIP_BBTSCAN; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci /* Re-populate low-level callbacks based on xfer modes */ 184162306a36Sopenharmony_ci switch (info->xfer_type) { 184262306a36Sopenharmony_ci case NAND_OMAP_PREFETCH_POLLED: 184362306a36Sopenharmony_ci info->data_in = omap_nand_data_in_pref; 184462306a36Sopenharmony_ci info->data_out = omap_nand_data_out_pref; 184562306a36Sopenharmony_ci break; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci case NAND_OMAP_POLLED: 184862306a36Sopenharmony_ci /* Use nand_base defaults for {read,write}_buf */ 184962306a36Sopenharmony_ci break; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci case NAND_OMAP_PREFETCH_DMA: 185262306a36Sopenharmony_ci dma_cap_zero(mask); 185362306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 185462306a36Sopenharmony_ci info->dma = dma_request_chan(dev->parent, "rxtx"); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci if (IS_ERR(info->dma)) { 185762306a36Sopenharmony_ci dev_err(dev, "DMA engine request failed\n"); 185862306a36Sopenharmony_ci return PTR_ERR(info->dma); 185962306a36Sopenharmony_ci } else { 186062306a36Sopenharmony_ci struct dma_slave_config cfg; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 186362306a36Sopenharmony_ci cfg.src_addr = info->phys_base; 186462306a36Sopenharmony_ci cfg.dst_addr = info->phys_base; 186562306a36Sopenharmony_ci cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 186662306a36Sopenharmony_ci cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 186762306a36Sopenharmony_ci cfg.src_maxburst = 16; 186862306a36Sopenharmony_ci cfg.dst_maxburst = 16; 186962306a36Sopenharmony_ci err = dmaengine_slave_config(info->dma, &cfg); 187062306a36Sopenharmony_ci if (err) { 187162306a36Sopenharmony_ci dev_err(dev, 187262306a36Sopenharmony_ci "DMA engine slave config failed: %d\n", 187362306a36Sopenharmony_ci err); 187462306a36Sopenharmony_ci return err; 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci info->data_in = omap_nand_data_in_dma_pref; 187862306a36Sopenharmony_ci info->data_out = omap_nand_data_out_dma_pref; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci break; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci case NAND_OMAP_PREFETCH_IRQ: 188362306a36Sopenharmony_ci info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); 188462306a36Sopenharmony_ci if (info->gpmc_irq_fifo <= 0) 188562306a36Sopenharmony_ci return -ENODEV; 188662306a36Sopenharmony_ci err = devm_request_irq(dev, info->gpmc_irq_fifo, 188762306a36Sopenharmony_ci omap_nand_irq, IRQF_SHARED, 188862306a36Sopenharmony_ci "gpmc-nand-fifo", info); 188962306a36Sopenharmony_ci if (err) { 189062306a36Sopenharmony_ci dev_err(dev, "Requesting IRQ %d, error %d\n", 189162306a36Sopenharmony_ci info->gpmc_irq_fifo, err); 189262306a36Sopenharmony_ci info->gpmc_irq_fifo = 0; 189362306a36Sopenharmony_ci return err; 189462306a36Sopenharmony_ci } 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci info->gpmc_irq_count = platform_get_irq(info->pdev, 1); 189762306a36Sopenharmony_ci if (info->gpmc_irq_count <= 0) 189862306a36Sopenharmony_ci return -ENODEV; 189962306a36Sopenharmony_ci err = devm_request_irq(dev, info->gpmc_irq_count, 190062306a36Sopenharmony_ci omap_nand_irq, IRQF_SHARED, 190162306a36Sopenharmony_ci "gpmc-nand-count", info); 190262306a36Sopenharmony_ci if (err) { 190362306a36Sopenharmony_ci dev_err(dev, "Requesting IRQ %d, error %d\n", 190462306a36Sopenharmony_ci info->gpmc_irq_count, err); 190562306a36Sopenharmony_ci info->gpmc_irq_count = 0; 190662306a36Sopenharmony_ci return err; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci info->data_in = omap_nand_data_in_irq_pref; 191062306a36Sopenharmony_ci info->data_out = omap_nand_data_out_irq_pref; 191162306a36Sopenharmony_ci break; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci default: 191462306a36Sopenharmony_ci dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type); 191562306a36Sopenharmony_ci return -EINVAL; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (!omap2_nand_ecc_check(info)) 191962306a36Sopenharmony_ci return -EINVAL; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* 192262306a36Sopenharmony_ci * Bail out earlier to let NAND_ECC_ENGINE_TYPE_SOFT code create its own 192362306a36Sopenharmony_ci * ooblayout instead of using ours. 192462306a36Sopenharmony_ci */ 192562306a36Sopenharmony_ci if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { 192662306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 192762306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 192862306a36Sopenharmony_ci return 0; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci /* Populate MTD interface based on ECC scheme */ 193262306a36Sopenharmony_ci switch (info->ecc_opt) { 193362306a36Sopenharmony_ci case OMAP_ECC_HAM1_CODE_HW: 193462306a36Sopenharmony_ci dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n"); 193562306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 193662306a36Sopenharmony_ci chip->ecc.bytes = 3; 193762306a36Sopenharmony_ci chip->ecc.size = 512; 193862306a36Sopenharmony_ci chip->ecc.strength = 1; 193962306a36Sopenharmony_ci chip->ecc.calculate = omap_calculate_ecc; 194062306a36Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc; 194162306a36Sopenharmony_ci chip->ecc.correct = omap_correct_data; 194262306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 194362306a36Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci if (!(chip->options & NAND_BUSWIDTH_16)) 194662306a36Sopenharmony_ci min_oobbytes = 1; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci break; 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 195162306a36Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); 195262306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 195362306a36Sopenharmony_ci chip->ecc.size = 512; 195462306a36Sopenharmony_ci chip->ecc.bytes = 7; 195562306a36Sopenharmony_ci chip->ecc.strength = 4; 195662306a36Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 195762306a36Sopenharmony_ci chip->ecc.correct = rawnand_sw_bch_correct; 195862306a36Sopenharmony_ci chip->ecc.calculate = omap_calculate_ecc_bch_sw; 195962306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); 196062306a36Sopenharmony_ci /* Reserve one byte for the OMAP marker */ 196162306a36Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes + 1; 196262306a36Sopenharmony_ci /* Software BCH library is used for locating errors */ 196362306a36Sopenharmony_ci err = rawnand_sw_bch_init(chip); 196462306a36Sopenharmony_ci if (err) { 196562306a36Sopenharmony_ci dev_err(dev, "Unable to use BCH library\n"); 196662306a36Sopenharmony_ci return err; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci break; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 197162306a36Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); 197262306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 197362306a36Sopenharmony_ci chip->ecc.size = 512; 197462306a36Sopenharmony_ci /* 14th bit is kept reserved for ROM-code compatibility */ 197562306a36Sopenharmony_ci chip->ecc.bytes = 7 + 1; 197662306a36Sopenharmony_ci chip->ecc.strength = 4; 197762306a36Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 197862306a36Sopenharmony_ci chip->ecc.correct = omap_elm_correct_data; 197962306a36Sopenharmony_ci chip->ecc.read_page = omap_read_page_bch; 198062306a36Sopenharmony_ci chip->ecc.write_page = omap_write_page_bch; 198162306a36Sopenharmony_ci chip->ecc.write_subpage = omap_write_subpage_bch; 198262306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 198362306a36Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 198462306a36Sopenharmony_ci elm_bch_strength = BCH4_ECC; 198562306a36Sopenharmony_ci break; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 198862306a36Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); 198962306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 199062306a36Sopenharmony_ci chip->ecc.size = 512; 199162306a36Sopenharmony_ci chip->ecc.bytes = 13; 199262306a36Sopenharmony_ci chip->ecc.strength = 8; 199362306a36Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 199462306a36Sopenharmony_ci chip->ecc.correct = rawnand_sw_bch_correct; 199562306a36Sopenharmony_ci chip->ecc.calculate = omap_calculate_ecc_bch_sw; 199662306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); 199762306a36Sopenharmony_ci /* Reserve one byte for the OMAP marker */ 199862306a36Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes + 1; 199962306a36Sopenharmony_ci /* Software BCH library is used for locating errors */ 200062306a36Sopenharmony_ci err = rawnand_sw_bch_init(chip); 200162306a36Sopenharmony_ci if (err) { 200262306a36Sopenharmony_ci dev_err(dev, "unable to use BCH library\n"); 200362306a36Sopenharmony_ci return err; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci break; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 200862306a36Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); 200962306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 201062306a36Sopenharmony_ci chip->ecc.size = 512; 201162306a36Sopenharmony_ci /* 14th bit is kept reserved for ROM-code compatibility */ 201262306a36Sopenharmony_ci chip->ecc.bytes = 13 + 1; 201362306a36Sopenharmony_ci chip->ecc.strength = 8; 201462306a36Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 201562306a36Sopenharmony_ci chip->ecc.correct = omap_elm_correct_data; 201662306a36Sopenharmony_ci chip->ecc.read_page = omap_read_page_bch; 201762306a36Sopenharmony_ci chip->ecc.write_page = omap_write_page_bch; 201862306a36Sopenharmony_ci chip->ecc.write_subpage = omap_write_subpage_bch; 201962306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 202062306a36Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 202162306a36Sopenharmony_ci elm_bch_strength = BCH8_ECC; 202262306a36Sopenharmony_ci break; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 202562306a36Sopenharmony_ci pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); 202662306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 202762306a36Sopenharmony_ci chip->ecc.size = 512; 202862306a36Sopenharmony_ci chip->ecc.bytes = 26; 202962306a36Sopenharmony_ci chip->ecc.strength = 16; 203062306a36Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 203162306a36Sopenharmony_ci chip->ecc.correct = omap_elm_correct_data; 203262306a36Sopenharmony_ci chip->ecc.read_page = omap_read_page_bch; 203362306a36Sopenharmony_ci chip->ecc.write_page = omap_write_page_bch; 203462306a36Sopenharmony_ci chip->ecc.write_subpage = omap_write_subpage_bch; 203562306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 203662306a36Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 203762306a36Sopenharmony_ci elm_bch_strength = BCH16_ECC; 203862306a36Sopenharmony_ci break; 203962306a36Sopenharmony_ci default: 204062306a36Sopenharmony_ci dev_err(dev, "Invalid or unsupported ECC scheme\n"); 204162306a36Sopenharmony_ci return -EINVAL; 204262306a36Sopenharmony_ci } 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci if (elm_bch_strength >= 0) { 204562306a36Sopenharmony_ci chip->ecc.steps = mtd->writesize / chip->ecc.size; 204662306a36Sopenharmony_ci info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX; 204762306a36Sopenharmony_ci if (info->neccpg) { 204862306a36Sopenharmony_ci info->nsteps_per_eccpg = ERROR_VECTOR_MAX; 204962306a36Sopenharmony_ci } else { 205062306a36Sopenharmony_ci info->neccpg = 1; 205162306a36Sopenharmony_ci info->nsteps_per_eccpg = chip->ecc.steps; 205262306a36Sopenharmony_ci } 205362306a36Sopenharmony_ci info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size; 205462306a36Sopenharmony_ci info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci err = elm_config(info->elm_dev, elm_bch_strength, 205762306a36Sopenharmony_ci info->nsteps_per_eccpg, chip->ecc.size, 205862306a36Sopenharmony_ci chip->ecc.bytes); 205962306a36Sopenharmony_ci if (err < 0) 206062306a36Sopenharmony_ci return err; 206162306a36Sopenharmony_ci } 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci /* Check if NAND device's OOB is enough to store ECC signatures */ 206462306a36Sopenharmony_ci min_oobbytes += (oobbytes_per_step * 206562306a36Sopenharmony_ci (mtd->writesize / chip->ecc.size)); 206662306a36Sopenharmony_ci if (mtd->oobsize < min_oobbytes) { 206762306a36Sopenharmony_ci dev_err(dev, 206862306a36Sopenharmony_ci "Not enough OOB bytes: required = %d, available=%d\n", 206962306a36Sopenharmony_ci min_oobbytes, mtd->oobsize); 207062306a36Sopenharmony_ci return -EINVAL; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci return 0; 207462306a36Sopenharmony_ci} 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_cistatic void omap_nand_data_in(struct nand_chip *chip, void *buf, 207762306a36Sopenharmony_ci unsigned int len, bool force_8bit) 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 208062306a36Sopenharmony_ci u32 alignment = ((uintptr_t)buf | len) & 3; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci if (force_8bit || (alignment & 1)) 208362306a36Sopenharmony_ci ioread8_rep(info->fifo, buf, len); 208462306a36Sopenharmony_ci else if (alignment & 3) 208562306a36Sopenharmony_ci ioread16_rep(info->fifo, buf, len >> 1); 208662306a36Sopenharmony_ci else 208762306a36Sopenharmony_ci ioread32_rep(info->fifo, buf, len >> 2); 208862306a36Sopenharmony_ci} 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_cistatic void omap_nand_data_out(struct nand_chip *chip, 209162306a36Sopenharmony_ci const void *buf, unsigned int len, 209262306a36Sopenharmony_ci bool force_8bit) 209362306a36Sopenharmony_ci{ 209462306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 209562306a36Sopenharmony_ci u32 alignment = ((uintptr_t)buf | len) & 3; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci if (force_8bit || (alignment & 1)) 209862306a36Sopenharmony_ci iowrite8_rep(info->fifo, buf, len); 209962306a36Sopenharmony_ci else if (alignment & 3) 210062306a36Sopenharmony_ci iowrite16_rep(info->fifo, buf, len >> 1); 210162306a36Sopenharmony_ci else 210262306a36Sopenharmony_ci iowrite32_rep(info->fifo, buf, len >> 2); 210362306a36Sopenharmony_ci} 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_cistatic int omap_nand_exec_instr(struct nand_chip *chip, 210662306a36Sopenharmony_ci const struct nand_op_instr *instr) 210762306a36Sopenharmony_ci{ 210862306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 210962306a36Sopenharmony_ci unsigned int i; 211062306a36Sopenharmony_ci int ret; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci switch (instr->type) { 211362306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 211462306a36Sopenharmony_ci iowrite8(instr->ctx.cmd.opcode, 211562306a36Sopenharmony_ci info->reg.gpmc_nand_command); 211662306a36Sopenharmony_ci break; 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 211962306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) { 212062306a36Sopenharmony_ci iowrite8(instr->ctx.addr.addrs[i], 212162306a36Sopenharmony_ci info->reg.gpmc_nand_address); 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci break; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 212662306a36Sopenharmony_ci info->data_in(chip, instr->ctx.data.buf.in, 212762306a36Sopenharmony_ci instr->ctx.data.len, 212862306a36Sopenharmony_ci instr->ctx.data.force_8bit); 212962306a36Sopenharmony_ci break; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 213262306a36Sopenharmony_ci info->data_out(chip, instr->ctx.data.buf.out, 213362306a36Sopenharmony_ci instr->ctx.data.len, 213462306a36Sopenharmony_ci instr->ctx.data.force_8bit); 213562306a36Sopenharmony_ci break; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 213862306a36Sopenharmony_ci ret = info->ready_gpiod ? 213962306a36Sopenharmony_ci nand_gpio_waitrdy(chip, info->ready_gpiod, instr->ctx.waitrdy.timeout_ms) : 214062306a36Sopenharmony_ci nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms); 214162306a36Sopenharmony_ci if (ret) 214262306a36Sopenharmony_ci return ret; 214362306a36Sopenharmony_ci break; 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (instr->delay_ns) 214762306a36Sopenharmony_ci ndelay(instr->delay_ns); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci return 0; 215062306a36Sopenharmony_ci} 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_cistatic int omap_nand_exec_op(struct nand_chip *chip, 215362306a36Sopenharmony_ci const struct nand_operation *op, 215462306a36Sopenharmony_ci bool check_only) 215562306a36Sopenharmony_ci{ 215662306a36Sopenharmony_ci unsigned int i; 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci if (check_only) 215962306a36Sopenharmony_ci return 0; 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 216262306a36Sopenharmony_ci int ret; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci ret = omap_nand_exec_instr(chip, &op->instrs[i]); 216562306a36Sopenharmony_ci if (ret) 216662306a36Sopenharmony_ci return ret; 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci return 0; 217062306a36Sopenharmony_ci} 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_cistatic const struct nand_controller_ops omap_nand_controller_ops = { 217362306a36Sopenharmony_ci .attach_chip = omap_nand_attach_chip, 217462306a36Sopenharmony_ci .exec_op = omap_nand_exec_op, 217562306a36Sopenharmony_ci}; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci/* Shared among all NAND instances to synchronize access to the ECC Engine */ 217862306a36Sopenharmony_cistatic struct nand_controller omap_gpmc_controller; 217962306a36Sopenharmony_cistatic bool omap_gpmc_controller_initialized; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_cistatic int omap_nand_probe(struct platform_device *pdev) 218262306a36Sopenharmony_ci{ 218362306a36Sopenharmony_ci struct omap_nand_info *info; 218462306a36Sopenharmony_ci struct mtd_info *mtd; 218562306a36Sopenharmony_ci struct nand_chip *nand_chip; 218662306a36Sopenharmony_ci int err; 218762306a36Sopenharmony_ci struct resource *res; 218862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 218962306a36Sopenharmony_ci void __iomem *vaddr; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), 219262306a36Sopenharmony_ci GFP_KERNEL); 219362306a36Sopenharmony_ci if (!info) 219462306a36Sopenharmony_ci return -ENOMEM; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci info->pdev = pdev; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci err = omap_get_dt_info(dev, info); 219962306a36Sopenharmony_ci if (err) 220062306a36Sopenharmony_ci return err; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); 220362306a36Sopenharmony_ci if (!info->ops) { 220462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); 220562306a36Sopenharmony_ci return -ENODEV; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci nand_chip = &info->nand; 220962306a36Sopenharmony_ci mtd = nand_to_mtd(nand_chip); 221062306a36Sopenharmony_ci mtd->dev.parent = &pdev->dev; 221162306a36Sopenharmony_ci nand_set_flash_node(nand_chip, dev->of_node); 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci if (!mtd->name) { 221462306a36Sopenharmony_ci mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 221562306a36Sopenharmony_ci "omap2-nand.%d", info->gpmc_cs); 221662306a36Sopenharmony_ci if (!mtd->name) { 221762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set MTD name\n"); 221862306a36Sopenharmony_ci return -ENOMEM; 221962306a36Sopenharmony_ci } 222062306a36Sopenharmony_ci } 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci vaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 222362306a36Sopenharmony_ci if (IS_ERR(vaddr)) 222462306a36Sopenharmony_ci return PTR_ERR(vaddr); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci info->fifo = vaddr; 222762306a36Sopenharmony_ci info->phys_base = res->start; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci if (!omap_gpmc_controller_initialized) { 223062306a36Sopenharmony_ci omap_gpmc_controller.ops = &omap_nand_controller_ops; 223162306a36Sopenharmony_ci nand_controller_init(&omap_gpmc_controller); 223262306a36Sopenharmony_ci omap_gpmc_controller_initialized = true; 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci nand_chip->controller = &omap_gpmc_controller; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", 223862306a36Sopenharmony_ci GPIOD_IN); 223962306a36Sopenharmony_ci if (IS_ERR(info->ready_gpiod)) { 224062306a36Sopenharmony_ci dev_err(dev, "failed to get ready gpio\n"); 224162306a36Sopenharmony_ci return PTR_ERR(info->ready_gpiod); 224262306a36Sopenharmony_ci } 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci if (info->flash_bbt) 224562306a36Sopenharmony_ci nand_chip->bbt_options |= NAND_BBT_USE_FLASH; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci /* default operations */ 224862306a36Sopenharmony_ci info->data_in = omap_nand_data_in; 224962306a36Sopenharmony_ci info->data_out = omap_nand_data_out; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci err = nand_scan(nand_chip, 1); 225262306a36Sopenharmony_ci if (err) 225362306a36Sopenharmony_ci goto return_error; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci err = mtd_device_register(mtd, NULL, 0); 225662306a36Sopenharmony_ci if (err) 225762306a36Sopenharmony_ci goto cleanup_nand; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci platform_set_drvdata(pdev, mtd); 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci return 0; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_cicleanup_nand: 226462306a36Sopenharmony_ci nand_cleanup(nand_chip); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_cireturn_error: 226762306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(info->dma)) 226862306a36Sopenharmony_ci dma_release_channel(info->dma); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci rawnand_sw_bch_cleanup(nand_chip); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci return err; 227362306a36Sopenharmony_ci} 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_cistatic void omap_nand_remove(struct platform_device *pdev) 227662306a36Sopenharmony_ci{ 227762306a36Sopenharmony_ci struct mtd_info *mtd = platform_get_drvdata(pdev); 227862306a36Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 227962306a36Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci rawnand_sw_bch_cleanup(nand_chip); 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ci if (info->dma) 228462306a36Sopenharmony_ci dma_release_channel(info->dma); 228562306a36Sopenharmony_ci WARN_ON(mtd_device_unregister(mtd)); 228662306a36Sopenharmony_ci nand_cleanup(nand_chip); 228762306a36Sopenharmony_ci} 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */ 229062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_nand_ids); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_cistatic struct platform_driver omap_nand_driver = { 229362306a36Sopenharmony_ci .probe = omap_nand_probe, 229462306a36Sopenharmony_ci .remove_new = omap_nand_remove, 229562306a36Sopenharmony_ci .driver = { 229662306a36Sopenharmony_ci .name = DRIVER_NAME, 229762306a36Sopenharmony_ci .of_match_table = omap_nand_ids, 229862306a36Sopenharmony_ci }, 229962306a36Sopenharmony_ci}; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cimodule_platform_driver(omap_nand_driver); 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 230462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 230562306a36Sopenharmony_ciMODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); 2306