18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. 48c2ecf20Sopenharmony_ci * Copyright 2008 Sascha Hauer, kernel@pengutronix.de 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/delay.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 128c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/irq.h> 218c2ecf20Sopenharmony_ci#include <linux/completion.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/of_device.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_data/mtd-mxc_nand.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DRIVER_NAME "mxc_nand" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Addresses for NFC registers */ 298c2ecf20Sopenharmony_ci#define NFC_V1_V2_BUF_SIZE (host->regs + 0x00) 308c2ecf20Sopenharmony_ci#define NFC_V1_V2_BUF_ADDR (host->regs + 0x04) 318c2ecf20Sopenharmony_ci#define NFC_V1_V2_FLASH_ADDR (host->regs + 0x06) 328c2ecf20Sopenharmony_ci#define NFC_V1_V2_FLASH_CMD (host->regs + 0x08) 338c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG (host->regs + 0x0a) 348c2ecf20Sopenharmony_ci#define NFC_V1_V2_ECC_STATUS_RESULT (host->regs + 0x0c) 358c2ecf20Sopenharmony_ci#define NFC_V1_V2_RSLTMAIN_AREA (host->regs + 0x0e) 368c2ecf20Sopenharmony_ci#define NFC_V21_RSLTSPARE_AREA (host->regs + 0x10) 378c2ecf20Sopenharmony_ci#define NFC_V1_V2_WRPROT (host->regs + 0x12) 388c2ecf20Sopenharmony_ci#define NFC_V1_UNLOCKSTART_BLKADDR (host->regs + 0x14) 398c2ecf20Sopenharmony_ci#define NFC_V1_UNLOCKEND_BLKADDR (host->regs + 0x16) 408c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR0 (host->regs + 0x20) 418c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR1 (host->regs + 0x24) 428c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR2 (host->regs + 0x28) 438c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKSTART_BLKADDR3 (host->regs + 0x2c) 448c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR0 (host->regs + 0x22) 458c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR1 (host->regs + 0x26) 468c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR2 (host->regs + 0x2a) 478c2ecf20Sopenharmony_ci#define NFC_V21_UNLOCKEND_BLKADDR3 (host->regs + 0x2e) 488c2ecf20Sopenharmony_ci#define NFC_V1_V2_NF_WRPRST (host->regs + 0x18) 498c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1 (host->regs + 0x1a) 508c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG2 (host->regs + 0x1c) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0) 538c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_SP_EN (1 << 2) 548c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3) 558c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4) 568c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_BIG (1 << 5) 578c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_RST (1 << 6) 588c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG1_CE (1 << 7) 598c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8) 608c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_PPB(x) (((x) & 0x3) << 9) 618c2ecf20Sopenharmony_ci#define NFC_V2_CONFIG1_FP_INT (1 << 11) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define NFC_V1_V2_CONFIG2_INT (1 << 15) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Operation modes for the NFC. Valid for v1, v2 and v3 678c2ecf20Sopenharmony_ci * type controllers. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#define NFC_CMD (1 << 0) 708c2ecf20Sopenharmony_ci#define NFC_ADDR (1 << 1) 718c2ecf20Sopenharmony_ci#define NFC_INPUT (1 << 2) 728c2ecf20Sopenharmony_ci#define NFC_OUTPUT (1 << 3) 738c2ecf20Sopenharmony_ci#define NFC_ID (1 << 4) 748c2ecf20Sopenharmony_ci#define NFC_STATUS (1 << 5) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define NFC_V3_FLASH_CMD (host->regs_axi + 0x00) 778c2ecf20Sopenharmony_ci#define NFC_V3_FLASH_ADDR0 (host->regs_axi + 0x04) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG1 (host->regs_axi + 0x34) 808c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG1_SP_EN (1 << 0) 818c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7 ) << 4) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define NFC_V3_ECC_STATUS_RESULT (host->regs_axi + 0x38) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define NFC_V3_LAUNCH (host->regs_axi + 0x40) 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT (host->regs_ip + 0x0) 888c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_LOCK_TIGHT (1 << 0) 898c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_LOCK (1 << 1) 908c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_UNLOCK (1 << 2) 918c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define NFC_V3_WRPROT_UNLOCK_BLK_ADD0 (host->regs_ip + 0x04) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2 (host->regs_ip + 0x24) 968c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PS_512 (0 << 0) 978c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PS_2048 (1 << 0) 988c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PS_4096 (2 << 0) 998c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2) 1008c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ECC_EN (1 << 3) 1018c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4) 1028c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_NUM_ADDR_PHASE0 (1 << 5) 1038c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6) 1048c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_PPB(x, shift) (((x) & 0x3) << shift) 1058c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_NUM_ADDR_PHASE1(x) (((x) & 0x3) << 12) 1068c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_INT_MSK (1 << 15) 1078c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24) 1088c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3 (host->regs_ip + 0x28) 1118c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0) 1128c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_FW8 (1 << 3) 1138c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8) 1148c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_NUM_OF_DEVICES(x) (((x) & 0x7) << 12) 1158c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_RBB_MODE (1 << 15) 1168c2ecf20Sopenharmony_ci#define NFC_V3_CONFIG3_NO_SDMA (1 << 20) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define NFC_V3_IPC (host->regs_ip + 0x2C) 1198c2ecf20Sopenharmony_ci#define NFC_V3_IPC_CREQ (1 << 0) 1208c2ecf20Sopenharmony_ci#define NFC_V3_IPC_INT (1 << 31) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define NFC_V3_DELAY_LINE (host->regs_ip + 0x34) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct mxc_nand_host; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistruct mxc_nand_devtype_data { 1278c2ecf20Sopenharmony_ci void (*preset)(struct mtd_info *); 1288c2ecf20Sopenharmony_ci int (*read_page)(struct nand_chip *chip, void *buf, void *oob, bool ecc, 1298c2ecf20Sopenharmony_ci int page); 1308c2ecf20Sopenharmony_ci void (*send_cmd)(struct mxc_nand_host *, uint16_t, int); 1318c2ecf20Sopenharmony_ci void (*send_addr)(struct mxc_nand_host *, uint16_t, int); 1328c2ecf20Sopenharmony_ci void (*send_page)(struct mtd_info *, unsigned int); 1338c2ecf20Sopenharmony_ci void (*send_read_id)(struct mxc_nand_host *); 1348c2ecf20Sopenharmony_ci uint16_t (*get_dev_status)(struct mxc_nand_host *); 1358c2ecf20Sopenharmony_ci int (*check_int)(struct mxc_nand_host *); 1368c2ecf20Sopenharmony_ci void (*irq_control)(struct mxc_nand_host *, int); 1378c2ecf20Sopenharmony_ci u32 (*get_ecc_status)(struct mxc_nand_host *); 1388c2ecf20Sopenharmony_ci const struct mtd_ooblayout_ops *ooblayout; 1398c2ecf20Sopenharmony_ci void (*select_chip)(struct nand_chip *chip, int cs); 1408c2ecf20Sopenharmony_ci int (*setup_interface)(struct nand_chip *chip, int csline, 1418c2ecf20Sopenharmony_ci const struct nand_interface_config *conf); 1428c2ecf20Sopenharmony_ci void (*enable_hwecc)(struct nand_chip *chip, bool enable); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * On i.MX21 the CONFIG2:INT bit cannot be read if interrupts are masked 1468c2ecf20Sopenharmony_ci * (CONFIG1:INT_MSK is set). To handle this the driver uses 1478c2ecf20Sopenharmony_ci * enable_irq/disable_irq_nosync instead of CONFIG1:INT_MSK 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci int irqpending_quirk; 1508c2ecf20Sopenharmony_ci int needs_ip; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci size_t regs_offset; 1538c2ecf20Sopenharmony_ci size_t spare0_offset; 1548c2ecf20Sopenharmony_ci size_t axi_offset; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci int spare_len; 1578c2ecf20Sopenharmony_ci int eccbytes; 1588c2ecf20Sopenharmony_ci int eccsize; 1598c2ecf20Sopenharmony_ci int ppb_shift; 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistruct mxc_nand_host { 1638c2ecf20Sopenharmony_ci struct nand_chip nand; 1648c2ecf20Sopenharmony_ci struct device *dev; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci void __iomem *spare0; 1678c2ecf20Sopenharmony_ci void __iomem *main_area0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci void __iomem *base; 1708c2ecf20Sopenharmony_ci void __iomem *regs; 1718c2ecf20Sopenharmony_ci void __iomem *regs_axi; 1728c2ecf20Sopenharmony_ci void __iomem *regs_ip; 1738c2ecf20Sopenharmony_ci int status_request; 1748c2ecf20Sopenharmony_ci struct clk *clk; 1758c2ecf20Sopenharmony_ci int clk_act; 1768c2ecf20Sopenharmony_ci int irq; 1778c2ecf20Sopenharmony_ci int eccsize; 1788c2ecf20Sopenharmony_ci int used_oobsize; 1798c2ecf20Sopenharmony_ci int active_cs; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci struct completion op_completion; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci uint8_t *data_buf; 1848c2ecf20Sopenharmony_ci unsigned int buf_start; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci const struct mxc_nand_devtype_data *devtype_data; 1878c2ecf20Sopenharmony_ci struct mxc_nand_platform_data pdata; 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic const char * const part_probes[] = { 1918c2ecf20Sopenharmony_ci "cmdlinepart", "RedBoot", "ofpart", NULL }; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int i; 1968c2ecf20Sopenharmony_ci u32 *t = trg; 1978c2ecf20Sopenharmony_ci const __iomem u32 *s = src; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci for (i = 0; i < (size >> 2); i++) 2008c2ecf20Sopenharmony_ci *t++ = __raw_readl(s++); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void memcpy16_fromio(void *trg, const void __iomem *src, size_t size) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int i; 2068c2ecf20Sopenharmony_ci u16 *t = trg; 2078c2ecf20Sopenharmony_ci const __iomem u16 *s = src; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* We assume that src (IO) is always 32bit aligned */ 2108c2ecf20Sopenharmony_ci if (PTR_ALIGN(trg, 4) == trg && IS_ALIGNED(size, 4)) { 2118c2ecf20Sopenharmony_ci memcpy32_fromio(trg, src, size); 2128c2ecf20Sopenharmony_ci return; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < (size >> 1); i++) 2168c2ecf20Sopenharmony_ci *t++ = __raw_readw(s++); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic inline void memcpy32_toio(void __iomem *trg, const void *src, int size) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci /* __iowrite32_copy use 32bit size values so divide by 4 */ 2228c2ecf20Sopenharmony_ci __iowrite32_copy(trg, src, size / 4); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void memcpy16_toio(void __iomem *trg, const void *src, int size) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int i; 2288c2ecf20Sopenharmony_ci __iomem u16 *t = trg; 2298c2ecf20Sopenharmony_ci const u16 *s = src; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* We assume that trg (IO) is always 32bit aligned */ 2328c2ecf20Sopenharmony_ci if (PTR_ALIGN(src, 4) == src && IS_ALIGNED(size, 4)) { 2338c2ecf20Sopenharmony_ci memcpy32_toio(trg, src, size); 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (i = 0; i < (size >> 1); i++) 2388c2ecf20Sopenharmony_ci __raw_writew(*s++, t++); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * The controller splits a page into data chunks of 512 bytes + partial oob. 2438c2ecf20Sopenharmony_ci * There are writesize / 512 such chunks, the size of the partial oob parts is 2448c2ecf20Sopenharmony_ci * oobsize / #chunks rounded down to a multiple of 2. The last oob chunk then 2458c2ecf20Sopenharmony_ci * contains additionally the byte lost by rounding (if any). 2468c2ecf20Sopenharmony_ci * This function handles the needed shuffling between host->data_buf (which 2478c2ecf20Sopenharmony_ci * holds a page in natural order, i.e. writesize bytes data + oobsize bytes 2488c2ecf20Sopenharmony_ci * spare) and the NFC buffer. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_cistatic void copy_spare(struct mtd_info *mtd, bool bfrom, void *buf) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 2538c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(this); 2548c2ecf20Sopenharmony_ci u16 i, oob_chunk_size; 2558c2ecf20Sopenharmony_ci u16 num_chunks = mtd->writesize / 512; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci u8 *d = buf; 2588c2ecf20Sopenharmony_ci u8 __iomem *s = host->spare0; 2598c2ecf20Sopenharmony_ci u16 sparebuf_size = host->devtype_data->spare_len; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* size of oob chunk for all but possibly the last one */ 2628c2ecf20Sopenharmony_ci oob_chunk_size = (host->used_oobsize / num_chunks) & ~1; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (bfrom) { 2658c2ecf20Sopenharmony_ci for (i = 0; i < num_chunks - 1; i++) 2668c2ecf20Sopenharmony_ci memcpy16_fromio(d + i * oob_chunk_size, 2678c2ecf20Sopenharmony_ci s + i * sparebuf_size, 2688c2ecf20Sopenharmony_ci oob_chunk_size); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* the last chunk */ 2718c2ecf20Sopenharmony_ci memcpy16_fromio(d + i * oob_chunk_size, 2728c2ecf20Sopenharmony_ci s + i * sparebuf_size, 2738c2ecf20Sopenharmony_ci host->used_oobsize - i * oob_chunk_size); 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci for (i = 0; i < num_chunks - 1; i++) 2768c2ecf20Sopenharmony_ci memcpy16_toio(&s[i * sparebuf_size], 2778c2ecf20Sopenharmony_ci &d[i * oob_chunk_size], 2788c2ecf20Sopenharmony_ci oob_chunk_size); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* the last chunk */ 2818c2ecf20Sopenharmony_ci memcpy16_toio(&s[i * sparebuf_size], 2828c2ecf20Sopenharmony_ci &d[i * oob_chunk_size], 2838c2ecf20Sopenharmony_ci host->used_oobsize - i * oob_chunk_size); 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * MXC NANDFC can only perform full page+spare or spare-only read/write. When 2898c2ecf20Sopenharmony_ci * the upper layers perform a read/write buf operation, the saved column address 2908c2ecf20Sopenharmony_ci * is used to index into the full page. So usually this function is called with 2918c2ecf20Sopenharmony_ci * column == 0 (unless no column cycle is needed indicated by column == -1) 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 2968c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* Write out column address, if necessary */ 2998c2ecf20Sopenharmony_ci if (column != -1) { 3008c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, column & 0xff, 3018c2ecf20Sopenharmony_ci page_addr == -1); 3028c2ecf20Sopenharmony_ci if (mtd->writesize > 512) 3038c2ecf20Sopenharmony_ci /* another col addr cycle for 2k page */ 3048c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3058c2ecf20Sopenharmony_ci (column >> 8) & 0xff, 3068c2ecf20Sopenharmony_ci false); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* Write out page address, if necessary */ 3108c2ecf20Sopenharmony_ci if (page_addr != -1) { 3118c2ecf20Sopenharmony_ci /* paddr_0 - p_addr_7 */ 3128c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, (page_addr & 0xff), false); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (mtd->writesize > 512) { 3158c2ecf20Sopenharmony_ci if (mtd->size >= 0x10000000) { 3168c2ecf20Sopenharmony_ci /* paddr_8 - paddr_15 */ 3178c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3188c2ecf20Sopenharmony_ci (page_addr >> 8) & 0xff, 3198c2ecf20Sopenharmony_ci false); 3208c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3218c2ecf20Sopenharmony_ci (page_addr >> 16) & 0xff, 3228c2ecf20Sopenharmony_ci true); 3238c2ecf20Sopenharmony_ci } else 3248c2ecf20Sopenharmony_ci /* paddr_8 - paddr_15 */ 3258c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3268c2ecf20Sopenharmony_ci (page_addr >> 8) & 0xff, true); 3278c2ecf20Sopenharmony_ci } else { 3288c2ecf20Sopenharmony_ci if (nand_chip->options & NAND_ROW_ADDR_3) { 3298c2ecf20Sopenharmony_ci /* paddr_8 - paddr_15 */ 3308c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3318c2ecf20Sopenharmony_ci (page_addr >> 8) & 0xff, 3328c2ecf20Sopenharmony_ci false); 3338c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3348c2ecf20Sopenharmony_ci (page_addr >> 16) & 0xff, 3358c2ecf20Sopenharmony_ci true); 3368c2ecf20Sopenharmony_ci } else 3378c2ecf20Sopenharmony_ci /* paddr_8 - paddr_15 */ 3388c2ecf20Sopenharmony_ci host->devtype_data->send_addr(host, 3398c2ecf20Sopenharmony_ci (page_addr >> 8) & 0xff, true); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int check_int_v3(struct mxc_nand_host *host) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci uint32_t tmp; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci tmp = readl(NFC_V3_IPC); 3498c2ecf20Sopenharmony_ci if (!(tmp & NFC_V3_IPC_INT)) 3508c2ecf20Sopenharmony_ci return 0; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci tmp &= ~NFC_V3_IPC_INT; 3538c2ecf20Sopenharmony_ci writel(tmp, NFC_V3_IPC); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci return 1; 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int check_int_v1_v2(struct mxc_nand_host *host) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci uint32_t tmp; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci tmp = readw(NFC_V1_V2_CONFIG2); 3638c2ecf20Sopenharmony_ci if (!(tmp & NFC_V1_V2_CONFIG2_INT)) 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!host->devtype_data->irqpending_quirk) 3678c2ecf20Sopenharmony_ci writew(tmp & ~NFC_V1_V2_CONFIG2_INT, NFC_V1_V2_CONFIG2); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 1; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void irq_control_v1_v2(struct mxc_nand_host *host, int activate) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci uint16_t tmp; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci tmp = readw(NFC_V1_V2_CONFIG1); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (activate) 3798c2ecf20Sopenharmony_ci tmp &= ~NFC_V1_V2_CONFIG1_INT_MSK; 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci tmp |= NFC_V1_V2_CONFIG1_INT_MSK; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci writew(tmp, NFC_V1_V2_CONFIG1); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void irq_control_v3(struct mxc_nand_host *host, int activate) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci uint32_t tmp; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci tmp = readl(NFC_V3_CONFIG2); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (activate) 3938c2ecf20Sopenharmony_ci tmp &= ~NFC_V3_CONFIG2_INT_MSK; 3948c2ecf20Sopenharmony_ci else 3958c2ecf20Sopenharmony_ci tmp |= NFC_V3_CONFIG2_INT_MSK; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci writel(tmp, NFC_V3_CONFIG2); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic void irq_control(struct mxc_nand_host *host, int activate) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci if (host->devtype_data->irqpending_quirk) { 4038c2ecf20Sopenharmony_ci if (activate) 4048c2ecf20Sopenharmony_ci enable_irq(host->irq); 4058c2ecf20Sopenharmony_ci else 4068c2ecf20Sopenharmony_ci disable_irq_nosync(host->irq); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci host->devtype_data->irq_control(host, activate); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic u32 get_ecc_status_v1(struct mxc_nand_host *host) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci return readw(NFC_V1_V2_ECC_STATUS_RESULT); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic u32 get_ecc_status_v2(struct mxc_nand_host *host) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci return readl(NFC_V1_V2_ECC_STATUS_RESULT); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic u32 get_ecc_status_v3(struct mxc_nand_host *host) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci return readl(NFC_V3_ECC_STATUS_RESULT); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic irqreturn_t mxc_nfc_irq(int irq, void *dev_id) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct mxc_nand_host *host = dev_id; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (!host->devtype_data->check_int(host)) 4328c2ecf20Sopenharmony_ci return IRQ_NONE; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci irq_control(host, 0); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci complete(&host->op_completion); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* This function polls the NANDFC to wait for the basic operation to 4428c2ecf20Sopenharmony_ci * complete by checking the INT bit of config2 register. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_cistatic int wait_op_done(struct mxc_nand_host *host, int useirq) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int ret = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * If operation is already complete, don't bother to setup an irq or a 4508c2ecf20Sopenharmony_ci * loop. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci if (host->devtype_data->check_int(host)) 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (useirq) { 4568c2ecf20Sopenharmony_ci unsigned long timeout; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci reinit_completion(&host->op_completion); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci irq_control(host, 1); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci timeout = wait_for_completion_timeout(&host->op_completion, HZ); 4638c2ecf20Sopenharmony_ci if (!timeout && !host->devtype_data->check_int(host)) { 4648c2ecf20Sopenharmony_ci dev_dbg(host->dev, "timeout waiting for irq\n"); 4658c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } else { 4688c2ecf20Sopenharmony_ci int max_retries = 8000; 4698c2ecf20Sopenharmony_ci int done; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci do { 4728c2ecf20Sopenharmony_ci udelay(1); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci done = host->devtype_data->check_int(host); 4758c2ecf20Sopenharmony_ci if (done) 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci } while (--max_retries); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!done) { 4818c2ecf20Sopenharmony_ci dev_dbg(host->dev, "timeout polling for completion\n"); 4828c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci WARN_ONCE(ret < 0, "timeout! useirq=%d\n", useirq); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic void send_cmd_v3(struct mxc_nand_host *host, uint16_t cmd, int useirq) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci /* fill command */ 4948c2ecf20Sopenharmony_ci writel(cmd, NFC_V3_FLASH_CMD); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* send out command */ 4978c2ecf20Sopenharmony_ci writel(NFC_CMD, NFC_V3_LAUNCH); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 5008c2ecf20Sopenharmony_ci wait_op_done(host, useirq); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* This function issues the specified command to the NAND device and 5048c2ecf20Sopenharmony_ci * waits for completion. */ 5058c2ecf20Sopenharmony_cistatic void send_cmd_v1_v2(struct mxc_nand_host *host, uint16_t cmd, int useirq) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci dev_dbg(host->dev, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci writew(cmd, NFC_V1_V2_FLASH_CMD); 5108c2ecf20Sopenharmony_ci writew(NFC_CMD, NFC_V1_V2_CONFIG2); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (host->devtype_data->irqpending_quirk && (cmd == NAND_CMD_RESET)) { 5138c2ecf20Sopenharmony_ci int max_retries = 100; 5148c2ecf20Sopenharmony_ci /* Reset completion is indicated by NFC_CONFIG2 */ 5158c2ecf20Sopenharmony_ci /* being set to 0 */ 5168c2ecf20Sopenharmony_ci while (max_retries-- > 0) { 5178c2ecf20Sopenharmony_ci if (readw(NFC_V1_V2_CONFIG2) == 0) { 5188c2ecf20Sopenharmony_ci break; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci udelay(1); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci if (max_retries < 0) 5238c2ecf20Sopenharmony_ci dev_dbg(host->dev, "%s: RESET failed\n", __func__); 5248c2ecf20Sopenharmony_ci } else { 5258c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 5268c2ecf20Sopenharmony_ci wait_op_done(host, useirq); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic void send_addr_v3(struct mxc_nand_host *host, uint16_t addr, int islast) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci /* fill address */ 5338c2ecf20Sopenharmony_ci writel(addr, NFC_V3_FLASH_ADDR0); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* send out address */ 5368c2ecf20Sopenharmony_ci writel(NFC_ADDR, NFC_V3_LAUNCH); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci wait_op_done(host, 0); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/* This function sends an address (or partial address) to the 5428c2ecf20Sopenharmony_ci * NAND device. The address is used to select the source/destination for 5438c2ecf20Sopenharmony_ci * a NAND command. */ 5448c2ecf20Sopenharmony_cistatic void send_addr_v1_v2(struct mxc_nand_host *host, uint16_t addr, int islast) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci dev_dbg(host->dev, "send_addr(host, 0x%x %d)\n", addr, islast); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci writew(addr, NFC_V1_V2_FLASH_ADDR); 5498c2ecf20Sopenharmony_ci writew(NFC_ADDR, NFC_V1_V2_CONFIG2); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 5528c2ecf20Sopenharmony_ci wait_op_done(host, islast); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic void send_page_v3(struct mtd_info *mtd, unsigned int ops) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 5588c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 5598c2ecf20Sopenharmony_ci uint32_t tmp; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci tmp = readl(NFC_V3_CONFIG1); 5628c2ecf20Sopenharmony_ci tmp &= ~(7 << 4); 5638c2ecf20Sopenharmony_ci writel(tmp, NFC_V3_CONFIG1); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* transfer data from NFC ram to nand */ 5668c2ecf20Sopenharmony_ci writel(ops, NFC_V3_LAUNCH); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci wait_op_done(host, false); 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void send_page_v2(struct mtd_info *mtd, unsigned int ops) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 5748c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* NANDFC buffer 0 is used for page read/write */ 5778c2ecf20Sopenharmony_ci writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci writew(ops, NFC_V1_V2_CONFIG2); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 5828c2ecf20Sopenharmony_ci wait_op_done(host, true); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic void send_page_v1(struct mtd_info *mtd, unsigned int ops) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 5888c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 5898c2ecf20Sopenharmony_ci int bufs, i; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (mtd->writesize > 512) 5928c2ecf20Sopenharmony_ci bufs = 4; 5938c2ecf20Sopenharmony_ci else 5948c2ecf20Sopenharmony_ci bufs = 1; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci for (i = 0; i < bufs; i++) { 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* NANDFC buffer 0 is used for page read/write */ 5998c2ecf20Sopenharmony_ci writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci writew(ops, NFC_V1_V2_CONFIG2); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 6048c2ecf20Sopenharmony_ci wait_op_done(host, true); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void send_read_id_v3(struct mxc_nand_host *host) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci /* Read ID into main buffer */ 6118c2ecf20Sopenharmony_ci writel(NFC_ID, NFC_V3_LAUNCH); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci wait_op_done(host, true); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci memcpy32_fromio(host->data_buf, host->main_area0, 16); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* Request the NANDFC to perform a read of the NAND device ID. */ 6198c2ecf20Sopenharmony_cistatic void send_read_id_v1_v2(struct mxc_nand_host *host) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci /* NANDFC buffer 0 is used for device ID output */ 6228c2ecf20Sopenharmony_ci writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci writew(NFC_ID, NFC_V1_V2_CONFIG2); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 6278c2ecf20Sopenharmony_ci wait_op_done(host, true); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci memcpy32_fromio(host->data_buf, host->main_area0, 16); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic uint16_t get_dev_status_v3(struct mxc_nand_host *host) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci writew(NFC_STATUS, NFC_V3_LAUNCH); 6358c2ecf20Sopenharmony_ci wait_op_done(host, true); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return readl(NFC_V3_CONFIG1) >> 16; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci/* This function requests the NANDFC to perform a read of the 6418c2ecf20Sopenharmony_ci * NAND device status and returns the current status. */ 6428c2ecf20Sopenharmony_cistatic uint16_t get_dev_status_v1_v2(struct mxc_nand_host *host) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci void __iomem *main_buf = host->main_area0; 6458c2ecf20Sopenharmony_ci uint32_t store; 6468c2ecf20Sopenharmony_ci uint16_t ret; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* 6518c2ecf20Sopenharmony_ci * The device status is stored in main_area0. To 6528c2ecf20Sopenharmony_ci * prevent corruption of the buffer save the value 6538c2ecf20Sopenharmony_ci * and restore it afterwards. 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ci store = readl(main_buf); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci writew(NFC_STATUS, NFC_V1_V2_CONFIG2); 6588c2ecf20Sopenharmony_ci wait_op_done(host, true); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci ret = readw(main_buf); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci writel(store, main_buf); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return ret; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic void mxc_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 6708c2ecf20Sopenharmony_ci uint16_t config1; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 6738c2ecf20Sopenharmony_ci return; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci config1 = readw(NFC_V1_V2_CONFIG1); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (enable) 6788c2ecf20Sopenharmony_ci config1 |= NFC_V1_V2_CONFIG1_ECC_EN; 6798c2ecf20Sopenharmony_ci else 6808c2ecf20Sopenharmony_ci config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci writew(config1, NFC_V1_V2_CONFIG1); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic void mxc_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 6888c2ecf20Sopenharmony_ci uint32_t config2; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 6918c2ecf20Sopenharmony_ci return; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci config2 = readl(NFC_V3_CONFIG2); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (enable) 6968c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_ECC_EN; 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci config2 &= ~NFC_V3_CONFIG2_ECC_EN; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci writel(config2, NFC_V3_CONFIG2); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci/* This functions is used by upper layer to checks if device is ready */ 7048c2ecf20Sopenharmony_cistatic int mxc_nand_dev_ready(struct nand_chip *chip) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci /* 7078c2ecf20Sopenharmony_ci * NFC handles R/B internally. Therefore, this function 7088c2ecf20Sopenharmony_ci * always returns status as ready. 7098c2ecf20Sopenharmony_ci */ 7108c2ecf20Sopenharmony_ci return 1; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic int mxc_nand_read_page_v1(struct nand_chip *chip, void *buf, void *oob, 7148c2ecf20Sopenharmony_ci bool ecc, int page) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7178c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 7188c2ecf20Sopenharmony_ci unsigned int bitflips_corrected = 0; 7198c2ecf20Sopenharmony_ci int no_subpages; 7208c2ecf20Sopenharmony_ci int i; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci host->devtype_data->enable_hwecc(chip, ecc); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); 7258c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, 0, page); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (mtd->writesize > 512) 7288c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_READSTART, true); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci no_subpages = mtd->writesize >> 9; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci for (i = 0; i < no_subpages; i++) { 7338c2ecf20Sopenharmony_ci uint16_t ecc_stats; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* NANDFC buffer 0 is used for page read/write */ 7368c2ecf20Sopenharmony_ci writew((host->active_cs << 4) | i, NFC_V1_V2_BUF_ADDR); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci writew(NFC_OUTPUT, NFC_V1_V2_CONFIG2); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* Wait for operation to complete */ 7418c2ecf20Sopenharmony_ci wait_op_done(host, true); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci ecc_stats = get_ecc_status_v1(host); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci ecc_stats >>= 2; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (buf && ecc) { 7488c2ecf20Sopenharmony_ci switch (ecc_stats & 0x3) { 7498c2ecf20Sopenharmony_ci case 0: 7508c2ecf20Sopenharmony_ci default: 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci case 1: 7538c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected++; 7548c2ecf20Sopenharmony_ci bitflips_corrected = 1; 7558c2ecf20Sopenharmony_ci break; 7568c2ecf20Sopenharmony_ci case 2: 7578c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 7588c2ecf20Sopenharmony_ci break; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (buf) 7648c2ecf20Sopenharmony_ci memcpy32_fromio(buf, host->main_area0, mtd->writesize); 7658c2ecf20Sopenharmony_ci if (oob) 7668c2ecf20Sopenharmony_ci copy_spare(mtd, true, oob); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return bitflips_corrected; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic int mxc_nand_read_page_v2_v3(struct nand_chip *chip, void *buf, 7728c2ecf20Sopenharmony_ci void *oob, bool ecc, int page) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7758c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 7768c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 7778c2ecf20Sopenharmony_ci u32 ecc_stat, err; 7788c2ecf20Sopenharmony_ci int no_subpages; 7798c2ecf20Sopenharmony_ci u8 ecc_bit_mask, err_limit; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci host->devtype_data->enable_hwecc(chip, ecc); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_READ0, false); 7848c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, 0, page); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (mtd->writesize > 512) 7878c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, 7888c2ecf20Sopenharmony_ci NAND_CMD_READSTART, true); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci host->devtype_data->send_page(mtd, NFC_OUTPUT); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (buf) 7938c2ecf20Sopenharmony_ci memcpy32_fromio(buf, host->main_area0, mtd->writesize); 7948c2ecf20Sopenharmony_ci if (oob) 7958c2ecf20Sopenharmony_ci copy_spare(mtd, true, oob); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf; 7988c2ecf20Sopenharmony_ci err_limit = (host->eccsize == 4) ? 0x4 : 0x8; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci no_subpages = mtd->writesize >> 9; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci ecc_stat = host->devtype_data->get_ecc_status(host); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci do { 8058c2ecf20Sopenharmony_ci err = ecc_stat & ecc_bit_mask; 8068c2ecf20Sopenharmony_ci if (err > err_limit) { 8078c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 8088c2ecf20Sopenharmony_ci } else { 8098c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += err; 8108c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, err); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci ecc_stat >>= 4; 8148c2ecf20Sopenharmony_ci } while (--no_subpages); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return max_bitflips; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int mxc_nand_read_page(struct nand_chip *chip, uint8_t *buf, 8208c2ecf20Sopenharmony_ci int oob_required, int page) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 8238c2ecf20Sopenharmony_ci void *oob_buf; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci if (oob_required) 8268c2ecf20Sopenharmony_ci oob_buf = chip->oob_poi; 8278c2ecf20Sopenharmony_ci else 8288c2ecf20Sopenharmony_ci oob_buf = NULL; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci return host->devtype_data->read_page(chip, buf, oob_buf, 1, page); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic int mxc_nand_read_page_raw(struct nand_chip *chip, uint8_t *buf, 8348c2ecf20Sopenharmony_ci int oob_required, int page) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 8378c2ecf20Sopenharmony_ci void *oob_buf; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (oob_required) 8408c2ecf20Sopenharmony_ci oob_buf = chip->oob_poi; 8418c2ecf20Sopenharmony_ci else 8428c2ecf20Sopenharmony_ci oob_buf = NULL; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return host->devtype_data->read_page(chip, buf, oob_buf, 0, page); 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int mxc_nand_read_oob(struct nand_chip *chip, int page) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return host->devtype_data->read_page(chip, NULL, chip->oob_poi, 0, 8528c2ecf20Sopenharmony_ci page); 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic int mxc_nand_write_page(struct nand_chip *chip, const uint8_t *buf, 8568c2ecf20Sopenharmony_ci bool ecc, int page) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 8598c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci host->devtype_data->enable_hwecc(chip, ecc); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_SEQIN, false); 8648c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, 0, page); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci memcpy32_toio(host->main_area0, buf, mtd->writesize); 8678c2ecf20Sopenharmony_ci copy_spare(mtd, false, chip->oob_poi); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci host->devtype_data->send_page(mtd, NFC_INPUT); 8708c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_PAGEPROG, true); 8718c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, 0, page); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return 0; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic int mxc_nand_write_page_ecc(struct nand_chip *chip, const uint8_t *buf, 8778c2ecf20Sopenharmony_ci int oob_required, int page) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci return mxc_nand_write_page(chip, buf, true, page); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int mxc_nand_write_page_raw(struct nand_chip *chip, const uint8_t *buf, 8838c2ecf20Sopenharmony_ci int oob_required, int page) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci return mxc_nand_write_page(chip, buf, false, page); 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic int mxc_nand_write_oob(struct nand_chip *chip, int page) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 8918c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci memset(host->data_buf, 0xff, mtd->writesize); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return mxc_nand_write_page(chip, host->data_buf, false, page); 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic u_char mxc_nand_read_byte(struct nand_chip *nand_chip) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 9018c2ecf20Sopenharmony_ci uint8_t ret; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* Check for status request */ 9048c2ecf20Sopenharmony_ci if (host->status_request) 9058c2ecf20Sopenharmony_ci return host->devtype_data->get_dev_status(host) & 0xFF; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (nand_chip->options & NAND_BUSWIDTH_16) { 9088c2ecf20Sopenharmony_ci /* only take the lower byte of each word */ 9098c2ecf20Sopenharmony_ci ret = *(uint16_t *)(host->data_buf + host->buf_start); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci host->buf_start += 2; 9128c2ecf20Sopenharmony_ci } else { 9138c2ecf20Sopenharmony_ci ret = *(uint8_t *)(host->data_buf + host->buf_start); 9148c2ecf20Sopenharmony_ci host->buf_start++; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci dev_dbg(host->dev, "%s: ret=0x%hhx (start=%u)\n", __func__, ret, host->buf_start); 9188c2ecf20Sopenharmony_ci return ret; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci/* Write data of length len to buffer buf. The data to be 9228c2ecf20Sopenharmony_ci * written on NAND Flash is first copied to RAMbuffer. After the Data Input 9238c2ecf20Sopenharmony_ci * Operation by the NFC, the data is written to NAND Flash */ 9248c2ecf20Sopenharmony_cistatic void mxc_nand_write_buf(struct nand_chip *nand_chip, const u_char *buf, 9258c2ecf20Sopenharmony_ci int len) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 9288c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 9298c2ecf20Sopenharmony_ci u16 col = host->buf_start; 9308c2ecf20Sopenharmony_ci int n = mtd->oobsize + mtd->writesize - col; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci n = min(n, len); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci memcpy(host->data_buf + col, buf, n); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci host->buf_start += n; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* Read the data buffer from the NAND Flash. To read the data from NAND 9408c2ecf20Sopenharmony_ci * Flash first the data output cycle is initiated by the NFC, which copies 9418c2ecf20Sopenharmony_ci * the data to RAMbuffer. This data of length len is then copied to buffer buf. 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_cistatic void mxc_nand_read_buf(struct nand_chip *nand_chip, u_char *buf, 9448c2ecf20Sopenharmony_ci int len) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 9478c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 9488c2ecf20Sopenharmony_ci u16 col = host->buf_start; 9498c2ecf20Sopenharmony_ci int n = mtd->oobsize + mtd->writesize - col; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci n = min(n, len); 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci memcpy(buf, host->data_buf + col, n); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci host->buf_start += n; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci/* This function is used by upper layer for select and 9598c2ecf20Sopenharmony_ci * deselect of the NAND chip */ 9608c2ecf20Sopenharmony_cistatic void mxc_nand_select_chip_v1_v3(struct nand_chip *nand_chip, int chip) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (chip == -1) { 9658c2ecf20Sopenharmony_ci /* Disable the NFC clock */ 9668c2ecf20Sopenharmony_ci if (host->clk_act) { 9678c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 9688c2ecf20Sopenharmony_ci host->clk_act = 0; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci return; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (!host->clk_act) { 9748c2ecf20Sopenharmony_ci /* Enable the NFC clock */ 9758c2ecf20Sopenharmony_ci clk_prepare_enable(host->clk); 9768c2ecf20Sopenharmony_ci host->clk_act = 1; 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void mxc_nand_select_chip_v2(struct nand_chip *nand_chip, int chip) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (chip == -1) { 9858c2ecf20Sopenharmony_ci /* Disable the NFC clock */ 9868c2ecf20Sopenharmony_ci if (host->clk_act) { 9878c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 9888c2ecf20Sopenharmony_ci host->clk_act = 0; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci return; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (!host->clk_act) { 9948c2ecf20Sopenharmony_ci /* Enable the NFC clock */ 9958c2ecf20Sopenharmony_ci clk_prepare_enable(host->clk); 9968c2ecf20Sopenharmony_ci host->clk_act = 1; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci host->active_cs = chip; 10008c2ecf20Sopenharmony_ci writew(host->active_cs << 4, NFC_V1_V2_BUF_ADDR); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci#define MXC_V1_ECCBYTES 5 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section, 10068c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (section >= nand_chip->ecc.steps) 10118c2ecf20Sopenharmony_ci return -ERANGE; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci oobregion->offset = (section * 16) + 6; 10148c2ecf20Sopenharmony_ci oobregion->length = MXC_V1_ECCBYTES; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci return 0; 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic int mxc_v1_ooblayout_free(struct mtd_info *mtd, int section, 10208c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (section > nand_chip->ecc.steps) 10258c2ecf20Sopenharmony_ci return -ERANGE; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (!section) { 10288c2ecf20Sopenharmony_ci if (mtd->writesize <= 512) { 10298c2ecf20Sopenharmony_ci oobregion->offset = 0; 10308c2ecf20Sopenharmony_ci oobregion->length = 5; 10318c2ecf20Sopenharmony_ci } else { 10328c2ecf20Sopenharmony_ci oobregion->offset = 2; 10338c2ecf20Sopenharmony_ci oobregion->length = 4; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci } else { 10368c2ecf20Sopenharmony_ci oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6; 10378c2ecf20Sopenharmony_ci if (section < nand_chip->ecc.steps) 10388c2ecf20Sopenharmony_ci oobregion->length = (section * 16) + 6 - 10398c2ecf20Sopenharmony_ci oobregion->offset; 10408c2ecf20Sopenharmony_ci else 10418c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return 0; 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops mxc_v1_ooblayout_ops = { 10488c2ecf20Sopenharmony_ci .ecc = mxc_v1_ooblayout_ecc, 10498c2ecf20Sopenharmony_ci .free = mxc_v1_ooblayout_free, 10508c2ecf20Sopenharmony_ci}; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic int mxc_v2_ooblayout_ecc(struct mtd_info *mtd, int section, 10538c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 10568c2ecf20Sopenharmony_ci int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (section >= nand_chip->ecc.steps) 10598c2ecf20Sopenharmony_ci return -ERANGE; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci oobregion->offset = (section * stepsize) + 7; 10628c2ecf20Sopenharmony_ci oobregion->length = nand_chip->ecc.bytes; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic int mxc_v2_ooblayout_free(struct mtd_info *mtd, int section, 10688c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 10718c2ecf20Sopenharmony_ci int stepsize = nand_chip->ecc.bytes == 9 ? 16 : 26; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (section >= nand_chip->ecc.steps) 10748c2ecf20Sopenharmony_ci return -ERANGE; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (!section) { 10778c2ecf20Sopenharmony_ci if (mtd->writesize <= 512) { 10788c2ecf20Sopenharmony_ci oobregion->offset = 0; 10798c2ecf20Sopenharmony_ci oobregion->length = 5; 10808c2ecf20Sopenharmony_ci } else { 10818c2ecf20Sopenharmony_ci oobregion->offset = 2; 10828c2ecf20Sopenharmony_ci oobregion->length = 4; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci } else { 10858c2ecf20Sopenharmony_ci oobregion->offset = section * stepsize; 10868c2ecf20Sopenharmony_ci oobregion->length = 7; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops mxc_v2_ooblayout_ops = { 10938c2ecf20Sopenharmony_ci .ecc = mxc_v2_ooblayout_ecc, 10948c2ecf20Sopenharmony_ci .free = mxc_v2_ooblayout_free, 10958c2ecf20Sopenharmony_ci}; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/* 10988c2ecf20Sopenharmony_ci * v2 and v3 type controllers can do 4bit or 8bit ecc depending 10998c2ecf20Sopenharmony_ci * on how much oob the nand chip has. For 8bit ecc we need at least 11008c2ecf20Sopenharmony_ci * 26 bytes of oob data per 512 byte block. 11018c2ecf20Sopenharmony_ci */ 11028c2ecf20Sopenharmony_cistatic int get_eccsize(struct mtd_info *mtd) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci int oobbytes_per_512 = 0; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci oobbytes_per_512 = mtd->oobsize * 512 / mtd->writesize; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (oobbytes_per_512 < 26) 11098c2ecf20Sopenharmony_ci return 4; 11108c2ecf20Sopenharmony_ci else 11118c2ecf20Sopenharmony_ci return 8; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic void preset_v1(struct mtd_info *mtd) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 11178c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 11188c2ecf20Sopenharmony_ci uint16_t config1 = 0; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST && 11218c2ecf20Sopenharmony_ci mtd->writesize) 11228c2ecf20Sopenharmony_ci config1 |= NFC_V1_V2_CONFIG1_ECC_EN; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (!host->devtype_data->irqpending_quirk) 11258c2ecf20Sopenharmony_ci config1 |= NFC_V1_V2_CONFIG1_INT_MSK; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci host->eccsize = 1; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci writew(config1, NFC_V1_V2_CONFIG1); 11308c2ecf20Sopenharmony_ci /* preset operation */ 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* Unlock the internal RAM Buffer */ 11338c2ecf20Sopenharmony_ci writew(0x2, NFC_V1_V2_CONFIG); 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* Blocks to be unlocked */ 11368c2ecf20Sopenharmony_ci writew(0x0, NFC_V1_UNLOCKSTART_BLKADDR); 11378c2ecf20Sopenharmony_ci writew(0xffff, NFC_V1_UNLOCKEND_BLKADDR); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci /* Unlock Block Command for given address range */ 11408c2ecf20Sopenharmony_ci writew(0x4, NFC_V1_V2_WRPROT); 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int mxc_nand_v2_setup_interface(struct nand_chip *chip, int csline, 11448c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 11478c2ecf20Sopenharmony_ci int tRC_min_ns, tRC_ps, ret; 11488c2ecf20Sopenharmony_ci unsigned long rate, rate_round; 11498c2ecf20Sopenharmony_ci const struct nand_sdr_timings *timings; 11508c2ecf20Sopenharmony_ci u16 config1; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci timings = nand_get_sdr_timings(conf); 11538c2ecf20Sopenharmony_ci if (IS_ERR(timings)) 11548c2ecf20Sopenharmony_ci return -ENOTSUPP; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci config1 = readw(NFC_V1_V2_CONFIG1); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci tRC_min_ns = timings->tRC_min / 1000; 11598c2ecf20Sopenharmony_ci rate = 1000000000 / tRC_min_ns; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* 11628c2ecf20Sopenharmony_ci * For tRC < 30ns we have to use EDO mode. In this case the controller 11638c2ecf20Sopenharmony_ci * does one access per clock cycle. Otherwise the controller does one 11648c2ecf20Sopenharmony_ci * access in two clock cycles, thus we have to double the rate to the 11658c2ecf20Sopenharmony_ci * controller. 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_ci if (tRC_min_ns < 30) { 11688c2ecf20Sopenharmony_ci rate_round = clk_round_rate(host->clk, rate); 11698c2ecf20Sopenharmony_ci config1 |= NFC_V2_CONFIG1_ONE_CYCLE; 11708c2ecf20Sopenharmony_ci tRC_ps = 1000000000 / (rate_round / 1000); 11718c2ecf20Sopenharmony_ci } else { 11728c2ecf20Sopenharmony_ci rate *= 2; 11738c2ecf20Sopenharmony_ci rate_round = clk_round_rate(host->clk, rate); 11748c2ecf20Sopenharmony_ci config1 &= ~NFC_V2_CONFIG1_ONE_CYCLE; 11758c2ecf20Sopenharmony_ci tRC_ps = 1000000000 / (rate_round / 1000 / 2); 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* 11798c2ecf20Sopenharmony_ci * The timing values compared against are from the i.MX25 Automotive 11808c2ecf20Sopenharmony_ci * datasheet, Table 50. NFC Timing Parameters 11818c2ecf20Sopenharmony_ci */ 11828c2ecf20Sopenharmony_ci if (timings->tCLS_min > tRC_ps - 1000 || 11838c2ecf20Sopenharmony_ci timings->tCLH_min > tRC_ps - 2000 || 11848c2ecf20Sopenharmony_ci timings->tCS_min > tRC_ps - 1000 || 11858c2ecf20Sopenharmony_ci timings->tCH_min > tRC_ps - 2000 || 11868c2ecf20Sopenharmony_ci timings->tWP_min > tRC_ps - 1500 || 11878c2ecf20Sopenharmony_ci timings->tALS_min > tRC_ps || 11888c2ecf20Sopenharmony_ci timings->tALH_min > tRC_ps - 3000 || 11898c2ecf20Sopenharmony_ci timings->tDS_min > tRC_ps || 11908c2ecf20Sopenharmony_ci timings->tDH_min > tRC_ps - 5000 || 11918c2ecf20Sopenharmony_ci timings->tWC_min > 2 * tRC_ps || 11928c2ecf20Sopenharmony_ci timings->tWH_min > tRC_ps - 2500 || 11938c2ecf20Sopenharmony_ci timings->tRR_min > 6 * tRC_ps || 11948c2ecf20Sopenharmony_ci timings->tRP_min > 3 * tRC_ps / 2 || 11958c2ecf20Sopenharmony_ci timings->tRC_min > 2 * tRC_ps || 11968c2ecf20Sopenharmony_ci timings->tREH_min > (tRC_ps / 2) - 2500) { 11978c2ecf20Sopenharmony_ci dev_dbg(host->dev, "Timing out of bounds\n"); 11988c2ecf20Sopenharmony_ci return -EINVAL; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 12028c2ecf20Sopenharmony_ci return 0; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci ret = clk_set_rate(host->clk, rate); 12058c2ecf20Sopenharmony_ci if (ret) 12068c2ecf20Sopenharmony_ci return ret; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci writew(config1, NFC_V1_V2_CONFIG1); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci dev_dbg(host->dev, "Setting rate to %ldHz, %s mode\n", rate_round, 12118c2ecf20Sopenharmony_ci config1 & NFC_V2_CONFIG1_ONE_CYCLE ? "One cycle (EDO)" : 12128c2ecf20Sopenharmony_ci "normal"); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return 0; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void preset_v2(struct mtd_info *mtd) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct nand_chip *nand_chip = mtd_to_nand(mtd); 12208c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 12218c2ecf20Sopenharmony_ci uint16_t config1 = 0; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci config1 |= NFC_V2_CONFIG1_FP_INT; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if (!host->devtype_data->irqpending_quirk) 12268c2ecf20Sopenharmony_ci config1 |= NFC_V1_V2_CONFIG1_INT_MSK; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (mtd->writesize) { 12298c2ecf20Sopenharmony_ci uint16_t pages_per_block = mtd->erasesize / mtd->writesize; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (nand_chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) 12328c2ecf20Sopenharmony_ci config1 |= NFC_V1_V2_CONFIG1_ECC_EN; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci host->eccsize = get_eccsize(mtd); 12358c2ecf20Sopenharmony_ci if (host->eccsize == 4) 12368c2ecf20Sopenharmony_ci config1 |= NFC_V2_CONFIG1_ECC_MODE_4; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci config1 |= NFC_V2_CONFIG1_PPB(ffs(pages_per_block) - 6); 12398c2ecf20Sopenharmony_ci } else { 12408c2ecf20Sopenharmony_ci host->eccsize = 1; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci writew(config1, NFC_V1_V2_CONFIG1); 12448c2ecf20Sopenharmony_ci /* preset operation */ 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci /* spare area size in 16-bit half-words */ 12478c2ecf20Sopenharmony_ci writew(mtd->oobsize / 2, NFC_V21_RSLTSPARE_AREA); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* Unlock the internal RAM Buffer */ 12508c2ecf20Sopenharmony_ci writew(0x2, NFC_V1_V2_CONFIG); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* Blocks to be unlocked */ 12538c2ecf20Sopenharmony_ci writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR0); 12548c2ecf20Sopenharmony_ci writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR1); 12558c2ecf20Sopenharmony_ci writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR2); 12568c2ecf20Sopenharmony_ci writew(0x0, NFC_V21_UNLOCKSTART_BLKADDR3); 12578c2ecf20Sopenharmony_ci writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR0); 12588c2ecf20Sopenharmony_ci writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR1); 12598c2ecf20Sopenharmony_ci writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR2); 12608c2ecf20Sopenharmony_ci writew(0xffff, NFC_V21_UNLOCKEND_BLKADDR3); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Unlock Block Command for given address range */ 12638c2ecf20Sopenharmony_ci writew(0x4, NFC_V1_V2_WRPROT); 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic void preset_v3(struct mtd_info *mtd) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 12698c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 12708c2ecf20Sopenharmony_ci uint32_t config2, config3; 12718c2ecf20Sopenharmony_ci int i, addr_phases; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci writel(NFC_V3_CONFIG1_RBA(0), NFC_V3_CONFIG1); 12748c2ecf20Sopenharmony_ci writel(NFC_V3_IPC_CREQ, NFC_V3_IPC); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* Unlock the internal RAM Buffer */ 12778c2ecf20Sopenharmony_ci writel(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK, 12788c2ecf20Sopenharmony_ci NFC_V3_WRPROT); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci /* Blocks to be unlocked */ 12818c2ecf20Sopenharmony_ci for (i = 0; i < NAND_MAX_CHIPS; i++) 12828c2ecf20Sopenharmony_ci writel(0xffff << 16, NFC_V3_WRPROT_UNLOCK_BLK_ADD0 + (i << 2)); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci writel(0, NFC_V3_IPC); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci config2 = NFC_V3_CONFIG2_ONE_CYCLE | 12878c2ecf20Sopenharmony_ci NFC_V3_CONFIG2_2CMD_PHASES | 12888c2ecf20Sopenharmony_ci NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | 12898c2ecf20Sopenharmony_ci NFC_V3_CONFIG2_ST_CMD(0x70) | 12908c2ecf20Sopenharmony_ci NFC_V3_CONFIG2_INT_MSK | 12918c2ecf20Sopenharmony_ci NFC_V3_CONFIG2_NUM_ADDR_PHASE0; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci addr_phases = fls(chip->pagemask) >> 3; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci if (mtd->writesize == 2048) { 12968c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_PS_2048; 12978c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); 12988c2ecf20Sopenharmony_ci } else if (mtd->writesize == 4096) { 12998c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_PS_4096; 13008c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases); 13018c2ecf20Sopenharmony_ci } else { 13028c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_PS_512; 13038c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_NUM_ADDR_PHASE1(addr_phases - 1); 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (mtd->writesize) { 13078c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) 13088c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_ECC_EN; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_PPB( 13118c2ecf20Sopenharmony_ci ffs(mtd->erasesize / mtd->writesize) - 6, 13128c2ecf20Sopenharmony_ci host->devtype_data->ppb_shift); 13138c2ecf20Sopenharmony_ci host->eccsize = get_eccsize(mtd); 13148c2ecf20Sopenharmony_ci if (host->eccsize == 8) 13158c2ecf20Sopenharmony_ci config2 |= NFC_V3_CONFIG2_ECC_MODE_8; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci writel(config2, NFC_V3_CONFIG2); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci config3 = NFC_V3_CONFIG3_NUM_OF_DEVICES(0) | 13218c2ecf20Sopenharmony_ci NFC_V3_CONFIG3_NO_SDMA | 13228c2ecf20Sopenharmony_ci NFC_V3_CONFIG3_RBB_MODE | 13238c2ecf20Sopenharmony_ci NFC_V3_CONFIG3_SBB(6) | /* Reset default */ 13248c2ecf20Sopenharmony_ci NFC_V3_CONFIG3_ADD_OP(0); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci if (!(chip->options & NAND_BUSWIDTH_16)) 13278c2ecf20Sopenharmony_ci config3 |= NFC_V3_CONFIG3_FW8; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci writel(config3, NFC_V3_CONFIG3); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci writel(0, NFC_V3_DELAY_LINE); 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci/* Used by the upper layer to write command to NAND Flash for 13358c2ecf20Sopenharmony_ci * different operations to be carried out on NAND Flash */ 13368c2ecf20Sopenharmony_cistatic void mxc_nand_command(struct nand_chip *nand_chip, unsigned command, 13378c2ecf20Sopenharmony_ci int column, int page_addr) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand_chip); 13408c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(nand_chip); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci dev_dbg(host->dev, "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", 13438c2ecf20Sopenharmony_ci command, column, page_addr); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* Reset command state information */ 13468c2ecf20Sopenharmony_ci host->status_request = false; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* Command pre-processing step */ 13498c2ecf20Sopenharmony_ci switch (command) { 13508c2ecf20Sopenharmony_ci case NAND_CMD_RESET: 13518c2ecf20Sopenharmony_ci host->devtype_data->preset(mtd); 13528c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, command, false); 13538c2ecf20Sopenharmony_ci break; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: 13568c2ecf20Sopenharmony_ci host->buf_start = 0; 13578c2ecf20Sopenharmony_ci host->status_request = true; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, command, true); 13608c2ecf20Sopenharmony_ci WARN_ONCE(column != -1 || page_addr != -1, 13618c2ecf20Sopenharmony_ci "Unexpected column/row value (cmd=%u, col=%d, row=%d)\n", 13628c2ecf20Sopenharmony_ci command, column, page_addr); 13638c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, column, page_addr); 13648c2ecf20Sopenharmony_ci break; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci case NAND_CMD_READID: 13678c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, command, true); 13688c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, column, page_addr); 13698c2ecf20Sopenharmony_ci host->devtype_data->send_read_id(host); 13708c2ecf20Sopenharmony_ci host->buf_start = 0; 13718c2ecf20Sopenharmony_ci break; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 13748c2ecf20Sopenharmony_ci case NAND_CMD_ERASE2: 13758c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, command, false); 13768c2ecf20Sopenharmony_ci WARN_ONCE(column != -1, 13778c2ecf20Sopenharmony_ci "Unexpected column value (cmd=%u, col=%d)\n", 13788c2ecf20Sopenharmony_ci command, column); 13798c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, column, page_addr); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci break; 13828c2ecf20Sopenharmony_ci case NAND_CMD_PARAM: 13838c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, command, false); 13848c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, column, page_addr); 13858c2ecf20Sopenharmony_ci host->devtype_data->send_page(mtd, NFC_OUTPUT); 13868c2ecf20Sopenharmony_ci memcpy32_fromio(host->data_buf, host->main_area0, 512); 13878c2ecf20Sopenharmony_ci host->buf_start = 0; 13888c2ecf20Sopenharmony_ci break; 13898c2ecf20Sopenharmony_ci default: 13908c2ecf20Sopenharmony_ci WARN_ONCE(1, "Unimplemented command (cmd=%u)\n", 13918c2ecf20Sopenharmony_ci command); 13928c2ecf20Sopenharmony_ci break; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic int mxc_nand_set_features(struct nand_chip *chip, int addr, 13978c2ecf20Sopenharmony_ci u8 *subfeature_param) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 14008c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 14018c2ecf20Sopenharmony_ci int i; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci host->buf_start = 0; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) 14068c2ecf20Sopenharmony_ci chip->legacy.write_byte(chip, subfeature_param[i]); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); 14098c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_SET_FEATURES, false); 14108c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, addr, -1); 14118c2ecf20Sopenharmony_ci host->devtype_data->send_page(mtd, NFC_INPUT); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci return 0; 14148c2ecf20Sopenharmony_ci} 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_cistatic int mxc_nand_get_features(struct nand_chip *chip, int addr, 14178c2ecf20Sopenharmony_ci u8 *subfeature_param) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 14208c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 14218c2ecf20Sopenharmony_ci int i; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci host->devtype_data->send_cmd(host, NAND_CMD_GET_FEATURES, false); 14248c2ecf20Sopenharmony_ci mxc_do_addr_cycle(mtd, addr, -1); 14258c2ecf20Sopenharmony_ci host->devtype_data->send_page(mtd, NFC_OUTPUT); 14268c2ecf20Sopenharmony_ci memcpy32_fromio(host->data_buf, host->main_area0, 512); 14278c2ecf20Sopenharmony_ci host->buf_start = 0; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci for (i = 0; i < ONFI_SUBFEATURE_PARAM_LEN; ++i) 14308c2ecf20Sopenharmony_ci *subfeature_param++ = chip->legacy.read_byte(chip); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci return 0; 14338c2ecf20Sopenharmony_ci} 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci/* 14368c2ecf20Sopenharmony_ci * The generic flash bbt descriptors overlap with our ecc 14378c2ecf20Sopenharmony_ci * hardware, so define some i.MX specific ones. 14388c2ecf20Sopenharmony_ci */ 14398c2ecf20Sopenharmony_cistatic uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' }; 14408c2ecf20Sopenharmony_cistatic uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' }; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = { 14438c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 14448c2ecf20Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 14458c2ecf20Sopenharmony_ci .offs = 0, 14468c2ecf20Sopenharmony_ci .len = 4, 14478c2ecf20Sopenharmony_ci .veroffs = 4, 14488c2ecf20Sopenharmony_ci .maxblocks = 4, 14498c2ecf20Sopenharmony_ci .pattern = bbt_pattern, 14508c2ecf20Sopenharmony_ci}; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = { 14538c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 14548c2ecf20Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 14558c2ecf20Sopenharmony_ci .offs = 0, 14568c2ecf20Sopenharmony_ci .len = 4, 14578c2ecf20Sopenharmony_ci .veroffs = 4, 14588c2ecf20Sopenharmony_ci .maxblocks = 4, 14598c2ecf20Sopenharmony_ci .pattern = mirror_pattern, 14608c2ecf20Sopenharmony_ci}; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci/* v1 + irqpending_quirk: i.MX21 */ 14638c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx21_nand_devtype_data = { 14648c2ecf20Sopenharmony_ci .preset = preset_v1, 14658c2ecf20Sopenharmony_ci .read_page = mxc_nand_read_page_v1, 14668c2ecf20Sopenharmony_ci .send_cmd = send_cmd_v1_v2, 14678c2ecf20Sopenharmony_ci .send_addr = send_addr_v1_v2, 14688c2ecf20Sopenharmony_ci .send_page = send_page_v1, 14698c2ecf20Sopenharmony_ci .send_read_id = send_read_id_v1_v2, 14708c2ecf20Sopenharmony_ci .get_dev_status = get_dev_status_v1_v2, 14718c2ecf20Sopenharmony_ci .check_int = check_int_v1_v2, 14728c2ecf20Sopenharmony_ci .irq_control = irq_control_v1_v2, 14738c2ecf20Sopenharmony_ci .get_ecc_status = get_ecc_status_v1, 14748c2ecf20Sopenharmony_ci .ooblayout = &mxc_v1_ooblayout_ops, 14758c2ecf20Sopenharmony_ci .select_chip = mxc_nand_select_chip_v1_v3, 14768c2ecf20Sopenharmony_ci .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, 14778c2ecf20Sopenharmony_ci .irqpending_quirk = 1, 14788c2ecf20Sopenharmony_ci .needs_ip = 0, 14798c2ecf20Sopenharmony_ci .regs_offset = 0xe00, 14808c2ecf20Sopenharmony_ci .spare0_offset = 0x800, 14818c2ecf20Sopenharmony_ci .spare_len = 16, 14828c2ecf20Sopenharmony_ci .eccbytes = 3, 14838c2ecf20Sopenharmony_ci .eccsize = 1, 14848c2ecf20Sopenharmony_ci}; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci/* v1 + !irqpending_quirk: i.MX27, i.MX31 */ 14878c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx27_nand_devtype_data = { 14888c2ecf20Sopenharmony_ci .preset = preset_v1, 14898c2ecf20Sopenharmony_ci .read_page = mxc_nand_read_page_v1, 14908c2ecf20Sopenharmony_ci .send_cmd = send_cmd_v1_v2, 14918c2ecf20Sopenharmony_ci .send_addr = send_addr_v1_v2, 14928c2ecf20Sopenharmony_ci .send_page = send_page_v1, 14938c2ecf20Sopenharmony_ci .send_read_id = send_read_id_v1_v2, 14948c2ecf20Sopenharmony_ci .get_dev_status = get_dev_status_v1_v2, 14958c2ecf20Sopenharmony_ci .check_int = check_int_v1_v2, 14968c2ecf20Sopenharmony_ci .irq_control = irq_control_v1_v2, 14978c2ecf20Sopenharmony_ci .get_ecc_status = get_ecc_status_v1, 14988c2ecf20Sopenharmony_ci .ooblayout = &mxc_v1_ooblayout_ops, 14998c2ecf20Sopenharmony_ci .select_chip = mxc_nand_select_chip_v1_v3, 15008c2ecf20Sopenharmony_ci .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, 15018c2ecf20Sopenharmony_ci .irqpending_quirk = 0, 15028c2ecf20Sopenharmony_ci .needs_ip = 0, 15038c2ecf20Sopenharmony_ci .regs_offset = 0xe00, 15048c2ecf20Sopenharmony_ci .spare0_offset = 0x800, 15058c2ecf20Sopenharmony_ci .axi_offset = 0, 15068c2ecf20Sopenharmony_ci .spare_len = 16, 15078c2ecf20Sopenharmony_ci .eccbytes = 3, 15088c2ecf20Sopenharmony_ci .eccsize = 1, 15098c2ecf20Sopenharmony_ci}; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci/* v21: i.MX25, i.MX35 */ 15128c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx25_nand_devtype_data = { 15138c2ecf20Sopenharmony_ci .preset = preset_v2, 15148c2ecf20Sopenharmony_ci .read_page = mxc_nand_read_page_v2_v3, 15158c2ecf20Sopenharmony_ci .send_cmd = send_cmd_v1_v2, 15168c2ecf20Sopenharmony_ci .send_addr = send_addr_v1_v2, 15178c2ecf20Sopenharmony_ci .send_page = send_page_v2, 15188c2ecf20Sopenharmony_ci .send_read_id = send_read_id_v1_v2, 15198c2ecf20Sopenharmony_ci .get_dev_status = get_dev_status_v1_v2, 15208c2ecf20Sopenharmony_ci .check_int = check_int_v1_v2, 15218c2ecf20Sopenharmony_ci .irq_control = irq_control_v1_v2, 15228c2ecf20Sopenharmony_ci .get_ecc_status = get_ecc_status_v2, 15238c2ecf20Sopenharmony_ci .ooblayout = &mxc_v2_ooblayout_ops, 15248c2ecf20Sopenharmony_ci .select_chip = mxc_nand_select_chip_v2, 15258c2ecf20Sopenharmony_ci .setup_interface = mxc_nand_v2_setup_interface, 15268c2ecf20Sopenharmony_ci .enable_hwecc = mxc_nand_enable_hwecc_v1_v2, 15278c2ecf20Sopenharmony_ci .irqpending_quirk = 0, 15288c2ecf20Sopenharmony_ci .needs_ip = 0, 15298c2ecf20Sopenharmony_ci .regs_offset = 0x1e00, 15308c2ecf20Sopenharmony_ci .spare0_offset = 0x1000, 15318c2ecf20Sopenharmony_ci .axi_offset = 0, 15328c2ecf20Sopenharmony_ci .spare_len = 64, 15338c2ecf20Sopenharmony_ci .eccbytes = 9, 15348c2ecf20Sopenharmony_ci .eccsize = 0, 15358c2ecf20Sopenharmony_ci}; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci/* v3.2a: i.MX51 */ 15388c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx51_nand_devtype_data = { 15398c2ecf20Sopenharmony_ci .preset = preset_v3, 15408c2ecf20Sopenharmony_ci .read_page = mxc_nand_read_page_v2_v3, 15418c2ecf20Sopenharmony_ci .send_cmd = send_cmd_v3, 15428c2ecf20Sopenharmony_ci .send_addr = send_addr_v3, 15438c2ecf20Sopenharmony_ci .send_page = send_page_v3, 15448c2ecf20Sopenharmony_ci .send_read_id = send_read_id_v3, 15458c2ecf20Sopenharmony_ci .get_dev_status = get_dev_status_v3, 15468c2ecf20Sopenharmony_ci .check_int = check_int_v3, 15478c2ecf20Sopenharmony_ci .irq_control = irq_control_v3, 15488c2ecf20Sopenharmony_ci .get_ecc_status = get_ecc_status_v3, 15498c2ecf20Sopenharmony_ci .ooblayout = &mxc_v2_ooblayout_ops, 15508c2ecf20Sopenharmony_ci .select_chip = mxc_nand_select_chip_v1_v3, 15518c2ecf20Sopenharmony_ci .enable_hwecc = mxc_nand_enable_hwecc_v3, 15528c2ecf20Sopenharmony_ci .irqpending_quirk = 0, 15538c2ecf20Sopenharmony_ci .needs_ip = 1, 15548c2ecf20Sopenharmony_ci .regs_offset = 0, 15558c2ecf20Sopenharmony_ci .spare0_offset = 0x1000, 15568c2ecf20Sopenharmony_ci .axi_offset = 0x1e00, 15578c2ecf20Sopenharmony_ci .spare_len = 64, 15588c2ecf20Sopenharmony_ci .eccbytes = 0, 15598c2ecf20Sopenharmony_ci .eccsize = 0, 15608c2ecf20Sopenharmony_ci .ppb_shift = 7, 15618c2ecf20Sopenharmony_ci}; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci/* v3.2b: i.MX53 */ 15648c2ecf20Sopenharmony_cistatic const struct mxc_nand_devtype_data imx53_nand_devtype_data = { 15658c2ecf20Sopenharmony_ci .preset = preset_v3, 15668c2ecf20Sopenharmony_ci .read_page = mxc_nand_read_page_v2_v3, 15678c2ecf20Sopenharmony_ci .send_cmd = send_cmd_v3, 15688c2ecf20Sopenharmony_ci .send_addr = send_addr_v3, 15698c2ecf20Sopenharmony_ci .send_page = send_page_v3, 15708c2ecf20Sopenharmony_ci .send_read_id = send_read_id_v3, 15718c2ecf20Sopenharmony_ci .get_dev_status = get_dev_status_v3, 15728c2ecf20Sopenharmony_ci .check_int = check_int_v3, 15738c2ecf20Sopenharmony_ci .irq_control = irq_control_v3, 15748c2ecf20Sopenharmony_ci .get_ecc_status = get_ecc_status_v3, 15758c2ecf20Sopenharmony_ci .ooblayout = &mxc_v2_ooblayout_ops, 15768c2ecf20Sopenharmony_ci .select_chip = mxc_nand_select_chip_v1_v3, 15778c2ecf20Sopenharmony_ci .enable_hwecc = mxc_nand_enable_hwecc_v3, 15788c2ecf20Sopenharmony_ci .irqpending_quirk = 0, 15798c2ecf20Sopenharmony_ci .needs_ip = 1, 15808c2ecf20Sopenharmony_ci .regs_offset = 0, 15818c2ecf20Sopenharmony_ci .spare0_offset = 0x1000, 15828c2ecf20Sopenharmony_ci .axi_offset = 0x1e00, 15838c2ecf20Sopenharmony_ci .spare_len = 64, 15848c2ecf20Sopenharmony_ci .eccbytes = 0, 15858c2ecf20Sopenharmony_ci .eccsize = 0, 15868c2ecf20Sopenharmony_ci .ppb_shift = 8, 15878c2ecf20Sopenharmony_ci}; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic inline int is_imx21_nfc(struct mxc_nand_host *host) 15908c2ecf20Sopenharmony_ci{ 15918c2ecf20Sopenharmony_ci return host->devtype_data == &imx21_nand_devtype_data; 15928c2ecf20Sopenharmony_ci} 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic inline int is_imx27_nfc(struct mxc_nand_host *host) 15958c2ecf20Sopenharmony_ci{ 15968c2ecf20Sopenharmony_ci return host->devtype_data == &imx27_nand_devtype_data; 15978c2ecf20Sopenharmony_ci} 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic inline int is_imx25_nfc(struct mxc_nand_host *host) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci return host->devtype_data == &imx25_nand_devtype_data; 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_cistatic inline int is_imx51_nfc(struct mxc_nand_host *host) 16058c2ecf20Sopenharmony_ci{ 16068c2ecf20Sopenharmony_ci return host->devtype_data == &imx51_nand_devtype_data; 16078c2ecf20Sopenharmony_ci} 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_cistatic inline int is_imx53_nfc(struct mxc_nand_host *host) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci return host->devtype_data == &imx53_nand_devtype_data; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic const struct platform_device_id mxcnd_devtype[] = { 16158c2ecf20Sopenharmony_ci { 16168c2ecf20Sopenharmony_ci .name = "imx21-nand", 16178c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t) &imx21_nand_devtype_data, 16188c2ecf20Sopenharmony_ci }, { 16198c2ecf20Sopenharmony_ci .name = "imx27-nand", 16208c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t) &imx27_nand_devtype_data, 16218c2ecf20Sopenharmony_ci }, { 16228c2ecf20Sopenharmony_ci .name = "imx25-nand", 16238c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t) &imx25_nand_devtype_data, 16248c2ecf20Sopenharmony_ci }, { 16258c2ecf20Sopenharmony_ci .name = "imx51-nand", 16268c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t) &imx51_nand_devtype_data, 16278c2ecf20Sopenharmony_ci }, { 16288c2ecf20Sopenharmony_ci .name = "imx53-nand", 16298c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t) &imx53_nand_devtype_data, 16308c2ecf20Sopenharmony_ci }, { 16318c2ecf20Sopenharmony_ci /* sentinel */ 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci}; 16348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, mxcnd_devtype); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 16378c2ecf20Sopenharmony_cistatic const struct of_device_id mxcnd_dt_ids[] = { 16388c2ecf20Sopenharmony_ci { 16398c2ecf20Sopenharmony_ci .compatible = "fsl,imx21-nand", 16408c2ecf20Sopenharmony_ci .data = &imx21_nand_devtype_data, 16418c2ecf20Sopenharmony_ci }, { 16428c2ecf20Sopenharmony_ci .compatible = "fsl,imx27-nand", 16438c2ecf20Sopenharmony_ci .data = &imx27_nand_devtype_data, 16448c2ecf20Sopenharmony_ci }, { 16458c2ecf20Sopenharmony_ci .compatible = "fsl,imx25-nand", 16468c2ecf20Sopenharmony_ci .data = &imx25_nand_devtype_data, 16478c2ecf20Sopenharmony_ci }, { 16488c2ecf20Sopenharmony_ci .compatible = "fsl,imx51-nand", 16498c2ecf20Sopenharmony_ci .data = &imx51_nand_devtype_data, 16508c2ecf20Sopenharmony_ci }, { 16518c2ecf20Sopenharmony_ci .compatible = "fsl,imx53-nand", 16528c2ecf20Sopenharmony_ci .data = &imx53_nand_devtype_data, 16538c2ecf20Sopenharmony_ci }, 16548c2ecf20Sopenharmony_ci { /* sentinel */ } 16558c2ecf20Sopenharmony_ci}; 16568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxcnd_dt_ids); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic int mxcnd_probe_dt(struct mxc_nand_host *host) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci struct device_node *np = host->dev->of_node; 16618c2ecf20Sopenharmony_ci const struct of_device_id *of_id = 16628c2ecf20Sopenharmony_ci of_match_device(mxcnd_dt_ids, host->dev); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci if (!np) 16658c2ecf20Sopenharmony_ci return 1; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci host->devtype_data = of_id->data; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci return 0; 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci#else 16728c2ecf20Sopenharmony_cistatic int mxcnd_probe_dt(struct mxc_nand_host *host) 16738c2ecf20Sopenharmony_ci{ 16748c2ecf20Sopenharmony_ci return 1; 16758c2ecf20Sopenharmony_ci} 16768c2ecf20Sopenharmony_ci#endif 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic int mxcnd_attach_chip(struct nand_chip *chip) 16798c2ecf20Sopenharmony_ci{ 16808c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 16818c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 16828c2ecf20Sopenharmony_ci struct device *dev = mtd->dev.parent; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci chip->ecc.bytes = host->devtype_data->eccbytes; 16858c2ecf20Sopenharmony_ci host->eccsize = host->devtype_data->eccsize; 16868c2ecf20Sopenharmony_ci chip->ecc.size = 512; 16878c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, host->devtype_data->ooblayout); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci switch (chip->ecc.engine_type) { 16908c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 16918c2ecf20Sopenharmony_ci chip->ecc.read_page = mxc_nand_read_page; 16928c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = mxc_nand_read_page_raw; 16938c2ecf20Sopenharmony_ci chip->ecc.read_oob = mxc_nand_read_oob; 16948c2ecf20Sopenharmony_ci chip->ecc.write_page = mxc_nand_write_page_ecc; 16958c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = mxc_nand_write_page_raw; 16968c2ecf20Sopenharmony_ci chip->ecc.write_oob = mxc_nand_write_oob; 16978c2ecf20Sopenharmony_ci break; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 17008c2ecf20Sopenharmony_ci break; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci default: 17038c2ecf20Sopenharmony_ci return -EINVAL; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (chip->bbt_options & NAND_BBT_USE_FLASH) { 17078c2ecf20Sopenharmony_ci chip->bbt_td = &bbt_main_descr; 17088c2ecf20Sopenharmony_ci chip->bbt_md = &bbt_mirror_descr; 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* Allocate the right size buffer now */ 17128c2ecf20Sopenharmony_ci devm_kfree(dev, (void *)host->data_buf); 17138c2ecf20Sopenharmony_ci host->data_buf = devm_kzalloc(dev, mtd->writesize + mtd->oobsize, 17148c2ecf20Sopenharmony_ci GFP_KERNEL); 17158c2ecf20Sopenharmony_ci if (!host->data_buf) 17168c2ecf20Sopenharmony_ci return -ENOMEM; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci /* Call preset again, with correct writesize chip time */ 17198c2ecf20Sopenharmony_ci host->devtype_data->preset(mtd); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci if (!chip->ecc.bytes) { 17228c2ecf20Sopenharmony_ci if (host->eccsize == 8) 17238c2ecf20Sopenharmony_ci chip->ecc.bytes = 18; 17248c2ecf20Sopenharmony_ci else if (host->eccsize == 4) 17258c2ecf20Sopenharmony_ci chip->ecc.bytes = 9; 17268c2ecf20Sopenharmony_ci } 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci /* 17298c2ecf20Sopenharmony_ci * Experimentation shows that i.MX NFC can only handle up to 218 oob 17308c2ecf20Sopenharmony_ci * bytes. Limit used_oobsize to 218 so as to not confuse copy_spare() 17318c2ecf20Sopenharmony_ci * into copying invalid data to/from the spare IO buffer, as this 17328c2ecf20Sopenharmony_ci * might cause ECC data corruption when doing sub-page write to a 17338c2ecf20Sopenharmony_ci * partially written page. 17348c2ecf20Sopenharmony_ci */ 17358c2ecf20Sopenharmony_ci host->used_oobsize = min(mtd->oobsize, 218U); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { 17388c2ecf20Sopenharmony_ci if (is_imx21_nfc(host) || is_imx27_nfc(host)) 17398c2ecf20Sopenharmony_ci chip->ecc.strength = 1; 17408c2ecf20Sopenharmony_ci else 17418c2ecf20Sopenharmony_ci chip->ecc.strength = (host->eccsize == 4) ? 4 : 8; 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return 0; 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int mxcnd_setup_interface(struct nand_chip *chip, int chipnr, 17488c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci struct mxc_nand_host *host = nand_get_controller_data(chip); 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci return host->devtype_data->setup_interface(chip, chipnr, conf); 17538c2ecf20Sopenharmony_ci} 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_cistatic const struct nand_controller_ops mxcnd_controller_ops = { 17568c2ecf20Sopenharmony_ci .attach_chip = mxcnd_attach_chip, 17578c2ecf20Sopenharmony_ci .setup_interface = mxcnd_setup_interface, 17588c2ecf20Sopenharmony_ci}; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic int mxcnd_probe(struct platform_device *pdev) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct nand_chip *this; 17638c2ecf20Sopenharmony_ci struct mtd_info *mtd; 17648c2ecf20Sopenharmony_ci struct mxc_nand_host *host; 17658c2ecf20Sopenharmony_ci struct resource *res; 17668c2ecf20Sopenharmony_ci int err = 0; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci /* Allocate memory for MTD device structure and private data */ 17698c2ecf20Sopenharmony_ci host = devm_kzalloc(&pdev->dev, sizeof(struct mxc_nand_host), 17708c2ecf20Sopenharmony_ci GFP_KERNEL); 17718c2ecf20Sopenharmony_ci if (!host) 17728c2ecf20Sopenharmony_ci return -ENOMEM; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* allocate a temporary buffer for the nand_scan_ident() */ 17758c2ecf20Sopenharmony_ci host->data_buf = devm_kzalloc(&pdev->dev, PAGE_SIZE, GFP_KERNEL); 17768c2ecf20Sopenharmony_ci if (!host->data_buf) 17778c2ecf20Sopenharmony_ci return -ENOMEM; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci host->dev = &pdev->dev; 17808c2ecf20Sopenharmony_ci /* structures must be linked */ 17818c2ecf20Sopenharmony_ci this = &host->nand; 17828c2ecf20Sopenharmony_ci mtd = nand_to_mtd(this); 17838c2ecf20Sopenharmony_ci mtd->dev.parent = &pdev->dev; 17848c2ecf20Sopenharmony_ci mtd->name = DRIVER_NAME; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci /* 50 us command delay time */ 17878c2ecf20Sopenharmony_ci this->legacy.chip_delay = 5; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci nand_set_controller_data(this, host); 17908c2ecf20Sopenharmony_ci nand_set_flash_node(this, pdev->dev.of_node), 17918c2ecf20Sopenharmony_ci this->legacy.dev_ready = mxc_nand_dev_ready; 17928c2ecf20Sopenharmony_ci this->legacy.cmdfunc = mxc_nand_command; 17938c2ecf20Sopenharmony_ci this->legacy.read_byte = mxc_nand_read_byte; 17948c2ecf20Sopenharmony_ci this->legacy.write_buf = mxc_nand_write_buf; 17958c2ecf20Sopenharmony_ci this->legacy.read_buf = mxc_nand_read_buf; 17968c2ecf20Sopenharmony_ci this->legacy.set_features = mxc_nand_set_features; 17978c2ecf20Sopenharmony_ci this->legacy.get_features = mxc_nand_get_features; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci host->clk = devm_clk_get(&pdev->dev, NULL); 18008c2ecf20Sopenharmony_ci if (IS_ERR(host->clk)) 18018c2ecf20Sopenharmony_ci return PTR_ERR(host->clk); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci err = mxcnd_probe_dt(host); 18048c2ecf20Sopenharmony_ci if (err > 0) { 18058c2ecf20Sopenharmony_ci struct mxc_nand_platform_data *pdata = 18068c2ecf20Sopenharmony_ci dev_get_platdata(&pdev->dev); 18078c2ecf20Sopenharmony_ci if (pdata) { 18088c2ecf20Sopenharmony_ci host->pdata = *pdata; 18098c2ecf20Sopenharmony_ci host->devtype_data = (struct mxc_nand_devtype_data *) 18108c2ecf20Sopenharmony_ci pdev->id_entry->driver_data; 18118c2ecf20Sopenharmony_ci } else { 18128c2ecf20Sopenharmony_ci err = -ENODEV; 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci if (err < 0) 18168c2ecf20Sopenharmony_ci return err; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (!host->devtype_data->setup_interface) 18198c2ecf20Sopenharmony_ci this->options |= NAND_KEEP_TIMINGS; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (host->devtype_data->needs_ip) { 18228c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 18238c2ecf20Sopenharmony_ci host->regs_ip = devm_ioremap_resource(&pdev->dev, res); 18248c2ecf20Sopenharmony_ci if (IS_ERR(host->regs_ip)) 18258c2ecf20Sopenharmony_ci return PTR_ERR(host->regs_ip); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 18288c2ecf20Sopenharmony_ci } else { 18298c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci host->base = devm_ioremap_resource(&pdev->dev, res); 18338c2ecf20Sopenharmony_ci if (IS_ERR(host->base)) 18348c2ecf20Sopenharmony_ci return PTR_ERR(host->base); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci host->main_area0 = host->base; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci if (host->devtype_data->regs_offset) 18398c2ecf20Sopenharmony_ci host->regs = host->base + host->devtype_data->regs_offset; 18408c2ecf20Sopenharmony_ci host->spare0 = host->base + host->devtype_data->spare0_offset; 18418c2ecf20Sopenharmony_ci if (host->devtype_data->axi_offset) 18428c2ecf20Sopenharmony_ci host->regs_axi = host->base + host->devtype_data->axi_offset; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci this->legacy.select_chip = host->devtype_data->select_chip; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci /* NAND bus width determines access functions used by upper layer */ 18478c2ecf20Sopenharmony_ci if (host->pdata.width == 2) 18488c2ecf20Sopenharmony_ci this->options |= NAND_BUSWIDTH_16; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci /* update flash based bbt */ 18518c2ecf20Sopenharmony_ci if (host->pdata.flash_bbt) 18528c2ecf20Sopenharmony_ci this->bbt_options |= NAND_BBT_USE_FLASH; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci init_completion(&host->op_completion); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci host->irq = platform_get_irq(pdev, 0); 18578c2ecf20Sopenharmony_ci if (host->irq < 0) 18588c2ecf20Sopenharmony_ci return host->irq; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci /* 18618c2ecf20Sopenharmony_ci * Use host->devtype_data->irq_control() here instead of irq_control() 18628c2ecf20Sopenharmony_ci * because we must not disable_irq_nosync without having requested the 18638c2ecf20Sopenharmony_ci * irq. 18648c2ecf20Sopenharmony_ci */ 18658c2ecf20Sopenharmony_ci host->devtype_data->irq_control(host, 0); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq, 18688c2ecf20Sopenharmony_ci 0, DRIVER_NAME, host); 18698c2ecf20Sopenharmony_ci if (err) 18708c2ecf20Sopenharmony_ci return err; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci err = clk_prepare_enable(host->clk); 18738c2ecf20Sopenharmony_ci if (err) 18748c2ecf20Sopenharmony_ci return err; 18758c2ecf20Sopenharmony_ci host->clk_act = 1; 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci /* 18788c2ecf20Sopenharmony_ci * Now that we "own" the interrupt make sure the interrupt mask bit is 18798c2ecf20Sopenharmony_ci * cleared on i.MX21. Otherwise we can't read the interrupt status bit 18808c2ecf20Sopenharmony_ci * on this machine. 18818c2ecf20Sopenharmony_ci */ 18828c2ecf20Sopenharmony_ci if (host->devtype_data->irqpending_quirk) { 18838c2ecf20Sopenharmony_ci disable_irq_nosync(host->irq); 18848c2ecf20Sopenharmony_ci host->devtype_data->irq_control(host, 1); 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci /* Scan the NAND device */ 18888c2ecf20Sopenharmony_ci this->legacy.dummy_controller.ops = &mxcnd_controller_ops; 18898c2ecf20Sopenharmony_ci err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1); 18908c2ecf20Sopenharmony_ci if (err) 18918c2ecf20Sopenharmony_ci goto escan; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci /* Register the partitions */ 18948c2ecf20Sopenharmony_ci err = mtd_device_parse_register(mtd, part_probes, NULL, 18958c2ecf20Sopenharmony_ci host->pdata.parts, 18968c2ecf20Sopenharmony_ci host->pdata.nr_parts); 18978c2ecf20Sopenharmony_ci if (err) 18988c2ecf20Sopenharmony_ci goto cleanup_nand; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, host); 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci return 0; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_cicleanup_nand: 19058c2ecf20Sopenharmony_ci nand_cleanup(this); 19068c2ecf20Sopenharmony_ciescan: 19078c2ecf20Sopenharmony_ci if (host->clk_act) 19088c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci return err; 19118c2ecf20Sopenharmony_ci} 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cistatic int mxcnd_remove(struct platform_device *pdev) 19148c2ecf20Sopenharmony_ci{ 19158c2ecf20Sopenharmony_ci struct mxc_nand_host *host = platform_get_drvdata(pdev); 19168c2ecf20Sopenharmony_ci struct nand_chip *chip = &host->nand; 19178c2ecf20Sopenharmony_ci int ret; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 19208c2ecf20Sopenharmony_ci WARN_ON(ret); 19218c2ecf20Sopenharmony_ci nand_cleanup(chip); 19228c2ecf20Sopenharmony_ci if (host->clk_act) 19238c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci return 0; 19268c2ecf20Sopenharmony_ci} 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cistatic struct platform_driver mxcnd_driver = { 19298c2ecf20Sopenharmony_ci .driver = { 19308c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 19318c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(mxcnd_dt_ids), 19328c2ecf20Sopenharmony_ci }, 19338c2ecf20Sopenharmony_ci .id_table = mxcnd_devtype, 19348c2ecf20Sopenharmony_ci .probe = mxcnd_probe, 19358c2ecf20Sopenharmony_ci .remove = mxcnd_remove, 19368c2ecf20Sopenharmony_ci}; 19378c2ecf20Sopenharmony_cimodule_platform_driver(mxcnd_driver); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 19408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MXC NAND MTD driver"); 19418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1942