18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com> 48c2ecf20Sopenharmony_ci * Copyright © 2004 Micron Technology Inc. 58c2ecf20Sopenharmony_ci * Copyright © 2004 David Brownell 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 98c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/sched.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 188c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 198c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 208c2ecf20Sopenharmony_ci#include <linux/omap-dma.h> 218c2ecf20Sopenharmony_ci#include <linux/io.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/of.h> 248c2ecf20Sopenharmony_ci#include <linux/of_device.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/mtd/nand_bch.h> 278c2ecf20Sopenharmony_ci#include <linux/platform_data/elm.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/omap-gpmc.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_data/mtd-nand-omap2.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define DRIVER_NAME "omap2-nand" 338c2ecf20Sopenharmony_ci#define OMAP_NAND_TIMEOUT_MS 5000 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define NAND_Ecc_P1e (1 << 0) 368c2ecf20Sopenharmony_ci#define NAND_Ecc_P2e (1 << 1) 378c2ecf20Sopenharmony_ci#define NAND_Ecc_P4e (1 << 2) 388c2ecf20Sopenharmony_ci#define NAND_Ecc_P8e (1 << 3) 398c2ecf20Sopenharmony_ci#define NAND_Ecc_P16e (1 << 4) 408c2ecf20Sopenharmony_ci#define NAND_Ecc_P32e (1 << 5) 418c2ecf20Sopenharmony_ci#define NAND_Ecc_P64e (1 << 6) 428c2ecf20Sopenharmony_ci#define NAND_Ecc_P128e (1 << 7) 438c2ecf20Sopenharmony_ci#define NAND_Ecc_P256e (1 << 8) 448c2ecf20Sopenharmony_ci#define NAND_Ecc_P512e (1 << 9) 458c2ecf20Sopenharmony_ci#define NAND_Ecc_P1024e (1 << 10) 468c2ecf20Sopenharmony_ci#define NAND_Ecc_P2048e (1 << 11) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define NAND_Ecc_P1o (1 << 16) 498c2ecf20Sopenharmony_ci#define NAND_Ecc_P2o (1 << 17) 508c2ecf20Sopenharmony_ci#define NAND_Ecc_P4o (1 << 18) 518c2ecf20Sopenharmony_ci#define NAND_Ecc_P8o (1 << 19) 528c2ecf20Sopenharmony_ci#define NAND_Ecc_P16o (1 << 20) 538c2ecf20Sopenharmony_ci#define NAND_Ecc_P32o (1 << 21) 548c2ecf20Sopenharmony_ci#define NAND_Ecc_P64o (1 << 22) 558c2ecf20Sopenharmony_ci#define NAND_Ecc_P128o (1 << 23) 568c2ecf20Sopenharmony_ci#define NAND_Ecc_P256o (1 << 24) 578c2ecf20Sopenharmony_ci#define NAND_Ecc_P512o (1 << 25) 588c2ecf20Sopenharmony_ci#define NAND_Ecc_P1024o (1 << 26) 598c2ecf20Sopenharmony_ci#define NAND_Ecc_P2048o (1 << 27) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define TF(value) (value ? 1 : 0) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) 648c2ecf20Sopenharmony_ci#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) 658c2ecf20Sopenharmony_ci#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) 668c2ecf20Sopenharmony_ci#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) 678c2ecf20Sopenharmony_ci#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) 688c2ecf20Sopenharmony_ci#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) 698c2ecf20Sopenharmony_ci#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) 708c2ecf20Sopenharmony_ci#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) 738c2ecf20Sopenharmony_ci#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) 748c2ecf20Sopenharmony_ci#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) 758c2ecf20Sopenharmony_ci#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) 768c2ecf20Sopenharmony_ci#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) 778c2ecf20Sopenharmony_ci#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) 788c2ecf20Sopenharmony_ci#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) 798c2ecf20Sopenharmony_ci#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) 828c2ecf20Sopenharmony_ci#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) 838c2ecf20Sopenharmony_ci#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) 848c2ecf20Sopenharmony_ci#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) 858c2ecf20Sopenharmony_ci#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) 868c2ecf20Sopenharmony_ci#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) 878c2ecf20Sopenharmony_ci#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) 888c2ecf20Sopenharmony_ci#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) 918c2ecf20Sopenharmony_ci#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) 928c2ecf20Sopenharmony_ci#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) 938c2ecf20Sopenharmony_ci#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) 948c2ecf20Sopenharmony_ci#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) 958c2ecf20Sopenharmony_ci#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) 968c2ecf20Sopenharmony_ci#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) 978c2ecf20Sopenharmony_ci#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) 1008c2ecf20Sopenharmony_ci#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define PREFETCH_CONFIG1_CS_SHIFT 24 1038c2ecf20Sopenharmony_ci#define ECC_CONFIG_CS_SHIFT 1 1048c2ecf20Sopenharmony_ci#define CS_MASK 0x7 1058c2ecf20Sopenharmony_ci#define ENABLE_PREFETCH (0x1 << 7) 1068c2ecf20Sopenharmony_ci#define DMA_MPU_MODE_SHIFT 2 1078c2ecf20Sopenharmony_ci#define ECCSIZE0_SHIFT 12 1088c2ecf20Sopenharmony_ci#define ECCSIZE1_SHIFT 22 1098c2ecf20Sopenharmony_ci#define ECC1RESULTSIZE 0x1 1108c2ecf20Sopenharmony_ci#define ECCCLEAR 0x100 1118c2ecf20Sopenharmony_ci#define ECC1 0x1 1128c2ecf20Sopenharmony_ci#define PREFETCH_FIFOTHRESHOLD_MAX 0x40 1138c2ecf20Sopenharmony_ci#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8) 1148c2ecf20Sopenharmony_ci#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff) 1158c2ecf20Sopenharmony_ci#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F) 1168c2ecf20Sopenharmony_ci#define STATUS_BUFF_EMPTY 0x00000001 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define SECTOR_BYTES 512 1198c2ecf20Sopenharmony_ci/* 4 bit padding to make byte aligned, 56 = 52 + 4 */ 1208c2ecf20Sopenharmony_ci#define BCH4_BIT_PAD 4 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* GPMC ecc engine settings for read */ 1238c2ecf20Sopenharmony_ci#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ 1248c2ecf20Sopenharmony_ci#define BCH8R_ECC_SIZE0 0x1a /* ecc_size0 = 26 */ 1258c2ecf20Sopenharmony_ci#define BCH8R_ECC_SIZE1 0x2 /* ecc_size1 = 2 */ 1268c2ecf20Sopenharmony_ci#define BCH4R_ECC_SIZE0 0xd /* ecc_size0 = 13 */ 1278c2ecf20Sopenharmony_ci#define BCH4R_ECC_SIZE1 0x3 /* ecc_size1 = 3 */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* GPMC ecc engine settings for write */ 1308c2ecf20Sopenharmony_ci#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */ 1318c2ecf20Sopenharmony_ci#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ 1328c2ecf20Sopenharmony_ci#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define BADBLOCK_MARKER_LENGTH 2 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55, 1378c2ecf20Sopenharmony_ci 0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78, 1388c2ecf20Sopenharmony_ci 0x48, 0x76, 0xa9, 0x3b, 0x97, 0xd1, 0x7a, 0x93, 1398c2ecf20Sopenharmony_ci 0x07, 0x0e}; 1408c2ecf20Sopenharmony_cistatic u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, 1418c2ecf20Sopenharmony_ci 0xac, 0x6b, 0xff, 0x99, 0x7b}; 1428c2ecf20Sopenharmony_cistatic u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct omap_nand_info { 1458c2ecf20Sopenharmony_ci struct nand_chip nand; 1468c2ecf20Sopenharmony_ci struct platform_device *pdev; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci int gpmc_cs; 1498c2ecf20Sopenharmony_ci bool dev_ready; 1508c2ecf20Sopenharmony_ci enum nand_io xfer_type; 1518c2ecf20Sopenharmony_ci int devsize; 1528c2ecf20Sopenharmony_ci enum omap_ecc ecc_opt; 1538c2ecf20Sopenharmony_ci struct device_node *elm_of_node; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci unsigned long phys_base; 1568c2ecf20Sopenharmony_ci struct completion comp; 1578c2ecf20Sopenharmony_ci struct dma_chan *dma; 1588c2ecf20Sopenharmony_ci int gpmc_irq_fifo; 1598c2ecf20Sopenharmony_ci int gpmc_irq_count; 1608c2ecf20Sopenharmony_ci enum { 1618c2ecf20Sopenharmony_ci OMAP_NAND_IO_READ = 0, /* read */ 1628c2ecf20Sopenharmony_ci OMAP_NAND_IO_WRITE, /* write */ 1638c2ecf20Sopenharmony_ci } iomode; 1648c2ecf20Sopenharmony_ci u_char *buf; 1658c2ecf20Sopenharmony_ci int buf_len; 1668c2ecf20Sopenharmony_ci /* Interface to GPMC */ 1678c2ecf20Sopenharmony_ci struct gpmc_nand_regs reg; 1688c2ecf20Sopenharmony_ci struct gpmc_nand_ops *ops; 1698c2ecf20Sopenharmony_ci bool flash_bbt; 1708c2ecf20Sopenharmony_ci /* fields specific for BCHx_HW ECC scheme */ 1718c2ecf20Sopenharmony_ci struct device *elm_dev; 1728c2ecf20Sopenharmony_ci /* NAND ready gpio */ 1738c2ecf20Sopenharmony_ci struct gpio_desc *ready_gpiod; 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/** 1828c2ecf20Sopenharmony_ci * omap_prefetch_enable - configures and starts prefetch transfer 1838c2ecf20Sopenharmony_ci * @cs: cs (chip select) number 1848c2ecf20Sopenharmony_ci * @fifo_th: fifo threshold to be used for read/ write 1858c2ecf20Sopenharmony_ci * @dma_mode: dma mode enable (1) or disable (0) 1868c2ecf20Sopenharmony_ci * @u32_count: number of bytes to be transferred 1878c2ecf20Sopenharmony_ci * @is_write: prefetch read(0) or write post(1) mode 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistatic int omap_prefetch_enable(int cs, int fifo_th, int dma_mode, 1908c2ecf20Sopenharmony_ci unsigned int u32_count, int is_write, struct omap_nand_info *info) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u32 val; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) 1958c2ecf20Sopenharmony_ci return -1; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (readl(info->reg.gpmc_prefetch_control)) 1988c2ecf20Sopenharmony_ci return -EBUSY; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Set the amount of bytes to be prefetched */ 2018c2ecf20Sopenharmony_ci writel(u32_count, info->reg.gpmc_prefetch_config2); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* Set dma/mpu mode, the prefetch read / post write and 2048c2ecf20Sopenharmony_ci * enable the engine. Set which cs is has requested for. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci val = ((cs << PREFETCH_CONFIG1_CS_SHIFT) | 2078c2ecf20Sopenharmony_ci PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH | 2088c2ecf20Sopenharmony_ci (dma_mode << DMA_MPU_MODE_SHIFT) | (is_write & 0x1)); 2098c2ecf20Sopenharmony_ci writel(val, info->reg.gpmc_prefetch_config1); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* Start the prefetch engine */ 2128c2ecf20Sopenharmony_ci writel(0x1, info->reg.gpmc_prefetch_control); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return 0; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/** 2188c2ecf20Sopenharmony_ci * omap_prefetch_reset - disables and stops the prefetch engine 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic int omap_prefetch_reset(int cs, struct omap_nand_info *info) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci u32 config1; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* check if the same module/cs is trying to reset */ 2258c2ecf20Sopenharmony_ci config1 = readl(info->reg.gpmc_prefetch_config1); 2268c2ecf20Sopenharmony_ci if (((config1 >> PREFETCH_CONFIG1_CS_SHIFT) & CS_MASK) != cs) 2278c2ecf20Sopenharmony_ci return -EINVAL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Stop the PFPW engine */ 2308c2ecf20Sopenharmony_ci writel(0x0, info->reg.gpmc_prefetch_control); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Reset/disable the PFPW engine */ 2338c2ecf20Sopenharmony_ci writel(0x0, info->reg.gpmc_prefetch_config1); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/** 2398c2ecf20Sopenharmony_ci * omap_hwcontrol - hardware specific access to control-lines 2408c2ecf20Sopenharmony_ci * @chip: NAND chip object 2418c2ecf20Sopenharmony_ci * @cmd: command to device 2428c2ecf20Sopenharmony_ci * @ctrl: 2438c2ecf20Sopenharmony_ci * NAND_NCE: bit 0 -> don't care 2448c2ecf20Sopenharmony_ci * NAND_CLE: bit 1 -> Command Latch 2458c2ecf20Sopenharmony_ci * NAND_ALE: bit 2 -> Address Latch 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * NOTE: boards may use different bits for these!! 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistatic void omap_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (cmd != NAND_CMD_NONE) { 2548c2ecf20Sopenharmony_ci if (ctrl & NAND_CLE) 2558c2ecf20Sopenharmony_ci writeb(cmd, info->reg.gpmc_nand_command); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci else if (ctrl & NAND_ALE) 2588c2ecf20Sopenharmony_ci writeb(cmd, info->reg.gpmc_nand_address); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci else /* NAND_NCE */ 2618c2ecf20Sopenharmony_ci writeb(cmd, info->reg.gpmc_nand_data); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/** 2668c2ecf20Sopenharmony_ci * omap_read_buf8 - read data from NAND controller into buffer 2678c2ecf20Sopenharmony_ci * @mtd: MTD device structure 2688c2ecf20Sopenharmony_ci * @buf: buffer to store date 2698c2ecf20Sopenharmony_ci * @len: number of bytes to read 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_cistatic void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ioread8_rep(nand->legacy.IO_ADDR_R, buf, len); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * omap_write_buf8 - write buffer to NAND controller 2808c2ecf20Sopenharmony_ci * @mtd: MTD device structure 2818c2ecf20Sopenharmony_ci * @buf: data buffer 2828c2ecf20Sopenharmony_ci * @len: number of bytes to write 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_cistatic void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 2878c2ecf20Sopenharmony_ci u_char *p = (u_char *)buf; 2888c2ecf20Sopenharmony_ci bool status; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci while (len--) { 2918c2ecf20Sopenharmony_ci iowrite8(*p++, info->nand.legacy.IO_ADDR_W); 2928c2ecf20Sopenharmony_ci /* wait until buffer is available for write */ 2938c2ecf20Sopenharmony_ci do { 2948c2ecf20Sopenharmony_ci status = info->ops->nand_writebuffer_empty(); 2958c2ecf20Sopenharmony_ci } while (!status); 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/** 3008c2ecf20Sopenharmony_ci * omap_read_buf16 - read data from NAND controller into buffer 3018c2ecf20Sopenharmony_ci * @mtd: MTD device structure 3028c2ecf20Sopenharmony_ci * @buf: buffer to store date 3038c2ecf20Sopenharmony_ci * @len: number of bytes to read 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ioread16_rep(nand->legacy.IO_ADDR_R, buf, len / 2); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/** 3138c2ecf20Sopenharmony_ci * omap_write_buf16 - write buffer to NAND controller 3148c2ecf20Sopenharmony_ci * @mtd: MTD device structure 3158c2ecf20Sopenharmony_ci * @buf: data buffer 3168c2ecf20Sopenharmony_ci * @len: number of bytes to write 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_cistatic void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 3218c2ecf20Sopenharmony_ci u16 *p = (u16 *) buf; 3228c2ecf20Sopenharmony_ci bool status; 3238c2ecf20Sopenharmony_ci /* FIXME try bursts of writesw() or DMA ... */ 3248c2ecf20Sopenharmony_ci len >>= 1; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci while (len--) { 3278c2ecf20Sopenharmony_ci iowrite16(*p++, info->nand.legacy.IO_ADDR_W); 3288c2ecf20Sopenharmony_ci /* wait until buffer is available for write */ 3298c2ecf20Sopenharmony_ci do { 3308c2ecf20Sopenharmony_ci status = info->ops->nand_writebuffer_empty(); 3318c2ecf20Sopenharmony_ci } while (!status); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/** 3368c2ecf20Sopenharmony_ci * omap_read_buf_pref - read data from NAND controller into buffer 3378c2ecf20Sopenharmony_ci * @chip: NAND chip object 3388c2ecf20Sopenharmony_ci * @buf: buffer to store date 3398c2ecf20Sopenharmony_ci * @len: number of bytes to read 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_cistatic void omap_read_buf_pref(struct nand_chip *chip, u_char *buf, int len) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 3448c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 3458c2ecf20Sopenharmony_ci uint32_t r_count = 0; 3468c2ecf20Sopenharmony_ci int ret = 0; 3478c2ecf20Sopenharmony_ci u32 *p = (u32 *)buf; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* take care of subpage reads */ 3508c2ecf20Sopenharmony_ci if (len % 4) { 3518c2ecf20Sopenharmony_ci if (info->nand.options & NAND_BUSWIDTH_16) 3528c2ecf20Sopenharmony_ci omap_read_buf16(mtd, buf, len % 4); 3538c2ecf20Sopenharmony_ci else 3548c2ecf20Sopenharmony_ci omap_read_buf8(mtd, buf, len % 4); 3558c2ecf20Sopenharmony_ci p = (u32 *) (buf + len % 4); 3568c2ecf20Sopenharmony_ci len -= len % 4; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* configure and start prefetch transfer */ 3608c2ecf20Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 3618c2ecf20Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info); 3628c2ecf20Sopenharmony_ci if (ret) { 3638c2ecf20Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 3648c2ecf20Sopenharmony_ci if (info->nand.options & NAND_BUSWIDTH_16) 3658c2ecf20Sopenharmony_ci omap_read_buf16(mtd, (u_char *)p, len); 3668c2ecf20Sopenharmony_ci else 3678c2ecf20Sopenharmony_ci omap_read_buf8(mtd, (u_char *)p, len); 3688c2ecf20Sopenharmony_ci } else { 3698c2ecf20Sopenharmony_ci do { 3708c2ecf20Sopenharmony_ci r_count = readl(info->reg.gpmc_prefetch_status); 3718c2ecf20Sopenharmony_ci r_count = PREFETCH_STATUS_FIFO_CNT(r_count); 3728c2ecf20Sopenharmony_ci r_count = r_count >> 2; 3738c2ecf20Sopenharmony_ci ioread32_rep(info->nand.legacy.IO_ADDR_R, p, r_count); 3748c2ecf20Sopenharmony_ci p += r_count; 3758c2ecf20Sopenharmony_ci len -= r_count << 2; 3768c2ecf20Sopenharmony_ci } while (len); 3778c2ecf20Sopenharmony_ci /* disable and stop the PFPW engine */ 3788c2ecf20Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/** 3838c2ecf20Sopenharmony_ci * omap_write_buf_pref - write buffer to NAND controller 3848c2ecf20Sopenharmony_ci * @chip: NAND chip object 3858c2ecf20Sopenharmony_ci * @buf: data buffer 3868c2ecf20Sopenharmony_ci * @len: number of bytes to write 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_cistatic void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf, 3898c2ecf20Sopenharmony_ci int len) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 3928c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 3938c2ecf20Sopenharmony_ci uint32_t w_count = 0; 3948c2ecf20Sopenharmony_ci int i = 0, ret = 0; 3958c2ecf20Sopenharmony_ci u16 *p = (u16 *)buf; 3968c2ecf20Sopenharmony_ci unsigned long tim, limit; 3978c2ecf20Sopenharmony_ci u32 val; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* take care of subpage writes */ 4008c2ecf20Sopenharmony_ci if (len % 2 != 0) { 4018c2ecf20Sopenharmony_ci writeb(*buf, info->nand.legacy.IO_ADDR_W); 4028c2ecf20Sopenharmony_ci p = (u16 *)(buf + 1); 4038c2ecf20Sopenharmony_ci len--; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci /* configure and start prefetch transfer */ 4078c2ecf20Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 4088c2ecf20Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info); 4098c2ecf20Sopenharmony_ci if (ret) { 4108c2ecf20Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 4118c2ecf20Sopenharmony_ci if (info->nand.options & NAND_BUSWIDTH_16) 4128c2ecf20Sopenharmony_ci omap_write_buf16(mtd, (u_char *)p, len); 4138c2ecf20Sopenharmony_ci else 4148c2ecf20Sopenharmony_ci omap_write_buf8(mtd, (u_char *)p, len); 4158c2ecf20Sopenharmony_ci } else { 4168c2ecf20Sopenharmony_ci while (len) { 4178c2ecf20Sopenharmony_ci w_count = readl(info->reg.gpmc_prefetch_status); 4188c2ecf20Sopenharmony_ci w_count = PREFETCH_STATUS_FIFO_CNT(w_count); 4198c2ecf20Sopenharmony_ci w_count = w_count >> 1; 4208c2ecf20Sopenharmony_ci for (i = 0; (i < w_count) && len; i++, len -= 2) 4218c2ecf20Sopenharmony_ci iowrite16(*p++, info->nand.legacy.IO_ADDR_W); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci /* wait for data to flushed-out before reset the prefetch */ 4248c2ecf20Sopenharmony_ci tim = 0; 4258c2ecf20Sopenharmony_ci limit = (loops_per_jiffy * 4268c2ecf20Sopenharmony_ci msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); 4278c2ecf20Sopenharmony_ci do { 4288c2ecf20Sopenharmony_ci cpu_relax(); 4298c2ecf20Sopenharmony_ci val = readl(info->reg.gpmc_prefetch_status); 4308c2ecf20Sopenharmony_ci val = PREFETCH_STATUS_COUNT(val); 4318c2ecf20Sopenharmony_ci } while (val && (tim++ < limit)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* disable and stop the PFPW engine */ 4348c2ecf20Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* 4398c2ecf20Sopenharmony_ci * omap_nand_dma_callback: callback on the completion of dma transfer 4408c2ecf20Sopenharmony_ci * @data: pointer to completion data structure 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic void omap_nand_dma_callback(void *data) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci complete((struct completion *) data); 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * omap_nand_dma_transfer: configure and start dma transfer 4498c2ecf20Sopenharmony_ci * @mtd: MTD device structure 4508c2ecf20Sopenharmony_ci * @addr: virtual address in RAM of source/destination 4518c2ecf20Sopenharmony_ci * @len: number of data bytes to be transferred 4528c2ecf20Sopenharmony_ci * @is_write: flag for read/write operation 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, 4558c2ecf20Sopenharmony_ci unsigned int len, int is_write) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 4588c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 4598c2ecf20Sopenharmony_ci enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : 4608c2ecf20Sopenharmony_ci DMA_FROM_DEVICE; 4618c2ecf20Sopenharmony_ci struct scatterlist sg; 4628c2ecf20Sopenharmony_ci unsigned long tim, limit; 4638c2ecf20Sopenharmony_ci unsigned n; 4648c2ecf20Sopenharmony_ci int ret; 4658c2ecf20Sopenharmony_ci u32 val; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!virt_addr_valid(addr)) 4688c2ecf20Sopenharmony_ci goto out_copy; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci sg_init_one(&sg, addr, len); 4718c2ecf20Sopenharmony_ci n = dma_map_sg(info->dma->device->dev, &sg, 1, dir); 4728c2ecf20Sopenharmony_ci if (n == 0) { 4738c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, 4748c2ecf20Sopenharmony_ci "Couldn't DMA map a %d byte buffer\n", len); 4758c2ecf20Sopenharmony_ci goto out_copy; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci tx = dmaengine_prep_slave_sg(info->dma, &sg, n, 4798c2ecf20Sopenharmony_ci is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, 4808c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 4818c2ecf20Sopenharmony_ci if (!tx) 4828c2ecf20Sopenharmony_ci goto out_copy_unmap; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci tx->callback = omap_nand_dma_callback; 4858c2ecf20Sopenharmony_ci tx->callback_param = &info->comp; 4868c2ecf20Sopenharmony_ci dmaengine_submit(tx); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci init_completion(&info->comp); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* setup and start DMA using dma_addr */ 4918c2ecf20Sopenharmony_ci dma_async_issue_pending(info->dma); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* configure and start prefetch transfer */ 4948c2ecf20Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 4958c2ecf20Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write, info); 4968c2ecf20Sopenharmony_ci if (ret) 4978c2ecf20Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 4988c2ecf20Sopenharmony_ci goto out_copy_unmap; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci wait_for_completion(&info->comp); 5018c2ecf20Sopenharmony_ci tim = 0; 5028c2ecf20Sopenharmony_ci limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci do { 5058c2ecf20Sopenharmony_ci cpu_relax(); 5068c2ecf20Sopenharmony_ci val = readl(info->reg.gpmc_prefetch_status); 5078c2ecf20Sopenharmony_ci val = PREFETCH_STATUS_COUNT(val); 5088c2ecf20Sopenharmony_ci } while (val && (tim++ < limit)); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* disable and stop the PFPW engine */ 5118c2ecf20Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciout_copy_unmap: 5178c2ecf20Sopenharmony_ci dma_unmap_sg(info->dma->device->dev, &sg, 1, dir); 5188c2ecf20Sopenharmony_ciout_copy: 5198c2ecf20Sopenharmony_ci if (info->nand.options & NAND_BUSWIDTH_16) 5208c2ecf20Sopenharmony_ci is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) 5218c2ecf20Sopenharmony_ci : omap_write_buf16(mtd, (u_char *) addr, len); 5228c2ecf20Sopenharmony_ci else 5238c2ecf20Sopenharmony_ci is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) 5248c2ecf20Sopenharmony_ci : omap_write_buf8(mtd, (u_char *) addr, len); 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/** 5298c2ecf20Sopenharmony_ci * omap_read_buf_dma_pref - read data from NAND controller into buffer 5308c2ecf20Sopenharmony_ci * @chip: NAND chip object 5318c2ecf20Sopenharmony_ci * @buf: buffer to store date 5328c2ecf20Sopenharmony_ci * @len: number of bytes to read 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_cistatic void omap_read_buf_dma_pref(struct nand_chip *chip, u_char *buf, 5358c2ecf20Sopenharmony_ci int len) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (len <= mtd->oobsize) 5408c2ecf20Sopenharmony_ci omap_read_buf_pref(chip, buf, len); 5418c2ecf20Sopenharmony_ci else 5428c2ecf20Sopenharmony_ci /* start transfer in DMA mode */ 5438c2ecf20Sopenharmony_ci omap_nand_dma_transfer(mtd, buf, len, 0x0); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/** 5478c2ecf20Sopenharmony_ci * omap_write_buf_dma_pref - write buffer to NAND controller 5488c2ecf20Sopenharmony_ci * @chip: NAND chip object 5498c2ecf20Sopenharmony_ci * @buf: data buffer 5508c2ecf20Sopenharmony_ci * @len: number of bytes to write 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_cistatic void omap_write_buf_dma_pref(struct nand_chip *chip, const u_char *buf, 5538c2ecf20Sopenharmony_ci int len) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (len <= mtd->oobsize) 5588c2ecf20Sopenharmony_ci omap_write_buf_pref(chip, buf, len); 5598c2ecf20Sopenharmony_ci else 5608c2ecf20Sopenharmony_ci /* start transfer in DMA mode */ 5618c2ecf20Sopenharmony_ci omap_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* 5658c2ecf20Sopenharmony_ci * omap_nand_irq - GPMC irq handler 5668c2ecf20Sopenharmony_ci * @this_irq: gpmc irq number 5678c2ecf20Sopenharmony_ci * @dev: omap_nand_info structure pointer is passed here 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_cistatic irqreturn_t omap_nand_irq(int this_irq, void *dev) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct omap_nand_info *info = (struct omap_nand_info *) dev; 5728c2ecf20Sopenharmony_ci u32 bytes; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci bytes = readl(info->reg.gpmc_prefetch_status); 5758c2ecf20Sopenharmony_ci bytes = PREFETCH_STATUS_FIFO_CNT(bytes); 5768c2ecf20Sopenharmony_ci bytes = bytes & 0xFFFC; /* io in multiple of 4 bytes */ 5778c2ecf20Sopenharmony_ci if (info->iomode == OMAP_NAND_IO_WRITE) { /* checks for write io */ 5788c2ecf20Sopenharmony_ci if (this_irq == info->gpmc_irq_count) 5798c2ecf20Sopenharmony_ci goto done; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (info->buf_len && (info->buf_len < bytes)) 5828c2ecf20Sopenharmony_ci bytes = info->buf_len; 5838c2ecf20Sopenharmony_ci else if (!info->buf_len) 5848c2ecf20Sopenharmony_ci bytes = 0; 5858c2ecf20Sopenharmony_ci iowrite32_rep(info->nand.legacy.IO_ADDR_W, (u32 *)info->buf, 5868c2ecf20Sopenharmony_ci bytes >> 2); 5878c2ecf20Sopenharmony_ci info->buf = info->buf + bytes; 5888c2ecf20Sopenharmony_ci info->buf_len -= bytes; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci } else { 5918c2ecf20Sopenharmony_ci ioread32_rep(info->nand.legacy.IO_ADDR_R, (u32 *)info->buf, 5928c2ecf20Sopenharmony_ci bytes >> 2); 5938c2ecf20Sopenharmony_ci info->buf = info->buf + bytes; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (this_irq == info->gpmc_irq_count) 5968c2ecf20Sopenharmony_ci goto done; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cidone: 6028c2ecf20Sopenharmony_ci complete(&info->comp); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci disable_irq_nosync(info->gpmc_irq_fifo); 6058c2ecf20Sopenharmony_ci disable_irq_nosync(info->gpmc_irq_count); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci/* 6118c2ecf20Sopenharmony_ci * omap_read_buf_irq_pref - read data from NAND controller into buffer 6128c2ecf20Sopenharmony_ci * @chip: NAND chip object 6138c2ecf20Sopenharmony_ci * @buf: buffer to store date 6148c2ecf20Sopenharmony_ci * @len: number of bytes to read 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_cistatic void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf, 6178c2ecf20Sopenharmony_ci int len) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6208c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 6218c2ecf20Sopenharmony_ci int ret = 0; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (len <= mtd->oobsize) { 6248c2ecf20Sopenharmony_ci omap_read_buf_pref(chip, buf, len); 6258c2ecf20Sopenharmony_ci return; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci info->iomode = OMAP_NAND_IO_READ; 6298c2ecf20Sopenharmony_ci info->buf = buf; 6308c2ecf20Sopenharmony_ci init_completion(&info->comp); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* configure and start prefetch transfer */ 6338c2ecf20Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 6348c2ecf20Sopenharmony_ci PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info); 6358c2ecf20Sopenharmony_ci if (ret) 6368c2ecf20Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 6378c2ecf20Sopenharmony_ci goto out_copy; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci info->buf_len = len; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci enable_irq(info->gpmc_irq_count); 6428c2ecf20Sopenharmony_ci enable_irq(info->gpmc_irq_fifo); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* waiting for read to complete */ 6458c2ecf20Sopenharmony_ci wait_for_completion(&info->comp); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* disable and stop the PFPW engine */ 6488c2ecf20Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ciout_copy: 6528c2ecf20Sopenharmony_ci if (info->nand.options & NAND_BUSWIDTH_16) 6538c2ecf20Sopenharmony_ci omap_read_buf16(mtd, buf, len); 6548c2ecf20Sopenharmony_ci else 6558c2ecf20Sopenharmony_ci omap_read_buf8(mtd, buf, len); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci/* 6598c2ecf20Sopenharmony_ci * omap_write_buf_irq_pref - write buffer to NAND controller 6608c2ecf20Sopenharmony_ci * @chip: NAND chip object 6618c2ecf20Sopenharmony_ci * @buf: data buffer 6628c2ecf20Sopenharmony_ci * @len: number of bytes to write 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_cistatic void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf, 6658c2ecf20Sopenharmony_ci int len) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6688c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 6698c2ecf20Sopenharmony_ci int ret = 0; 6708c2ecf20Sopenharmony_ci unsigned long tim, limit; 6718c2ecf20Sopenharmony_ci u32 val; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (len <= mtd->oobsize) { 6748c2ecf20Sopenharmony_ci omap_write_buf_pref(chip, buf, len); 6758c2ecf20Sopenharmony_ci return; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci info->iomode = OMAP_NAND_IO_WRITE; 6798c2ecf20Sopenharmony_ci info->buf = (u_char *) buf; 6808c2ecf20Sopenharmony_ci init_completion(&info->comp); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* configure and start prefetch transfer : size=24 */ 6838c2ecf20Sopenharmony_ci ret = omap_prefetch_enable(info->gpmc_cs, 6848c2ecf20Sopenharmony_ci (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info); 6858c2ecf20Sopenharmony_ci if (ret) 6868c2ecf20Sopenharmony_ci /* PFPW engine is busy, use cpu copy method */ 6878c2ecf20Sopenharmony_ci goto out_copy; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci info->buf_len = len; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci enable_irq(info->gpmc_irq_count); 6928c2ecf20Sopenharmony_ci enable_irq(info->gpmc_irq_fifo); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* waiting for write to complete */ 6958c2ecf20Sopenharmony_ci wait_for_completion(&info->comp); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* wait for data to flushed-out before reset the prefetch */ 6988c2ecf20Sopenharmony_ci tim = 0; 6998c2ecf20Sopenharmony_ci limit = (loops_per_jiffy * msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS)); 7008c2ecf20Sopenharmony_ci do { 7018c2ecf20Sopenharmony_ci val = readl(info->reg.gpmc_prefetch_status); 7028c2ecf20Sopenharmony_ci val = PREFETCH_STATUS_COUNT(val); 7038c2ecf20Sopenharmony_ci cpu_relax(); 7048c2ecf20Sopenharmony_ci } while (val && (tim++ < limit)); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* disable and stop the PFPW engine */ 7078c2ecf20Sopenharmony_ci omap_prefetch_reset(info->gpmc_cs, info); 7088c2ecf20Sopenharmony_ci return; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ciout_copy: 7118c2ecf20Sopenharmony_ci if (info->nand.options & NAND_BUSWIDTH_16) 7128c2ecf20Sopenharmony_ci omap_write_buf16(mtd, buf, len); 7138c2ecf20Sopenharmony_ci else 7148c2ecf20Sopenharmony_ci omap_write_buf8(mtd, buf, len); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci/** 7188c2ecf20Sopenharmony_ci * gen_true_ecc - This function will generate true ECC value 7198c2ecf20Sopenharmony_ci * @ecc_buf: buffer to store ecc code 7208c2ecf20Sopenharmony_ci * 7218c2ecf20Sopenharmony_ci * This generated true ECC value can be used when correcting 7228c2ecf20Sopenharmony_ci * data read from NAND flash memory core 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_cistatic void gen_true_ecc(u8 *ecc_buf) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | 7278c2ecf20Sopenharmony_ci ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | 7308c2ecf20Sopenharmony_ci P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); 7318c2ecf20Sopenharmony_ci ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | 7328c2ecf20Sopenharmony_ci P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); 7338c2ecf20Sopenharmony_ci ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | 7348c2ecf20Sopenharmony_ci P1e(tmp) | P2048o(tmp) | P2048e(tmp)); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci/** 7388c2ecf20Sopenharmony_ci * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data 7398c2ecf20Sopenharmony_ci * @ecc_data1: ecc code from nand spare area 7408c2ecf20Sopenharmony_ci * @ecc_data2: ecc code from hardware register obtained from hardware ecc 7418c2ecf20Sopenharmony_ci * @page_data: page data 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * This function compares two ECC's and indicates if there is an error. 7448c2ecf20Sopenharmony_ci * If the error can be corrected it will be corrected to the buffer. 7458c2ecf20Sopenharmony_ci * If there is no error, %0 is returned. If there is an error but it 7468c2ecf20Sopenharmony_ci * was corrected, %1 is returned. Otherwise, %-1 is returned. 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_cistatic int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ 7498c2ecf20Sopenharmony_ci u8 *ecc_data2, /* read from register */ 7508c2ecf20Sopenharmony_ci u8 *page_data) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci uint i; 7538c2ecf20Sopenharmony_ci u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; 7548c2ecf20Sopenharmony_ci u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; 7558c2ecf20Sopenharmony_ci u8 ecc_bit[24]; 7568c2ecf20Sopenharmony_ci u8 ecc_sum = 0; 7578c2ecf20Sopenharmony_ci u8 find_bit = 0; 7588c2ecf20Sopenharmony_ci uint find_byte = 0; 7598c2ecf20Sopenharmony_ci int isEccFF; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci gen_true_ecc(ecc_data1); 7648c2ecf20Sopenharmony_ci gen_true_ecc(ecc_data2); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci for (i = 0; i <= 2; i++) { 7678c2ecf20Sopenharmony_ci *(ecc_data1 + i) = ~(*(ecc_data1 + i)); 7688c2ecf20Sopenharmony_ci *(ecc_data2 + i) = ~(*(ecc_data2 + i)); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7728c2ecf20Sopenharmony_ci tmp0_bit[i] = *ecc_data1 % 2; 7738c2ecf20Sopenharmony_ci *ecc_data1 = *ecc_data1 / 2; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7778c2ecf20Sopenharmony_ci tmp1_bit[i] = *(ecc_data1 + 1) % 2; 7788c2ecf20Sopenharmony_ci *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7828c2ecf20Sopenharmony_ci tmp2_bit[i] = *(ecc_data1 + 2) % 2; 7838c2ecf20Sopenharmony_ci *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7878c2ecf20Sopenharmony_ci comp0_bit[i] = *ecc_data2 % 2; 7888c2ecf20Sopenharmony_ci *ecc_data2 = *ecc_data2 / 2; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7928c2ecf20Sopenharmony_ci comp1_bit[i] = *(ecc_data2 + 1) % 2; 7938c2ecf20Sopenharmony_ci *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 7978c2ecf20Sopenharmony_ci comp2_bit[i] = *(ecc_data2 + 2) % 2; 7988c2ecf20Sopenharmony_ci *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 8028c2ecf20Sopenharmony_ci ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 8058c2ecf20Sopenharmony_ci ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 8088c2ecf20Sopenharmony_ci ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; 8118c2ecf20Sopenharmony_ci ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci for (i = 0; i < 24; i++) 8148c2ecf20Sopenharmony_ci ecc_sum += ecc_bit[i]; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci switch (ecc_sum) { 8178c2ecf20Sopenharmony_ci case 0: 8188c2ecf20Sopenharmony_ci /* Not reached because this function is not called if 8198c2ecf20Sopenharmony_ci * ECC values are equal 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_ci return 0; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci case 1: 8248c2ecf20Sopenharmony_ci /* Uncorrectable error */ 8258c2ecf20Sopenharmony_ci pr_debug("ECC UNCORRECTED_ERROR 1\n"); 8268c2ecf20Sopenharmony_ci return -EBADMSG; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci case 11: 8298c2ecf20Sopenharmony_ci /* UN-Correctable error */ 8308c2ecf20Sopenharmony_ci pr_debug("ECC UNCORRECTED_ERROR B\n"); 8318c2ecf20Sopenharmony_ci return -EBADMSG; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci case 12: 8348c2ecf20Sopenharmony_ci /* Correctable error */ 8358c2ecf20Sopenharmony_ci find_byte = (ecc_bit[23] << 8) + 8368c2ecf20Sopenharmony_ci (ecc_bit[21] << 7) + 8378c2ecf20Sopenharmony_ci (ecc_bit[19] << 6) + 8388c2ecf20Sopenharmony_ci (ecc_bit[17] << 5) + 8398c2ecf20Sopenharmony_ci (ecc_bit[15] << 4) + 8408c2ecf20Sopenharmony_ci (ecc_bit[13] << 3) + 8418c2ecf20Sopenharmony_ci (ecc_bit[11] << 2) + 8428c2ecf20Sopenharmony_ci (ecc_bit[9] << 1) + 8438c2ecf20Sopenharmony_ci ecc_bit[7]; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci pr_debug("Correcting single bit ECC error at offset: " 8488c2ecf20Sopenharmony_ci "%d, bit: %d\n", find_byte, find_bit); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci page_data[find_byte] ^= (1 << find_bit); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci return 1; 8538c2ecf20Sopenharmony_ci default: 8548c2ecf20Sopenharmony_ci if (isEccFF) { 8558c2ecf20Sopenharmony_ci if (ecc_data2[0] == 0 && 8568c2ecf20Sopenharmony_ci ecc_data2[1] == 0 && 8578c2ecf20Sopenharmony_ci ecc_data2[2] == 0) 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci pr_debug("UNCORRECTED_ERROR default\n"); 8618c2ecf20Sopenharmony_ci return -EBADMSG; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci/** 8668c2ecf20Sopenharmony_ci * omap_correct_data - Compares the ECC read with HW generated ECC 8678c2ecf20Sopenharmony_ci * @chip: NAND chip object 8688c2ecf20Sopenharmony_ci * @dat: page data 8698c2ecf20Sopenharmony_ci * @read_ecc: ecc read from nand flash 8708c2ecf20Sopenharmony_ci * @calc_ecc: ecc read from HW ECC registers 8718c2ecf20Sopenharmony_ci * 8728c2ecf20Sopenharmony_ci * Compares the ecc read from nand spare area with ECC registers values 8738c2ecf20Sopenharmony_ci * and if ECC's mismatched, it will call 'omap_compare_ecc' for error 8748c2ecf20Sopenharmony_ci * detection and correction. If there are no errors, %0 is returned. If 8758c2ecf20Sopenharmony_ci * there were errors and all of the errors were corrected, the number of 8768c2ecf20Sopenharmony_ci * corrected errors is returned. If uncorrectable errors exist, %-1 is 8778c2ecf20Sopenharmony_ci * returned. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_cistatic int omap_correct_data(struct nand_chip *chip, u_char *dat, 8808c2ecf20Sopenharmony_ci u_char *read_ecc, u_char *calc_ecc) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 8838c2ecf20Sopenharmony_ci int blockCnt = 0, i = 0, ret = 0; 8848c2ecf20Sopenharmony_ci int stat = 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* Ex NAND_ECC_HW12_2048 */ 8878c2ecf20Sopenharmony_ci if (info->nand.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST && 8888c2ecf20Sopenharmony_ci info->nand.ecc.size == 2048) 8898c2ecf20Sopenharmony_ci blockCnt = 4; 8908c2ecf20Sopenharmony_ci else 8918c2ecf20Sopenharmony_ci blockCnt = 1; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci for (i = 0; i < blockCnt; i++) { 8948c2ecf20Sopenharmony_ci if (memcmp(read_ecc, calc_ecc, 3) != 0) { 8958c2ecf20Sopenharmony_ci ret = omap_compare_ecc(read_ecc, calc_ecc, dat); 8968c2ecf20Sopenharmony_ci if (ret < 0) 8978c2ecf20Sopenharmony_ci return ret; 8988c2ecf20Sopenharmony_ci /* keep track of the number of corrected errors */ 8998c2ecf20Sopenharmony_ci stat += ret; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci read_ecc += 3; 9028c2ecf20Sopenharmony_ci calc_ecc += 3; 9038c2ecf20Sopenharmony_ci dat += 512; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci return stat; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci/** 9098c2ecf20Sopenharmony_ci * omap_calcuate_ecc - Generate non-inverted ECC bytes. 9108c2ecf20Sopenharmony_ci * @chip: NAND chip object 9118c2ecf20Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 9128c2ecf20Sopenharmony_ci * @ecc_code: The ecc_code buffer 9138c2ecf20Sopenharmony_ci * 9148c2ecf20Sopenharmony_ci * Using noninverted ECC can be considered ugly since writing a blank 9158c2ecf20Sopenharmony_ci * page ie. padding will clear the ECC bytes. This is no problem as long 9168c2ecf20Sopenharmony_ci * nobody is trying to write data on the seemingly unused page. Reading 9178c2ecf20Sopenharmony_ci * an erased page will produce an ECC mismatch between generated and read 9188c2ecf20Sopenharmony_ci * ECC bytes that has to be dealt with separately. 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_cistatic int omap_calculate_ecc(struct nand_chip *chip, const u_char *dat, 9218c2ecf20Sopenharmony_ci u_char *ecc_code) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 9248c2ecf20Sopenharmony_ci u32 val; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci val = readl(info->reg.gpmc_ecc_config); 9278c2ecf20Sopenharmony_ci if (((val >> ECC_CONFIG_CS_SHIFT) & CS_MASK) != info->gpmc_cs) 9288c2ecf20Sopenharmony_ci return -EINVAL; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci /* read ecc result */ 9318c2ecf20Sopenharmony_ci val = readl(info->reg.gpmc_ecc1_result); 9328c2ecf20Sopenharmony_ci *ecc_code++ = val; /* P128e, ..., P1e */ 9338c2ecf20Sopenharmony_ci *ecc_code++ = val >> 16; /* P128o, ..., P1o */ 9348c2ecf20Sopenharmony_ci /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ 9358c2ecf20Sopenharmony_ci *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci/** 9418c2ecf20Sopenharmony_ci * omap_enable_hwecc - This function enables the hardware ecc functionality 9428c2ecf20Sopenharmony_ci * @mtd: MTD device structure 9438c2ecf20Sopenharmony_ci * @mode: Read/Write mode 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_cistatic void omap_enable_hwecc(struct nand_chip *chip, int mode) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 9488c2ecf20Sopenharmony_ci unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; 9498c2ecf20Sopenharmony_ci u32 val; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* clear ecc and enable bits */ 9528c2ecf20Sopenharmony_ci val = ECCCLEAR | ECC1; 9538c2ecf20Sopenharmony_ci writel(val, info->reg.gpmc_ecc_control); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* program ecc and result sizes */ 9568c2ecf20Sopenharmony_ci val = ((((info->nand.ecc.size >> 1) - 1) << ECCSIZE1_SHIFT) | 9578c2ecf20Sopenharmony_ci ECC1RESULTSIZE); 9588c2ecf20Sopenharmony_ci writel(val, info->reg.gpmc_ecc_size_config); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci switch (mode) { 9618c2ecf20Sopenharmony_ci case NAND_ECC_READ: 9628c2ecf20Sopenharmony_ci case NAND_ECC_WRITE: 9638c2ecf20Sopenharmony_ci writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci case NAND_ECC_READSYN: 9668c2ecf20Sopenharmony_ci writel(ECCCLEAR, info->reg.gpmc_ecc_control); 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci default: 9698c2ecf20Sopenharmony_ci dev_info(&info->pdev->dev, 9708c2ecf20Sopenharmony_ci "error: unrecognized Mode[%d]!\n", mode); 9718c2ecf20Sopenharmony_ci break; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ 9758c2ecf20Sopenharmony_ci val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); 9768c2ecf20Sopenharmony_ci writel(val, info->reg.gpmc_ecc_config); 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci/** 9808c2ecf20Sopenharmony_ci * omap_wait - wait until the command is done 9818c2ecf20Sopenharmony_ci * @this: NAND Chip structure 9828c2ecf20Sopenharmony_ci * 9838c2ecf20Sopenharmony_ci * Wait function is called during Program and erase operations and 9848c2ecf20Sopenharmony_ci * the way it is called from MTD layer, we should wait till the NAND 9858c2ecf20Sopenharmony_ci * chip is ready after the programming/erase operation has completed. 9868c2ecf20Sopenharmony_ci * 9878c2ecf20Sopenharmony_ci * Erase can take up to 400ms and program up to 20ms according to 9888c2ecf20Sopenharmony_ci * general NAND and SmartMedia specs 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_cistatic int omap_wait(struct nand_chip *this) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(this)); 9938c2ecf20Sopenharmony_ci unsigned long timeo = jiffies; 9948c2ecf20Sopenharmony_ci int status; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci timeo += msecs_to_jiffies(400); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command); 9998c2ecf20Sopenharmony_ci while (time_before(jiffies, timeo)) { 10008c2ecf20Sopenharmony_ci status = readb(info->reg.gpmc_nand_data); 10018c2ecf20Sopenharmony_ci if (status & NAND_STATUS_READY) 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci cond_resched(); 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci status = readb(info->reg.gpmc_nand_data); 10078c2ecf20Sopenharmony_ci return status; 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci/** 10118c2ecf20Sopenharmony_ci * omap_dev_ready - checks the NAND Ready GPIO line 10128c2ecf20Sopenharmony_ci * @mtd: MTD device structure 10138c2ecf20Sopenharmony_ci * 10148c2ecf20Sopenharmony_ci * Returns true if ready and false if busy. 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_cistatic int omap_dev_ready(struct nand_chip *chip) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci return gpiod_get_value(info->ready_gpiod); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci/** 10248c2ecf20Sopenharmony_ci * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation 10258c2ecf20Sopenharmony_ci * @mtd: MTD device structure 10268c2ecf20Sopenharmony_ci * @mode: Read/Write mode 10278c2ecf20Sopenharmony_ci * 10288c2ecf20Sopenharmony_ci * When using BCH with SW correction (i.e. no ELM), sector size is set 10298c2ecf20Sopenharmony_ci * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode 10308c2ecf20Sopenharmony_ci * for both reading and writing with: 10318c2ecf20Sopenharmony_ci * eccsize0 = 0 (no additional protected byte in spare area) 10328c2ecf20Sopenharmony_ci * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) 10338c2ecf20Sopenharmony_ci */ 10348c2ecf20Sopenharmony_cistatic void __maybe_unused omap_enable_hwecc_bch(struct nand_chip *chip, 10358c2ecf20Sopenharmony_ci int mode) 10368c2ecf20Sopenharmony_ci{ 10378c2ecf20Sopenharmony_ci unsigned int bch_type; 10388c2ecf20Sopenharmony_ci unsigned int dev_width, nsectors; 10398c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 10408c2ecf20Sopenharmony_ci enum omap_ecc ecc_opt = info->ecc_opt; 10418c2ecf20Sopenharmony_ci u32 val, wr_mode; 10428c2ecf20Sopenharmony_ci unsigned int ecc_size1, ecc_size0; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci /* GPMC configurations for calculating ECC */ 10458c2ecf20Sopenharmony_ci switch (ecc_opt) { 10468c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 10478c2ecf20Sopenharmony_ci bch_type = 0; 10488c2ecf20Sopenharmony_ci nsectors = 1; 10498c2ecf20Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 10508c2ecf20Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 10518c2ecf20Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 10528c2ecf20Sopenharmony_ci break; 10538c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 10548c2ecf20Sopenharmony_ci bch_type = 0; 10558c2ecf20Sopenharmony_ci nsectors = chip->ecc.steps; 10568c2ecf20Sopenharmony_ci if (mode == NAND_ECC_READ) { 10578c2ecf20Sopenharmony_ci wr_mode = BCH_WRAPMODE_1; 10588c2ecf20Sopenharmony_ci ecc_size0 = BCH4R_ECC_SIZE0; 10598c2ecf20Sopenharmony_ci ecc_size1 = BCH4R_ECC_SIZE1; 10608c2ecf20Sopenharmony_ci } else { 10618c2ecf20Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 10628c2ecf20Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 10638c2ecf20Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci break; 10668c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 10678c2ecf20Sopenharmony_ci bch_type = 1; 10688c2ecf20Sopenharmony_ci nsectors = 1; 10698c2ecf20Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 10708c2ecf20Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 10718c2ecf20Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 10728c2ecf20Sopenharmony_ci break; 10738c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 10748c2ecf20Sopenharmony_ci bch_type = 1; 10758c2ecf20Sopenharmony_ci nsectors = chip->ecc.steps; 10768c2ecf20Sopenharmony_ci if (mode == NAND_ECC_READ) { 10778c2ecf20Sopenharmony_ci wr_mode = BCH_WRAPMODE_1; 10788c2ecf20Sopenharmony_ci ecc_size0 = BCH8R_ECC_SIZE0; 10798c2ecf20Sopenharmony_ci ecc_size1 = BCH8R_ECC_SIZE1; 10808c2ecf20Sopenharmony_ci } else { 10818c2ecf20Sopenharmony_ci wr_mode = BCH_WRAPMODE_6; 10828c2ecf20Sopenharmony_ci ecc_size0 = BCH_ECC_SIZE0; 10838c2ecf20Sopenharmony_ci ecc_size1 = BCH_ECC_SIZE1; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 10878c2ecf20Sopenharmony_ci bch_type = 0x2; 10888c2ecf20Sopenharmony_ci nsectors = chip->ecc.steps; 10898c2ecf20Sopenharmony_ci if (mode == NAND_ECC_READ) { 10908c2ecf20Sopenharmony_ci wr_mode = 0x01; 10918c2ecf20Sopenharmony_ci ecc_size0 = 52; /* ECC bits in nibbles per sector */ 10928c2ecf20Sopenharmony_ci ecc_size1 = 0; /* non-ECC bits in nibbles per sector */ 10938c2ecf20Sopenharmony_ci } else { 10948c2ecf20Sopenharmony_ci wr_mode = 0x01; 10958c2ecf20Sopenharmony_ci ecc_size0 = 0; /* extra bits in nibbles per sector */ 10968c2ecf20Sopenharmony_ci ecc_size1 = 52; /* OOB bits in nibbles per sector */ 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci break; 10998c2ecf20Sopenharmony_ci default: 11008c2ecf20Sopenharmony_ci return; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci writel(ECC1, info->reg.gpmc_ecc_control); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* Configure ecc size for BCH */ 11068c2ecf20Sopenharmony_ci val = (ecc_size1 << ECCSIZE1_SHIFT) | (ecc_size0 << ECCSIZE0_SHIFT); 11078c2ecf20Sopenharmony_ci writel(val, info->reg.gpmc_ecc_size_config); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* BCH configuration */ 11128c2ecf20Sopenharmony_ci val = ((1 << 16) | /* enable BCH */ 11138c2ecf20Sopenharmony_ci (bch_type << 12) | /* BCH4/BCH8/BCH16 */ 11148c2ecf20Sopenharmony_ci (wr_mode << 8) | /* wrap mode */ 11158c2ecf20Sopenharmony_ci (dev_width << 7) | /* bus width */ 11168c2ecf20Sopenharmony_ci (((nsectors-1) & 0x7) << 4) | /* number of sectors */ 11178c2ecf20Sopenharmony_ci (info->gpmc_cs << 1) | /* ECC CS */ 11188c2ecf20Sopenharmony_ci (0x1)); /* enable ECC */ 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci writel(val, info->reg.gpmc_ecc_config); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* Clear ecc and enable bits */ 11238c2ecf20Sopenharmony_ci writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); 11248c2ecf20Sopenharmony_ci} 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_cistatic u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; 11278c2ecf20Sopenharmony_cistatic u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, 11288c2ecf20Sopenharmony_ci 0x97, 0x79, 0xe5, 0x24, 0xb5}; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/** 11318c2ecf20Sopenharmony_ci * _omap_calculate_ecc_bch - Generate ECC bytes for one sector 11328c2ecf20Sopenharmony_ci * @mtd: MTD device structure 11338c2ecf20Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 11348c2ecf20Sopenharmony_ci * @ecc_code: The ecc_code buffer 11358c2ecf20Sopenharmony_ci * @i: The sector number (for a multi sector page) 11368c2ecf20Sopenharmony_ci * 11378c2ecf20Sopenharmony_ci * Support calculating of BCH4/8/16 ECC vectors for one sector 11388c2ecf20Sopenharmony_ci * within a page. Sector number is in @i. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_cistatic int _omap_calculate_ecc_bch(struct mtd_info *mtd, 11418c2ecf20Sopenharmony_ci const u_char *dat, u_char *ecc_calc, int i) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 11448c2ecf20Sopenharmony_ci int eccbytes = info->nand.ecc.bytes; 11458c2ecf20Sopenharmony_ci struct gpmc_nand_regs *gpmc_regs = &info->reg; 11468c2ecf20Sopenharmony_ci u8 *ecc_code; 11478c2ecf20Sopenharmony_ci unsigned long bch_val1, bch_val2, bch_val3, bch_val4; 11488c2ecf20Sopenharmony_ci u32 val; 11498c2ecf20Sopenharmony_ci int j; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci ecc_code = ecc_calc; 11528c2ecf20Sopenharmony_ci switch (info->ecc_opt) { 11538c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 11548c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 11558c2ecf20Sopenharmony_ci bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); 11568c2ecf20Sopenharmony_ci bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); 11578c2ecf20Sopenharmony_ci bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); 11588c2ecf20Sopenharmony_ci bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); 11598c2ecf20Sopenharmony_ci *ecc_code++ = (bch_val4 & 0xFF); 11608c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val3 >> 24) & 0xFF); 11618c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val3 >> 16) & 0xFF); 11628c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val3 >> 8) & 0xFF); 11638c2ecf20Sopenharmony_ci *ecc_code++ = (bch_val3 & 0xFF); 11648c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 24) & 0xFF); 11658c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 16) & 0xFF); 11668c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 8) & 0xFF); 11678c2ecf20Sopenharmony_ci *ecc_code++ = (bch_val2 & 0xFF); 11688c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 24) & 0xFF); 11698c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 16) & 0xFF); 11708c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 8) & 0xFF); 11718c2ecf20Sopenharmony_ci *ecc_code++ = (bch_val1 & 0xFF); 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 11748c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 11758c2ecf20Sopenharmony_ci bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); 11768c2ecf20Sopenharmony_ci bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); 11778c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 12) & 0xFF); 11788c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val2 >> 4) & 0xFF); 11798c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val2 & 0xF) << 4) | 11808c2ecf20Sopenharmony_ci ((bch_val1 >> 28) & 0xF); 11818c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 20) & 0xFF); 11828c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 12) & 0xFF); 11838c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 >> 4) & 0xFF); 11848c2ecf20Sopenharmony_ci *ecc_code++ = ((bch_val1 & 0xF) << 4); 11858c2ecf20Sopenharmony_ci break; 11868c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 11878c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result6[i]); 11888c2ecf20Sopenharmony_ci ecc_code[0] = ((val >> 8) & 0xFF); 11898c2ecf20Sopenharmony_ci ecc_code[1] = ((val >> 0) & 0xFF); 11908c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result5[i]); 11918c2ecf20Sopenharmony_ci ecc_code[2] = ((val >> 24) & 0xFF); 11928c2ecf20Sopenharmony_ci ecc_code[3] = ((val >> 16) & 0xFF); 11938c2ecf20Sopenharmony_ci ecc_code[4] = ((val >> 8) & 0xFF); 11948c2ecf20Sopenharmony_ci ecc_code[5] = ((val >> 0) & 0xFF); 11958c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result4[i]); 11968c2ecf20Sopenharmony_ci ecc_code[6] = ((val >> 24) & 0xFF); 11978c2ecf20Sopenharmony_ci ecc_code[7] = ((val >> 16) & 0xFF); 11988c2ecf20Sopenharmony_ci ecc_code[8] = ((val >> 8) & 0xFF); 11998c2ecf20Sopenharmony_ci ecc_code[9] = ((val >> 0) & 0xFF); 12008c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result3[i]); 12018c2ecf20Sopenharmony_ci ecc_code[10] = ((val >> 24) & 0xFF); 12028c2ecf20Sopenharmony_ci ecc_code[11] = ((val >> 16) & 0xFF); 12038c2ecf20Sopenharmony_ci ecc_code[12] = ((val >> 8) & 0xFF); 12048c2ecf20Sopenharmony_ci ecc_code[13] = ((val >> 0) & 0xFF); 12058c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result2[i]); 12068c2ecf20Sopenharmony_ci ecc_code[14] = ((val >> 24) & 0xFF); 12078c2ecf20Sopenharmony_ci ecc_code[15] = ((val >> 16) & 0xFF); 12088c2ecf20Sopenharmony_ci ecc_code[16] = ((val >> 8) & 0xFF); 12098c2ecf20Sopenharmony_ci ecc_code[17] = ((val >> 0) & 0xFF); 12108c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result1[i]); 12118c2ecf20Sopenharmony_ci ecc_code[18] = ((val >> 24) & 0xFF); 12128c2ecf20Sopenharmony_ci ecc_code[19] = ((val >> 16) & 0xFF); 12138c2ecf20Sopenharmony_ci ecc_code[20] = ((val >> 8) & 0xFF); 12148c2ecf20Sopenharmony_ci ecc_code[21] = ((val >> 0) & 0xFF); 12158c2ecf20Sopenharmony_ci val = readl(gpmc_regs->gpmc_bch_result0[i]); 12168c2ecf20Sopenharmony_ci ecc_code[22] = ((val >> 24) & 0xFF); 12178c2ecf20Sopenharmony_ci ecc_code[23] = ((val >> 16) & 0xFF); 12188c2ecf20Sopenharmony_ci ecc_code[24] = ((val >> 8) & 0xFF); 12198c2ecf20Sopenharmony_ci ecc_code[25] = ((val >> 0) & 0xFF); 12208c2ecf20Sopenharmony_ci break; 12218c2ecf20Sopenharmony_ci default: 12228c2ecf20Sopenharmony_ci return -EINVAL; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci /* ECC scheme specific syndrome customizations */ 12268c2ecf20Sopenharmony_ci switch (info->ecc_opt) { 12278c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 12288c2ecf20Sopenharmony_ci /* Add constant polynomial to remainder, so that 12298c2ecf20Sopenharmony_ci * ECC of blank pages results in 0x0 on reading back 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_ci for (j = 0; j < eccbytes; j++) 12328c2ecf20Sopenharmony_ci ecc_calc[j] ^= bch4_polynomial[j]; 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 12358c2ecf20Sopenharmony_ci /* Set 8th ECC byte as 0x0 for ROM compatibility */ 12368c2ecf20Sopenharmony_ci ecc_calc[eccbytes - 1] = 0x0; 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 12398c2ecf20Sopenharmony_ci /* Add constant polynomial to remainder, so that 12408c2ecf20Sopenharmony_ci * ECC of blank pages results in 0x0 on reading back 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci for (j = 0; j < eccbytes; j++) 12438c2ecf20Sopenharmony_ci ecc_calc[j] ^= bch8_polynomial[j]; 12448c2ecf20Sopenharmony_ci break; 12458c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 12468c2ecf20Sopenharmony_ci /* Set 14th ECC byte as 0x0 for ROM compatibility */ 12478c2ecf20Sopenharmony_ci ecc_calc[eccbytes - 1] = 0x0; 12488c2ecf20Sopenharmony_ci break; 12498c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 12508c2ecf20Sopenharmony_ci break; 12518c2ecf20Sopenharmony_ci default: 12528c2ecf20Sopenharmony_ci return -EINVAL; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci return 0; 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci/** 12598c2ecf20Sopenharmony_ci * omap_calculate_ecc_bch_sw - ECC generator for sector for SW based correction 12608c2ecf20Sopenharmony_ci * @chip: NAND chip object 12618c2ecf20Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 12628c2ecf20Sopenharmony_ci * @ecc_code: The ecc_code buffer 12638c2ecf20Sopenharmony_ci * 12648c2ecf20Sopenharmony_ci * Support calculating of BCH4/8/16 ECC vectors for one sector. This is used 12658c2ecf20Sopenharmony_ci * when SW based correction is required as ECC is required for one sector 12668c2ecf20Sopenharmony_ci * at a time. 12678c2ecf20Sopenharmony_ci */ 12688c2ecf20Sopenharmony_cistatic int omap_calculate_ecc_bch_sw(struct nand_chip *chip, 12698c2ecf20Sopenharmony_ci const u_char *dat, u_char *ecc_calc) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci return _omap_calculate_ecc_bch(nand_to_mtd(chip), dat, ecc_calc, 0); 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci/** 12758c2ecf20Sopenharmony_ci * omap_calculate_ecc_bch_multi - Generate ECC for multiple sectors 12768c2ecf20Sopenharmony_ci * @mtd: MTD device structure 12778c2ecf20Sopenharmony_ci * @dat: The pointer to data on which ecc is computed 12788c2ecf20Sopenharmony_ci * @ecc_code: The ecc_code buffer 12798c2ecf20Sopenharmony_ci * 12808c2ecf20Sopenharmony_ci * Support calculating of BCH4/8/16 ecc vectors for the entire page in one go. 12818c2ecf20Sopenharmony_ci */ 12828c2ecf20Sopenharmony_cistatic int omap_calculate_ecc_bch_multi(struct mtd_info *mtd, 12838c2ecf20Sopenharmony_ci const u_char *dat, u_char *ecc_calc) 12848c2ecf20Sopenharmony_ci{ 12858c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 12868c2ecf20Sopenharmony_ci int eccbytes = info->nand.ecc.bytes; 12878c2ecf20Sopenharmony_ci unsigned long nsectors; 12888c2ecf20Sopenharmony_ci int i, ret; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; 12918c2ecf20Sopenharmony_ci for (i = 0; i < nsectors; i++) { 12928c2ecf20Sopenharmony_ci ret = _omap_calculate_ecc_bch(mtd, dat, ecc_calc, i); 12938c2ecf20Sopenharmony_ci if (ret) 12948c2ecf20Sopenharmony_ci return ret; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci ecc_calc += eccbytes; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci return 0; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci/** 13038c2ecf20Sopenharmony_ci * erased_sector_bitflips - count bit flips 13048c2ecf20Sopenharmony_ci * @data: data sector buffer 13058c2ecf20Sopenharmony_ci * @oob: oob buffer 13068c2ecf20Sopenharmony_ci * @info: omap_nand_info 13078c2ecf20Sopenharmony_ci * 13088c2ecf20Sopenharmony_ci * Check the bit flips in erased page falls below correctable level. 13098c2ecf20Sopenharmony_ci * If falls below, report the page as erased with correctable bit 13108c2ecf20Sopenharmony_ci * flip, else report as uncorrectable page. 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_cistatic int erased_sector_bitflips(u_char *data, u_char *oob, 13138c2ecf20Sopenharmony_ci struct omap_nand_info *info) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci int flip_bits = 0, i; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci for (i = 0; i < info->nand.ecc.size; i++) { 13188c2ecf20Sopenharmony_ci flip_bits += hweight8(~data[i]); 13198c2ecf20Sopenharmony_ci if (flip_bits > info->nand.ecc.strength) 13208c2ecf20Sopenharmony_ci return 0; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci for (i = 0; i < info->nand.ecc.bytes - 1; i++) { 13248c2ecf20Sopenharmony_ci flip_bits += hweight8(~oob[i]); 13258c2ecf20Sopenharmony_ci if (flip_bits > info->nand.ecc.strength) 13268c2ecf20Sopenharmony_ci return 0; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* 13308c2ecf20Sopenharmony_ci * Bit flips falls in correctable level. 13318c2ecf20Sopenharmony_ci * Fill data area with 0xFF 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_ci if (flip_bits) { 13348c2ecf20Sopenharmony_ci memset(data, 0xFF, info->nand.ecc.size); 13358c2ecf20Sopenharmony_ci memset(oob, 0xFF, info->nand.ecc.bytes); 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci return flip_bits; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci/** 13428c2ecf20Sopenharmony_ci * omap_elm_correct_data - corrects page data area in case error reported 13438c2ecf20Sopenharmony_ci * @chip: NAND chip object 13448c2ecf20Sopenharmony_ci * @data: page data 13458c2ecf20Sopenharmony_ci * @read_ecc: ecc read from nand flash 13468c2ecf20Sopenharmony_ci * @calc_ecc: ecc read from HW ECC registers 13478c2ecf20Sopenharmony_ci * 13488c2ecf20Sopenharmony_ci * Calculated ecc vector reported as zero in case of non-error pages. 13498c2ecf20Sopenharmony_ci * In case of non-zero ecc vector, first filter out erased-pages, and 13508c2ecf20Sopenharmony_ci * then process data via ELM to detect bit-flips. 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_cistatic int omap_elm_correct_data(struct nand_chip *chip, u_char *data, 13538c2ecf20Sopenharmony_ci u_char *read_ecc, u_char *calc_ecc) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip)); 13568c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &info->nand.ecc; 13578c2ecf20Sopenharmony_ci int eccsteps = info->nand.ecc.steps; 13588c2ecf20Sopenharmony_ci int i , j, stat = 0; 13598c2ecf20Sopenharmony_ci int eccflag, actual_eccbytes; 13608c2ecf20Sopenharmony_ci struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; 13618c2ecf20Sopenharmony_ci u_char *ecc_vec = calc_ecc; 13628c2ecf20Sopenharmony_ci u_char *spare_ecc = read_ecc; 13638c2ecf20Sopenharmony_ci u_char *erased_ecc_vec; 13648c2ecf20Sopenharmony_ci u_char *buf; 13658c2ecf20Sopenharmony_ci int bitflip_count; 13668c2ecf20Sopenharmony_ci bool is_error_reported = false; 13678c2ecf20Sopenharmony_ci u32 bit_pos, byte_pos, error_max, pos; 13688c2ecf20Sopenharmony_ci int err; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci switch (info->ecc_opt) { 13718c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 13728c2ecf20Sopenharmony_ci /* omit 7th ECC byte reserved for ROM code compatibility */ 13738c2ecf20Sopenharmony_ci actual_eccbytes = ecc->bytes - 1; 13748c2ecf20Sopenharmony_ci erased_ecc_vec = bch4_vector; 13758c2ecf20Sopenharmony_ci break; 13768c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 13778c2ecf20Sopenharmony_ci /* omit 14th ECC byte reserved for ROM code compatibility */ 13788c2ecf20Sopenharmony_ci actual_eccbytes = ecc->bytes - 1; 13798c2ecf20Sopenharmony_ci erased_ecc_vec = bch8_vector; 13808c2ecf20Sopenharmony_ci break; 13818c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 13828c2ecf20Sopenharmony_ci actual_eccbytes = ecc->bytes; 13838c2ecf20Sopenharmony_ci erased_ecc_vec = bch16_vector; 13848c2ecf20Sopenharmony_ci break; 13858c2ecf20Sopenharmony_ci default: 13868c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "invalid driver configuration\n"); 13878c2ecf20Sopenharmony_ci return -EINVAL; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* Initialize elm error vector to zero */ 13918c2ecf20Sopenharmony_ci memset(err_vec, 0, sizeof(err_vec)); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci for (i = 0; i < eccsteps ; i++) { 13948c2ecf20Sopenharmony_ci eccflag = 0; /* initialize eccflag */ 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci /* 13978c2ecf20Sopenharmony_ci * Check any error reported, 13988c2ecf20Sopenharmony_ci * In case of error, non zero ecc reported. 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci for (j = 0; j < actual_eccbytes; j++) { 14018c2ecf20Sopenharmony_ci if (calc_ecc[j] != 0) { 14028c2ecf20Sopenharmony_ci eccflag = 1; /* non zero ecc, error present */ 14038c2ecf20Sopenharmony_ci break; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if (eccflag == 1) { 14088c2ecf20Sopenharmony_ci if (memcmp(calc_ecc, erased_ecc_vec, 14098c2ecf20Sopenharmony_ci actual_eccbytes) == 0) { 14108c2ecf20Sopenharmony_ci /* 14118c2ecf20Sopenharmony_ci * calc_ecc[] matches pattern for ECC(all 0xff) 14128c2ecf20Sopenharmony_ci * so this is definitely an erased-page 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_ci } else { 14158c2ecf20Sopenharmony_ci buf = &data[info->nand.ecc.size * i]; 14168c2ecf20Sopenharmony_ci /* 14178c2ecf20Sopenharmony_ci * count number of 0-bits in read_buf. 14188c2ecf20Sopenharmony_ci * This check can be removed once a similar 14198c2ecf20Sopenharmony_ci * check is introduced in generic NAND driver 14208c2ecf20Sopenharmony_ci */ 14218c2ecf20Sopenharmony_ci bitflip_count = erased_sector_bitflips( 14228c2ecf20Sopenharmony_ci buf, read_ecc, info); 14238c2ecf20Sopenharmony_ci if (bitflip_count) { 14248c2ecf20Sopenharmony_ci /* 14258c2ecf20Sopenharmony_ci * number of 0-bits within ECC limits 14268c2ecf20Sopenharmony_ci * So this may be an erased-page 14278c2ecf20Sopenharmony_ci */ 14288c2ecf20Sopenharmony_ci stat += bitflip_count; 14298c2ecf20Sopenharmony_ci } else { 14308c2ecf20Sopenharmony_ci /* 14318c2ecf20Sopenharmony_ci * Too many 0-bits. It may be a 14328c2ecf20Sopenharmony_ci * - programmed-page, OR 14338c2ecf20Sopenharmony_ci * - erased-page with many bit-flips 14348c2ecf20Sopenharmony_ci * So this page requires check by ELM 14358c2ecf20Sopenharmony_ci */ 14368c2ecf20Sopenharmony_ci err_vec[i].error_reported = true; 14378c2ecf20Sopenharmony_ci is_error_reported = true; 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* Update the ecc vector */ 14438c2ecf20Sopenharmony_ci calc_ecc += ecc->bytes; 14448c2ecf20Sopenharmony_ci read_ecc += ecc->bytes; 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci /* Check if any error reported */ 14488c2ecf20Sopenharmony_ci if (!is_error_reported) 14498c2ecf20Sopenharmony_ci return stat; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* Decode BCH error using ELM module */ 14528c2ecf20Sopenharmony_ci elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci err = 0; 14558c2ecf20Sopenharmony_ci for (i = 0; i < eccsteps; i++) { 14568c2ecf20Sopenharmony_ci if (err_vec[i].error_uncorrectable) { 14578c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, 14588c2ecf20Sopenharmony_ci "uncorrectable bit-flips found\n"); 14598c2ecf20Sopenharmony_ci err = -EBADMSG; 14608c2ecf20Sopenharmony_ci } else if (err_vec[i].error_reported) { 14618c2ecf20Sopenharmony_ci for (j = 0; j < err_vec[i].error_count; j++) { 14628c2ecf20Sopenharmony_ci switch (info->ecc_opt) { 14638c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 14648c2ecf20Sopenharmony_ci /* Add 4 bits to take care of padding */ 14658c2ecf20Sopenharmony_ci pos = err_vec[i].error_loc[j] + 14668c2ecf20Sopenharmony_ci BCH4_BIT_PAD; 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 14698c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 14708c2ecf20Sopenharmony_ci pos = err_vec[i].error_loc[j]; 14718c2ecf20Sopenharmony_ci break; 14728c2ecf20Sopenharmony_ci default: 14738c2ecf20Sopenharmony_ci return -EINVAL; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci error_max = (ecc->size + actual_eccbytes) * 8; 14768c2ecf20Sopenharmony_ci /* Calculate bit position of error */ 14778c2ecf20Sopenharmony_ci bit_pos = pos % 8; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci /* Calculate byte position of error */ 14808c2ecf20Sopenharmony_ci byte_pos = (error_max - pos - 1) / 8; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci if (pos < error_max) { 14838c2ecf20Sopenharmony_ci if (byte_pos < 512) { 14848c2ecf20Sopenharmony_ci pr_debug("bitflip@dat[%d]=%x\n", 14858c2ecf20Sopenharmony_ci byte_pos, data[byte_pos]); 14868c2ecf20Sopenharmony_ci data[byte_pos] ^= 1 << bit_pos; 14878c2ecf20Sopenharmony_ci } else { 14888c2ecf20Sopenharmony_ci pr_debug("bitflip@oob[%d]=%x\n", 14898c2ecf20Sopenharmony_ci (byte_pos - 512), 14908c2ecf20Sopenharmony_ci spare_ecc[byte_pos - 512]); 14918c2ecf20Sopenharmony_ci spare_ecc[byte_pos - 512] ^= 14928c2ecf20Sopenharmony_ci 1 << bit_pos; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci } else { 14958c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, 14968c2ecf20Sopenharmony_ci "invalid bit-flip @ %d:%d\n", 14978c2ecf20Sopenharmony_ci byte_pos, bit_pos); 14988c2ecf20Sopenharmony_ci err = -EBADMSG; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci } 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci /* Update number of correctable errors */ 15048c2ecf20Sopenharmony_ci stat = max_t(unsigned int, stat, err_vec[i].error_count); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci /* Update page data with sector size */ 15078c2ecf20Sopenharmony_ci data += ecc->size; 15088c2ecf20Sopenharmony_ci spare_ecc += ecc->bytes; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci return (err) ? err : stat; 15128c2ecf20Sopenharmony_ci} 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci/** 15158c2ecf20Sopenharmony_ci * omap_write_page_bch - BCH ecc based write page function for entire page 15168c2ecf20Sopenharmony_ci * @chip: nand chip info structure 15178c2ecf20Sopenharmony_ci * @buf: data buffer 15188c2ecf20Sopenharmony_ci * @oob_required: must write chip->oob_poi to OOB 15198c2ecf20Sopenharmony_ci * @page: page 15208c2ecf20Sopenharmony_ci * 15218c2ecf20Sopenharmony_ci * Custom write page method evolved to support multi sector writing in one shot 15228c2ecf20Sopenharmony_ci */ 15238c2ecf20Sopenharmony_cistatic int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf, 15248c2ecf20Sopenharmony_ci int oob_required, int page) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 15278c2ecf20Sopenharmony_ci int ret; 15288c2ecf20Sopenharmony_ci uint8_t *ecc_calc = chip->ecc.calc_buf; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci /* Enable GPMC ecc engine */ 15338c2ecf20Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_WRITE); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci /* Write data */ 15368c2ecf20Sopenharmony_ci chip->legacy.write_buf(chip, buf, mtd->writesize); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* Update ecc vector from GPMC result registers */ 15398c2ecf20Sopenharmony_ci omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, 15428c2ecf20Sopenharmony_ci chip->ecc.total); 15438c2ecf20Sopenharmony_ci if (ret) 15448c2ecf20Sopenharmony_ci return ret; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* Write ecc vector to OOB area */ 15478c2ecf20Sopenharmony_ci chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci/** 15538c2ecf20Sopenharmony_ci * omap_write_subpage_bch - BCH hardware ECC based subpage write 15548c2ecf20Sopenharmony_ci * @chip: nand chip info structure 15558c2ecf20Sopenharmony_ci * @offset: column address of subpage within the page 15568c2ecf20Sopenharmony_ci * @data_len: data length 15578c2ecf20Sopenharmony_ci * @buf: data buffer 15588c2ecf20Sopenharmony_ci * @oob_required: must write chip->oob_poi to OOB 15598c2ecf20Sopenharmony_ci * @page: page number to write 15608c2ecf20Sopenharmony_ci * 15618c2ecf20Sopenharmony_ci * OMAP optimized subpage write method. 15628c2ecf20Sopenharmony_ci */ 15638c2ecf20Sopenharmony_cistatic int omap_write_subpage_bch(struct nand_chip *chip, u32 offset, 15648c2ecf20Sopenharmony_ci u32 data_len, const u8 *buf, 15658c2ecf20Sopenharmony_ci int oob_required, int page) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 15688c2ecf20Sopenharmony_ci u8 *ecc_calc = chip->ecc.calc_buf; 15698c2ecf20Sopenharmony_ci int ecc_size = chip->ecc.size; 15708c2ecf20Sopenharmony_ci int ecc_bytes = chip->ecc.bytes; 15718c2ecf20Sopenharmony_ci int ecc_steps = chip->ecc.steps; 15728c2ecf20Sopenharmony_ci u32 start_step = offset / ecc_size; 15738c2ecf20Sopenharmony_ci u32 end_step = (offset + data_len - 1) / ecc_size; 15748c2ecf20Sopenharmony_ci int step, ret = 0; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* 15778c2ecf20Sopenharmony_ci * Write entire page at one go as it would be optimal 15788c2ecf20Sopenharmony_ci * as ECC is calculated by hardware. 15798c2ecf20Sopenharmony_ci * ECC is calculated for all subpages but we choose 15808c2ecf20Sopenharmony_ci * only what we want. 15818c2ecf20Sopenharmony_ci */ 15828c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* Enable GPMC ECC engine */ 15858c2ecf20Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_WRITE); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* Write data */ 15888c2ecf20Sopenharmony_ci chip->legacy.write_buf(chip, buf, mtd->writesize); 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci for (step = 0; step < ecc_steps; step++) { 15918c2ecf20Sopenharmony_ci /* mask ECC of un-touched subpages by padding 0xFF */ 15928c2ecf20Sopenharmony_ci if (step < start_step || step > end_step) 15938c2ecf20Sopenharmony_ci memset(ecc_calc, 0xff, ecc_bytes); 15948c2ecf20Sopenharmony_ci else 15958c2ecf20Sopenharmony_ci ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step); 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if (ret) 15988c2ecf20Sopenharmony_ci return ret; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci buf += ecc_size; 16018c2ecf20Sopenharmony_ci ecc_calc += ecc_bytes; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci /* copy calculated ECC for whole page to chip->buffer->oob */ 16058c2ecf20Sopenharmony_ci /* this include masked-value(0xFF) for unwritten subpages */ 16068c2ecf20Sopenharmony_ci ecc_calc = chip->ecc.calc_buf; 16078c2ecf20Sopenharmony_ci ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0, 16088c2ecf20Sopenharmony_ci chip->ecc.total); 16098c2ecf20Sopenharmony_ci if (ret) 16108c2ecf20Sopenharmony_ci return ret; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* write OOB buffer to NAND device */ 16138c2ecf20Sopenharmony_ci chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci/** 16198c2ecf20Sopenharmony_ci * omap_read_page_bch - BCH ecc based page read function for entire page 16208c2ecf20Sopenharmony_ci * @chip: nand chip info structure 16218c2ecf20Sopenharmony_ci * @buf: buffer to store read data 16228c2ecf20Sopenharmony_ci * @oob_required: caller requires OOB data read to chip->oob_poi 16238c2ecf20Sopenharmony_ci * @page: page number to read 16248c2ecf20Sopenharmony_ci * 16258c2ecf20Sopenharmony_ci * For BCH ecc scheme, GPMC used for syndrome calculation and ELM module 16268c2ecf20Sopenharmony_ci * used for error correction. 16278c2ecf20Sopenharmony_ci * Custom method evolved to support ELM error correction & multi sector 16288c2ecf20Sopenharmony_ci * reading. On reading page data area is read along with OOB data with 16298c2ecf20Sopenharmony_ci * ecc engine enabled. ecc vector updated after read of OOB data. 16308c2ecf20Sopenharmony_ci * For non error pages ecc vector reported as zero. 16318c2ecf20Sopenharmony_ci */ 16328c2ecf20Sopenharmony_cistatic int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf, 16338c2ecf20Sopenharmony_ci int oob_required, int page) 16348c2ecf20Sopenharmony_ci{ 16358c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 16368c2ecf20Sopenharmony_ci uint8_t *ecc_calc = chip->ecc.calc_buf; 16378c2ecf20Sopenharmony_ci uint8_t *ecc_code = chip->ecc.code_buf; 16388c2ecf20Sopenharmony_ci int stat, ret; 16398c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci /* Enable GPMC ecc engine */ 16448c2ecf20Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_READ); 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci /* Read data */ 16478c2ecf20Sopenharmony_ci chip->legacy.read_buf(chip, buf, mtd->writesize); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* Read oob bytes */ 16508c2ecf20Sopenharmony_ci nand_change_read_column_op(chip, 16518c2ecf20Sopenharmony_ci mtd->writesize + BADBLOCK_MARKER_LENGTH, 16528c2ecf20Sopenharmony_ci chip->oob_poi + BADBLOCK_MARKER_LENGTH, 16538c2ecf20Sopenharmony_ci chip->ecc.total, false); 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci /* Calculate ecc bytes */ 16568c2ecf20Sopenharmony_ci omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, 16598c2ecf20Sopenharmony_ci chip->ecc.total); 16608c2ecf20Sopenharmony_ci if (ret) 16618c2ecf20Sopenharmony_ci return ret; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci stat = chip->ecc.correct(chip, buf, ecc_code, ecc_calc); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci if (stat < 0) { 16668c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 16678c2ecf20Sopenharmony_ci } else { 16688c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += stat; 16698c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, stat); 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci return max_bitflips; 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci/** 16768c2ecf20Sopenharmony_ci * is_elm_present - checks for presence of ELM module by scanning DT nodes 16778c2ecf20Sopenharmony_ci * @omap_nand_info: NAND device structure containing platform data 16788c2ecf20Sopenharmony_ci */ 16798c2ecf20Sopenharmony_cistatic bool is_elm_present(struct omap_nand_info *info, 16808c2ecf20Sopenharmony_ci struct device_node *elm_node) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci struct platform_device *pdev; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci /* check whether elm-id is passed via DT */ 16858c2ecf20Sopenharmony_ci if (!elm_node) { 16868c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "ELM devicetree node not found\n"); 16878c2ecf20Sopenharmony_ci return false; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci pdev = of_find_device_by_node(elm_node); 16908c2ecf20Sopenharmony_ci /* check whether ELM device is registered */ 16918c2ecf20Sopenharmony_ci if (!pdev) { 16928c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "ELM device not found\n"); 16938c2ecf20Sopenharmony_ci return false; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci /* ELM module available, now configure it */ 16968c2ecf20Sopenharmony_ci info->elm_dev = &pdev->dev; 16978c2ecf20Sopenharmony_ci return true; 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic bool omap2_nand_ecc_check(struct omap_nand_info *info) 17018c2ecf20Sopenharmony_ci{ 17028c2ecf20Sopenharmony_ci bool ecc_needs_bch, ecc_needs_omap_bch, ecc_needs_elm; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci switch (info->ecc_opt) { 17058c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 17068c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 17078c2ecf20Sopenharmony_ci ecc_needs_omap_bch = false; 17088c2ecf20Sopenharmony_ci ecc_needs_bch = true; 17098c2ecf20Sopenharmony_ci ecc_needs_elm = false; 17108c2ecf20Sopenharmony_ci break; 17118c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 17128c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 17138c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 17148c2ecf20Sopenharmony_ci ecc_needs_omap_bch = true; 17158c2ecf20Sopenharmony_ci ecc_needs_bch = false; 17168c2ecf20Sopenharmony_ci ecc_needs_elm = true; 17178c2ecf20Sopenharmony_ci break; 17188c2ecf20Sopenharmony_ci default: 17198c2ecf20Sopenharmony_ci ecc_needs_omap_bch = false; 17208c2ecf20Sopenharmony_ci ecc_needs_bch = false; 17218c2ecf20Sopenharmony_ci ecc_needs_elm = false; 17228c2ecf20Sopenharmony_ci break; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (ecc_needs_bch && !IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_BCH)) { 17268c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, 17278c2ecf20Sopenharmony_ci "CONFIG_MTD_NAND_ECC_SW_BCH not enabled\n"); 17288c2ecf20Sopenharmony_ci return false; 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci if (ecc_needs_omap_bch && !IS_ENABLED(CONFIG_MTD_NAND_OMAP_BCH)) { 17318c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, 17328c2ecf20Sopenharmony_ci "CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); 17338c2ecf20Sopenharmony_ci return false; 17348c2ecf20Sopenharmony_ci } 17358c2ecf20Sopenharmony_ci if (ecc_needs_elm && !is_elm_present(info, info->elm_of_node)) { 17368c2ecf20Sopenharmony_ci dev_err(&info->pdev->dev, "ELM not available\n"); 17378c2ecf20Sopenharmony_ci return false; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci return true; 17418c2ecf20Sopenharmony_ci} 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_cistatic const char * const nand_xfer_types[] = { 17448c2ecf20Sopenharmony_ci [NAND_OMAP_PREFETCH_POLLED] = "prefetch-polled", 17458c2ecf20Sopenharmony_ci [NAND_OMAP_POLLED] = "polled", 17468c2ecf20Sopenharmony_ci [NAND_OMAP_PREFETCH_DMA] = "prefetch-dma", 17478c2ecf20Sopenharmony_ci [NAND_OMAP_PREFETCH_IRQ] = "prefetch-irq", 17488c2ecf20Sopenharmony_ci}; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic int omap_get_dt_info(struct device *dev, struct omap_nand_info *info) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci struct device_node *child = dev->of_node; 17538c2ecf20Sopenharmony_ci int i; 17548c2ecf20Sopenharmony_ci const char *s; 17558c2ecf20Sopenharmony_ci u32 cs; 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &cs) < 0) { 17588c2ecf20Sopenharmony_ci dev_err(dev, "reg not found in DT\n"); 17598c2ecf20Sopenharmony_ci return -EINVAL; 17608c2ecf20Sopenharmony_ci } 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci info->gpmc_cs = cs; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci /* detect availability of ELM module. Won't be present pre-OMAP4 */ 17658c2ecf20Sopenharmony_ci info->elm_of_node = of_parse_phandle(child, "ti,elm-id", 0); 17668c2ecf20Sopenharmony_ci if (!info->elm_of_node) { 17678c2ecf20Sopenharmony_ci info->elm_of_node = of_parse_phandle(child, "elm_id", 0); 17688c2ecf20Sopenharmony_ci if (!info->elm_of_node) 17698c2ecf20Sopenharmony_ci dev_dbg(dev, "ti,elm-id not in DT\n"); 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci /* select ecc-scheme for NAND */ 17738c2ecf20Sopenharmony_ci if (of_property_read_string(child, "ti,nand-ecc-opt", &s)) { 17748c2ecf20Sopenharmony_ci dev_err(dev, "ti,nand-ecc-opt not found\n"); 17758c2ecf20Sopenharmony_ci return -EINVAL; 17768c2ecf20Sopenharmony_ci } 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (!strcmp(s, "sw")) { 17798c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_HAM1_CODE_SW; 17808c2ecf20Sopenharmony_ci } else if (!strcmp(s, "ham1") || 17818c2ecf20Sopenharmony_ci !strcmp(s, "hw") || !strcmp(s, "hw-romcode")) { 17828c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_HAM1_CODE_HW; 17838c2ecf20Sopenharmony_ci } else if (!strcmp(s, "bch4")) { 17848c2ecf20Sopenharmony_ci if (info->elm_of_node) 17858c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH4_CODE_HW; 17868c2ecf20Sopenharmony_ci else 17878c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH4_CODE_HW_DETECTION_SW; 17888c2ecf20Sopenharmony_ci } else if (!strcmp(s, "bch8")) { 17898c2ecf20Sopenharmony_ci if (info->elm_of_node) 17908c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH8_CODE_HW; 17918c2ecf20Sopenharmony_ci else 17928c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW; 17938c2ecf20Sopenharmony_ci } else if (!strcmp(s, "bch16")) { 17948c2ecf20Sopenharmony_ci info->ecc_opt = OMAP_ECC_BCH16_CODE_HW; 17958c2ecf20Sopenharmony_ci } else { 17968c2ecf20Sopenharmony_ci dev_err(dev, "unrecognized value for ti,nand-ecc-opt\n"); 17978c2ecf20Sopenharmony_ci return -EINVAL; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci /* select data transfer mode */ 18018c2ecf20Sopenharmony_ci if (!of_property_read_string(child, "ti,nand-xfer-type", &s)) { 18028c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nand_xfer_types); i++) { 18038c2ecf20Sopenharmony_ci if (!strcasecmp(s, nand_xfer_types[i])) { 18048c2ecf20Sopenharmony_ci info->xfer_type = i; 18058c2ecf20Sopenharmony_ci return 0; 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci dev_err(dev, "unrecognized value for ti,nand-xfer-type\n"); 18108c2ecf20Sopenharmony_ci return -EINVAL; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci return 0; 18148c2ecf20Sopenharmony_ci} 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_cistatic int omap_ooblayout_ecc(struct mtd_info *mtd, int section, 18178c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 18188c2ecf20Sopenharmony_ci{ 18198c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 18208c2ecf20Sopenharmony_ci struct nand_chip *chip = &info->nand; 18218c2ecf20Sopenharmony_ci int off = BADBLOCK_MARKER_LENGTH; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && 18248c2ecf20Sopenharmony_ci !(chip->options & NAND_BUSWIDTH_16)) 18258c2ecf20Sopenharmony_ci off = 1; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (section) 18288c2ecf20Sopenharmony_ci return -ERANGE; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci oobregion->offset = off; 18318c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.total; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci return 0; 18348c2ecf20Sopenharmony_ci} 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_cistatic int omap_ooblayout_free(struct mtd_info *mtd, int section, 18378c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 18388c2ecf20Sopenharmony_ci{ 18398c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 18408c2ecf20Sopenharmony_ci struct nand_chip *chip = &info->nand; 18418c2ecf20Sopenharmony_ci int off = BADBLOCK_MARKER_LENGTH; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW && 18448c2ecf20Sopenharmony_ci !(chip->options & NAND_BUSWIDTH_16)) 18458c2ecf20Sopenharmony_ci off = 1; 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (section) 18488c2ecf20Sopenharmony_ci return -ERANGE; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci off += chip->ecc.total; 18518c2ecf20Sopenharmony_ci if (off >= mtd->oobsize) 18528c2ecf20Sopenharmony_ci return -ERANGE; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci oobregion->offset = off; 18558c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - off; 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci return 0; 18588c2ecf20Sopenharmony_ci} 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops omap_ooblayout_ops = { 18618c2ecf20Sopenharmony_ci .ecc = omap_ooblayout_ecc, 18628c2ecf20Sopenharmony_ci .free = omap_ooblayout_free, 18638c2ecf20Sopenharmony_ci}; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section, 18668c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 18678c2ecf20Sopenharmony_ci{ 18688c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 18698c2ecf20Sopenharmony_ci int off = BADBLOCK_MARKER_LENGTH; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci if (section >= chip->ecc.steps) 18728c2ecf20Sopenharmony_ci return -ERANGE; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci /* 18758c2ecf20Sopenharmony_ci * When SW correction is employed, one OMAP specific marker byte is 18768c2ecf20Sopenharmony_ci * reserved after each ECC step. 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_ci oobregion->offset = off + (section * (chip->ecc.bytes + 1)); 18798c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.bytes; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci return 0; 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cistatic int omap_sw_ooblayout_free(struct mtd_info *mtd, int section, 18858c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 18888c2ecf20Sopenharmony_ci int off = BADBLOCK_MARKER_LENGTH; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (section) 18918c2ecf20Sopenharmony_ci return -ERANGE; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* 18948c2ecf20Sopenharmony_ci * When SW correction is employed, one OMAP specific marker byte is 18958c2ecf20Sopenharmony_ci * reserved after each ECC step. 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ci off += ((chip->ecc.bytes + 1) * chip->ecc.steps); 18988c2ecf20Sopenharmony_ci if (off >= mtd->oobsize) 18998c2ecf20Sopenharmony_ci return -ERANGE; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci oobregion->offset = off; 19028c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - off; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci return 0; 19058c2ecf20Sopenharmony_ci} 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops omap_sw_ooblayout_ops = { 19088c2ecf20Sopenharmony_ci .ecc = omap_sw_ooblayout_ecc, 19098c2ecf20Sopenharmony_ci .free = omap_sw_ooblayout_free, 19108c2ecf20Sopenharmony_ci}; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic int omap_nand_attach_chip(struct nand_chip *chip) 19138c2ecf20Sopenharmony_ci{ 19148c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 19158c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 19168c2ecf20Sopenharmony_ci struct device *dev = &info->pdev->dev; 19178c2ecf20Sopenharmony_ci int min_oobbytes = BADBLOCK_MARKER_LENGTH; 19188c2ecf20Sopenharmony_ci int oobbytes_per_step; 19198c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 19208c2ecf20Sopenharmony_ci int err; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci if (chip->bbt_options & NAND_BBT_USE_FLASH) 19238c2ecf20Sopenharmony_ci chip->bbt_options |= NAND_BBT_NO_OOB; 19248c2ecf20Sopenharmony_ci else 19258c2ecf20Sopenharmony_ci chip->options |= NAND_SKIP_BBTSCAN; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci /* Re-populate low-level callbacks based on xfer modes */ 19288c2ecf20Sopenharmony_ci switch (info->xfer_type) { 19298c2ecf20Sopenharmony_ci case NAND_OMAP_PREFETCH_POLLED: 19308c2ecf20Sopenharmony_ci chip->legacy.read_buf = omap_read_buf_pref; 19318c2ecf20Sopenharmony_ci chip->legacy.write_buf = omap_write_buf_pref; 19328c2ecf20Sopenharmony_ci break; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci case NAND_OMAP_POLLED: 19358c2ecf20Sopenharmony_ci /* Use nand_base defaults for {read,write}_buf */ 19368c2ecf20Sopenharmony_ci break; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci case NAND_OMAP_PREFETCH_DMA: 19398c2ecf20Sopenharmony_ci dma_cap_zero(mask); 19408c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 19418c2ecf20Sopenharmony_ci info->dma = dma_request_chan(dev->parent, "rxtx"); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci if (IS_ERR(info->dma)) { 19448c2ecf20Sopenharmony_ci dev_err(dev, "DMA engine request failed\n"); 19458c2ecf20Sopenharmony_ci return PTR_ERR(info->dma); 19468c2ecf20Sopenharmony_ci } else { 19478c2ecf20Sopenharmony_ci struct dma_slave_config cfg; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 19508c2ecf20Sopenharmony_ci cfg.src_addr = info->phys_base; 19518c2ecf20Sopenharmony_ci cfg.dst_addr = info->phys_base; 19528c2ecf20Sopenharmony_ci cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 19538c2ecf20Sopenharmony_ci cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 19548c2ecf20Sopenharmony_ci cfg.src_maxburst = 16; 19558c2ecf20Sopenharmony_ci cfg.dst_maxburst = 16; 19568c2ecf20Sopenharmony_ci err = dmaengine_slave_config(info->dma, &cfg); 19578c2ecf20Sopenharmony_ci if (err) { 19588c2ecf20Sopenharmony_ci dev_err(dev, 19598c2ecf20Sopenharmony_ci "DMA engine slave config failed: %d\n", 19608c2ecf20Sopenharmony_ci err); 19618c2ecf20Sopenharmony_ci return err; 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci chip->legacy.read_buf = omap_read_buf_dma_pref; 19648c2ecf20Sopenharmony_ci chip->legacy.write_buf = omap_write_buf_dma_pref; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci break; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci case NAND_OMAP_PREFETCH_IRQ: 19698c2ecf20Sopenharmony_ci info->gpmc_irq_fifo = platform_get_irq(info->pdev, 0); 19708c2ecf20Sopenharmony_ci if (info->gpmc_irq_fifo <= 0) 19718c2ecf20Sopenharmony_ci return -ENODEV; 19728c2ecf20Sopenharmony_ci err = devm_request_irq(dev, info->gpmc_irq_fifo, 19738c2ecf20Sopenharmony_ci omap_nand_irq, IRQF_SHARED, 19748c2ecf20Sopenharmony_ci "gpmc-nand-fifo", info); 19758c2ecf20Sopenharmony_ci if (err) { 19768c2ecf20Sopenharmony_ci dev_err(dev, "Requesting IRQ %d, error %d\n", 19778c2ecf20Sopenharmony_ci info->gpmc_irq_fifo, err); 19788c2ecf20Sopenharmony_ci info->gpmc_irq_fifo = 0; 19798c2ecf20Sopenharmony_ci return err; 19808c2ecf20Sopenharmony_ci } 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci info->gpmc_irq_count = platform_get_irq(info->pdev, 1); 19838c2ecf20Sopenharmony_ci if (info->gpmc_irq_count <= 0) 19848c2ecf20Sopenharmony_ci return -ENODEV; 19858c2ecf20Sopenharmony_ci err = devm_request_irq(dev, info->gpmc_irq_count, 19868c2ecf20Sopenharmony_ci omap_nand_irq, IRQF_SHARED, 19878c2ecf20Sopenharmony_ci "gpmc-nand-count", info); 19888c2ecf20Sopenharmony_ci if (err) { 19898c2ecf20Sopenharmony_ci dev_err(dev, "Requesting IRQ %d, error %d\n", 19908c2ecf20Sopenharmony_ci info->gpmc_irq_count, err); 19918c2ecf20Sopenharmony_ci info->gpmc_irq_count = 0; 19928c2ecf20Sopenharmony_ci return err; 19938c2ecf20Sopenharmony_ci } 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci chip->legacy.read_buf = omap_read_buf_irq_pref; 19968c2ecf20Sopenharmony_ci chip->legacy.write_buf = omap_write_buf_irq_pref; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci break; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci default: 20018c2ecf20Sopenharmony_ci dev_err(dev, "xfer_type %d not supported!\n", info->xfer_type); 20028c2ecf20Sopenharmony_ci return -EINVAL; 20038c2ecf20Sopenharmony_ci } 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci if (!omap2_nand_ecc_check(info)) 20068c2ecf20Sopenharmony_ci return -EINVAL; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* 20098c2ecf20Sopenharmony_ci * Bail out earlier to let NAND_ECC_ENGINE_TYPE_SOFT code create its own 20108c2ecf20Sopenharmony_ci * ooblayout instead of using ours. 20118c2ecf20Sopenharmony_ci */ 20128c2ecf20Sopenharmony_ci if (info->ecc_opt == OMAP_ECC_HAM1_CODE_SW) { 20138c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 20148c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 20158c2ecf20Sopenharmony_ci return 0; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci /* Populate MTD interface based on ECC scheme */ 20198c2ecf20Sopenharmony_ci switch (info->ecc_opt) { 20208c2ecf20Sopenharmony_ci case OMAP_ECC_HAM1_CODE_HW: 20218c2ecf20Sopenharmony_ci dev_info(dev, "nand: using OMAP_ECC_HAM1_CODE_HW\n"); 20228c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 20238c2ecf20Sopenharmony_ci chip->ecc.bytes = 3; 20248c2ecf20Sopenharmony_ci chip->ecc.size = 512; 20258c2ecf20Sopenharmony_ci chip->ecc.strength = 1; 20268c2ecf20Sopenharmony_ci chip->ecc.calculate = omap_calculate_ecc; 20278c2ecf20Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc; 20288c2ecf20Sopenharmony_ci chip->ecc.correct = omap_correct_data; 20298c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 20308c2ecf20Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (!(chip->options & NAND_BUSWIDTH_16)) 20338c2ecf20Sopenharmony_ci min_oobbytes = 1; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci break; 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: 20388c2ecf20Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); 20398c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 20408c2ecf20Sopenharmony_ci chip->ecc.size = 512; 20418c2ecf20Sopenharmony_ci chip->ecc.bytes = 7; 20428c2ecf20Sopenharmony_ci chip->ecc.strength = 4; 20438c2ecf20Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 20448c2ecf20Sopenharmony_ci chip->ecc.correct = nand_bch_correct_data; 20458c2ecf20Sopenharmony_ci chip->ecc.calculate = omap_calculate_ecc_bch_sw; 20468c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); 20478c2ecf20Sopenharmony_ci /* Reserve one byte for the OMAP marker */ 20488c2ecf20Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes + 1; 20498c2ecf20Sopenharmony_ci /* Software BCH library is used for locating errors */ 20508c2ecf20Sopenharmony_ci chip->ecc.priv = nand_bch_init(mtd); 20518c2ecf20Sopenharmony_ci if (!chip->ecc.priv) { 20528c2ecf20Sopenharmony_ci dev_err(dev, "Unable to use BCH library\n"); 20538c2ecf20Sopenharmony_ci return -EINVAL; 20548c2ecf20Sopenharmony_ci } 20558c2ecf20Sopenharmony_ci break; 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci case OMAP_ECC_BCH4_CODE_HW: 20588c2ecf20Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); 20598c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 20608c2ecf20Sopenharmony_ci chip->ecc.size = 512; 20618c2ecf20Sopenharmony_ci /* 14th bit is kept reserved for ROM-code compatibility */ 20628c2ecf20Sopenharmony_ci chip->ecc.bytes = 7 + 1; 20638c2ecf20Sopenharmony_ci chip->ecc.strength = 4; 20648c2ecf20Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 20658c2ecf20Sopenharmony_ci chip->ecc.correct = omap_elm_correct_data; 20668c2ecf20Sopenharmony_ci chip->ecc.read_page = omap_read_page_bch; 20678c2ecf20Sopenharmony_ci chip->ecc.write_page = omap_write_page_bch; 20688c2ecf20Sopenharmony_ci chip->ecc.write_subpage = omap_write_subpage_bch; 20698c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 20708c2ecf20Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci err = elm_config(info->elm_dev, BCH4_ECC, 20738c2ecf20Sopenharmony_ci mtd->writesize / chip->ecc.size, 20748c2ecf20Sopenharmony_ci chip->ecc.size, chip->ecc.bytes); 20758c2ecf20Sopenharmony_ci if (err < 0) 20768c2ecf20Sopenharmony_ci return err; 20778c2ecf20Sopenharmony_ci break; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: 20808c2ecf20Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); 20818c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 20828c2ecf20Sopenharmony_ci chip->ecc.size = 512; 20838c2ecf20Sopenharmony_ci chip->ecc.bytes = 13; 20848c2ecf20Sopenharmony_ci chip->ecc.strength = 8; 20858c2ecf20Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 20868c2ecf20Sopenharmony_ci chip->ecc.correct = nand_bch_correct_data; 20878c2ecf20Sopenharmony_ci chip->ecc.calculate = omap_calculate_ecc_bch_sw; 20888c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_sw_ooblayout_ops); 20898c2ecf20Sopenharmony_ci /* Reserve one byte for the OMAP marker */ 20908c2ecf20Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes + 1; 20918c2ecf20Sopenharmony_ci /* Software BCH library is used for locating errors */ 20928c2ecf20Sopenharmony_ci chip->ecc.priv = nand_bch_init(mtd); 20938c2ecf20Sopenharmony_ci if (!chip->ecc.priv) { 20948c2ecf20Sopenharmony_ci dev_err(dev, "unable to use BCH library\n"); 20958c2ecf20Sopenharmony_ci return -EINVAL; 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci break; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci case OMAP_ECC_BCH8_CODE_HW: 21008c2ecf20Sopenharmony_ci pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); 21018c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 21028c2ecf20Sopenharmony_ci chip->ecc.size = 512; 21038c2ecf20Sopenharmony_ci /* 14th bit is kept reserved for ROM-code compatibility */ 21048c2ecf20Sopenharmony_ci chip->ecc.bytes = 13 + 1; 21058c2ecf20Sopenharmony_ci chip->ecc.strength = 8; 21068c2ecf20Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 21078c2ecf20Sopenharmony_ci chip->ecc.correct = omap_elm_correct_data; 21088c2ecf20Sopenharmony_ci chip->ecc.read_page = omap_read_page_bch; 21098c2ecf20Sopenharmony_ci chip->ecc.write_page = omap_write_page_bch; 21108c2ecf20Sopenharmony_ci chip->ecc.write_subpage = omap_write_subpage_bch; 21118c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 21128c2ecf20Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci err = elm_config(info->elm_dev, BCH8_ECC, 21158c2ecf20Sopenharmony_ci mtd->writesize / chip->ecc.size, 21168c2ecf20Sopenharmony_ci chip->ecc.size, chip->ecc.bytes); 21178c2ecf20Sopenharmony_ci if (err < 0) 21188c2ecf20Sopenharmony_ci return err; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci break; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci case OMAP_ECC_BCH16_CODE_HW: 21238c2ecf20Sopenharmony_ci pr_info("Using OMAP_ECC_BCH16_CODE_HW ECC scheme\n"); 21248c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 21258c2ecf20Sopenharmony_ci chip->ecc.size = 512; 21268c2ecf20Sopenharmony_ci chip->ecc.bytes = 26; 21278c2ecf20Sopenharmony_ci chip->ecc.strength = 16; 21288c2ecf20Sopenharmony_ci chip->ecc.hwctl = omap_enable_hwecc_bch; 21298c2ecf20Sopenharmony_ci chip->ecc.correct = omap_elm_correct_data; 21308c2ecf20Sopenharmony_ci chip->ecc.read_page = omap_read_page_bch; 21318c2ecf20Sopenharmony_ci chip->ecc.write_page = omap_write_page_bch; 21328c2ecf20Sopenharmony_ci chip->ecc.write_subpage = omap_write_subpage_bch; 21338c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &omap_ooblayout_ops); 21348c2ecf20Sopenharmony_ci oobbytes_per_step = chip->ecc.bytes; 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci err = elm_config(info->elm_dev, BCH16_ECC, 21378c2ecf20Sopenharmony_ci mtd->writesize / chip->ecc.size, 21388c2ecf20Sopenharmony_ci chip->ecc.size, chip->ecc.bytes); 21398c2ecf20Sopenharmony_ci if (err < 0) 21408c2ecf20Sopenharmony_ci return err; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci break; 21438c2ecf20Sopenharmony_ci default: 21448c2ecf20Sopenharmony_ci dev_err(dev, "Invalid or unsupported ECC scheme\n"); 21458c2ecf20Sopenharmony_ci return -EINVAL; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci /* Check if NAND device's OOB is enough to store ECC signatures */ 21498c2ecf20Sopenharmony_ci min_oobbytes += (oobbytes_per_step * 21508c2ecf20Sopenharmony_ci (mtd->writesize / chip->ecc.size)); 21518c2ecf20Sopenharmony_ci if (mtd->oobsize < min_oobbytes) { 21528c2ecf20Sopenharmony_ci dev_err(dev, 21538c2ecf20Sopenharmony_ci "Not enough OOB bytes: required = %d, available=%d\n", 21548c2ecf20Sopenharmony_ci min_oobbytes, mtd->oobsize); 21558c2ecf20Sopenharmony_ci return -EINVAL; 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci return 0; 21598c2ecf20Sopenharmony_ci} 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_cistatic const struct nand_controller_ops omap_nand_controller_ops = { 21628c2ecf20Sopenharmony_ci .attach_chip = omap_nand_attach_chip, 21638c2ecf20Sopenharmony_ci}; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci/* Shared among all NAND instances to synchronize access to the ECC Engine */ 21668c2ecf20Sopenharmony_cistatic struct nand_controller omap_gpmc_controller; 21678c2ecf20Sopenharmony_cistatic bool omap_gpmc_controller_initialized; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_cistatic int omap_nand_probe(struct platform_device *pdev) 21708c2ecf20Sopenharmony_ci{ 21718c2ecf20Sopenharmony_ci struct omap_nand_info *info; 21728c2ecf20Sopenharmony_ci struct mtd_info *mtd; 21738c2ecf20Sopenharmony_ci struct nand_chip *nand_chip; 21748c2ecf20Sopenharmony_ci int err; 21758c2ecf20Sopenharmony_ci struct resource *res; 21768c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), 21798c2ecf20Sopenharmony_ci GFP_KERNEL); 21808c2ecf20Sopenharmony_ci if (!info) 21818c2ecf20Sopenharmony_ci return -ENOMEM; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci info->pdev = pdev; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci err = omap_get_dt_info(dev, info); 21868c2ecf20Sopenharmony_ci if (err) 21878c2ecf20Sopenharmony_ci return err; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci info->ops = gpmc_omap_get_nand_ops(&info->reg, info->gpmc_cs); 21908c2ecf20Sopenharmony_ci if (!info->ops) { 21918c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get GPMC->NAND interface\n"); 21928c2ecf20Sopenharmony_ci return -ENODEV; 21938c2ecf20Sopenharmony_ci } 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci nand_chip = &info->nand; 21968c2ecf20Sopenharmony_ci mtd = nand_to_mtd(nand_chip); 21978c2ecf20Sopenharmony_ci mtd->dev.parent = &pdev->dev; 21988c2ecf20Sopenharmony_ci nand_chip->ecc.priv = NULL; 21998c2ecf20Sopenharmony_ci nand_set_flash_node(nand_chip, dev->of_node); 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci if (!mtd->name) { 22028c2ecf20Sopenharmony_ci mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 22038c2ecf20Sopenharmony_ci "omap2-nand.%d", info->gpmc_cs); 22048c2ecf20Sopenharmony_ci if (!mtd->name) { 22058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to set MTD name\n"); 22068c2ecf20Sopenharmony_ci return -ENOMEM; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 22118c2ecf20Sopenharmony_ci nand_chip->legacy.IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); 22128c2ecf20Sopenharmony_ci if (IS_ERR(nand_chip->legacy.IO_ADDR_R)) 22138c2ecf20Sopenharmony_ci return PTR_ERR(nand_chip->legacy.IO_ADDR_R); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci info->phys_base = res->start; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (!omap_gpmc_controller_initialized) { 22188c2ecf20Sopenharmony_ci omap_gpmc_controller.ops = &omap_nand_controller_ops; 22198c2ecf20Sopenharmony_ci nand_controller_init(&omap_gpmc_controller); 22208c2ecf20Sopenharmony_ci omap_gpmc_controller_initialized = true; 22218c2ecf20Sopenharmony_ci } 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci nand_chip->controller = &omap_gpmc_controller; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R; 22268c2ecf20Sopenharmony_ci nand_chip->legacy.cmd_ctrl = omap_hwcontrol; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb", 22298c2ecf20Sopenharmony_ci GPIOD_IN); 22308c2ecf20Sopenharmony_ci if (IS_ERR(info->ready_gpiod)) { 22318c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ready gpio\n"); 22328c2ecf20Sopenharmony_ci return PTR_ERR(info->ready_gpiod); 22338c2ecf20Sopenharmony_ci } 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci /* 22368c2ecf20Sopenharmony_ci * If RDY/BSY line is connected to OMAP then use the omap ready 22378c2ecf20Sopenharmony_ci * function and the generic nand_wait function which reads the status 22388c2ecf20Sopenharmony_ci * register after monitoring the RDY/BSY line. Otherwise use a standard 22398c2ecf20Sopenharmony_ci * chip delay which is slightly more than tR (AC Timing) of the NAND 22408c2ecf20Sopenharmony_ci * device and read status register until you get a failure or success 22418c2ecf20Sopenharmony_ci */ 22428c2ecf20Sopenharmony_ci if (info->ready_gpiod) { 22438c2ecf20Sopenharmony_ci nand_chip->legacy.dev_ready = omap_dev_ready; 22448c2ecf20Sopenharmony_ci nand_chip->legacy.chip_delay = 0; 22458c2ecf20Sopenharmony_ci } else { 22468c2ecf20Sopenharmony_ci nand_chip->legacy.waitfunc = omap_wait; 22478c2ecf20Sopenharmony_ci nand_chip->legacy.chip_delay = 50; 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci if (info->flash_bbt) 22518c2ecf20Sopenharmony_ci nand_chip->bbt_options |= NAND_BBT_USE_FLASH; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci /* scan NAND device connected to chip controller */ 22548c2ecf20Sopenharmony_ci nand_chip->options |= info->devsize & NAND_BUSWIDTH_16; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci err = nand_scan(nand_chip, 1); 22578c2ecf20Sopenharmony_ci if (err) 22588c2ecf20Sopenharmony_ci goto return_error; 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci err = mtd_device_register(mtd, NULL, 0); 22618c2ecf20Sopenharmony_ci if (err) 22628c2ecf20Sopenharmony_ci goto cleanup_nand; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mtd); 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci return 0; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_cicleanup_nand: 22698c2ecf20Sopenharmony_ci nand_cleanup(nand_chip); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_cireturn_error: 22728c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(info->dma)) 22738c2ecf20Sopenharmony_ci dma_release_channel(info->dma); 22748c2ecf20Sopenharmony_ci if (nand_chip->ecc.priv) { 22758c2ecf20Sopenharmony_ci nand_bch_free(nand_chip->ecc.priv); 22768c2ecf20Sopenharmony_ci nand_chip->ecc.priv = NULL; 22778c2ecf20Sopenharmony_ci } 22788c2ecf20Sopenharmony_ci return err; 22798c2ecf20Sopenharmony_ci} 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_cistatic int omap_nand_remove(struct platform_device *pdev) 22828c2ecf20Sopenharmony_ci{ 22838c2ecf20Sopenharmony_ci struct mtd_info *mtd = platform_get_drvdata(pdev); 22848c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 22858c2ecf20Sopenharmony_ci struct omap_nand_info *info = mtd_to_omap(mtd); 22868c2ecf20Sopenharmony_ci int ret; 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci if (nand_chip->ecc.priv) { 22898c2ecf20Sopenharmony_ci nand_bch_free(nand_chip->ecc.priv); 22908c2ecf20Sopenharmony_ci nand_chip->ecc.priv = NULL; 22918c2ecf20Sopenharmony_ci } 22928c2ecf20Sopenharmony_ci if (info->dma) 22938c2ecf20Sopenharmony_ci dma_release_channel(info->dma); 22948c2ecf20Sopenharmony_ci ret = mtd_device_unregister(mtd); 22958c2ecf20Sopenharmony_ci WARN_ON(ret); 22968c2ecf20Sopenharmony_ci nand_cleanup(nand_chip); 22978c2ecf20Sopenharmony_ci return ret; 22988c2ecf20Sopenharmony_ci} 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_cistatic const struct of_device_id omap_nand_ids[] = { 23018c2ecf20Sopenharmony_ci { .compatible = "ti,omap2-nand", }, 23028c2ecf20Sopenharmony_ci {}, 23038c2ecf20Sopenharmony_ci}; 23048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_nand_ids); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_cistatic struct platform_driver omap_nand_driver = { 23078c2ecf20Sopenharmony_ci .probe = omap_nand_probe, 23088c2ecf20Sopenharmony_ci .remove = omap_nand_remove, 23098c2ecf20Sopenharmony_ci .driver = { 23108c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 23118c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(omap_nand_ids), 23128c2ecf20Sopenharmony_ci }, 23138c2ecf20Sopenharmony_ci}; 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_cimodule_platform_driver(omap_nand_driver); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 23188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 23198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); 2320