18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale Integrated Flash Controller NAND driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011-2012 Freescale Semiconductor, Inc 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 188c2ecf20Sopenharmony_ci#include <linux/mtd/nand_ecc.h> 198c2ecf20Sopenharmony_ci#include <linux/fsl_ifc.h> 208c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define ERR_BYTE 0xFF /* Value returned for read 238c2ecf20Sopenharmony_ci bytes when read failed */ 248c2ecf20Sopenharmony_ci#define IFC_TIMEOUT_MSECS 1000 /* Maximum timeout to wait 258c2ecf20Sopenharmony_ci for IFC NAND Machine */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct fsl_ifc_ctrl; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* mtd information per set */ 308c2ecf20Sopenharmony_cistruct fsl_ifc_mtd { 318c2ecf20Sopenharmony_ci struct nand_chip chip; 328c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci struct device *dev; 358c2ecf20Sopenharmony_ci int bank; /* Chip select bank number */ 368c2ecf20Sopenharmony_ci unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ 378c2ecf20Sopenharmony_ci u8 __iomem *vbase; /* Chip select base virtual address */ 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* overview of the fsl ifc controller */ 418c2ecf20Sopenharmony_cistruct fsl_ifc_nand_ctrl { 428c2ecf20Sopenharmony_ci struct nand_controller controller; 438c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci void __iomem *addr; /* Address of assigned IFC buffer */ 468c2ecf20Sopenharmony_ci unsigned int page; /* Last page written to / read from */ 478c2ecf20Sopenharmony_ci unsigned int read_bytes;/* Number of bytes read during command */ 488c2ecf20Sopenharmony_ci unsigned int column; /* Saved column from SEQIN */ 498c2ecf20Sopenharmony_ci unsigned int index; /* Pointer to next byte to 'read' */ 508c2ecf20Sopenharmony_ci unsigned int oob; /* Non zero if operating on OOB data */ 518c2ecf20Sopenharmony_ci unsigned int eccread; /* Non zero for a full-page ECC read */ 528c2ecf20Sopenharmony_ci unsigned int counter; /* counter for the initializations */ 538c2ecf20Sopenharmony_ci unsigned int max_bitflips; /* Saved during READ0 cmd */ 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Generic flash bbt descriptors 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic u8 bbt_pattern[] = {'B', 'b', 't', '0' }; 628c2ecf20Sopenharmony_cistatic u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = { 658c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | 668c2ecf20Sopenharmony_ci NAND_BBT_2BIT | NAND_BBT_VERSION, 678c2ecf20Sopenharmony_ci .offs = 2, /* 0 on 8-bit small page */ 688c2ecf20Sopenharmony_ci .len = 4, 698c2ecf20Sopenharmony_ci .veroffs = 6, 708c2ecf20Sopenharmony_ci .maxblocks = 4, 718c2ecf20Sopenharmony_ci .pattern = bbt_pattern, 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = { 758c2ecf20Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | 768c2ecf20Sopenharmony_ci NAND_BBT_2BIT | NAND_BBT_VERSION, 778c2ecf20Sopenharmony_ci .offs = 2, /* 0 on 8-bit small page */ 788c2ecf20Sopenharmony_ci .len = 4, 798c2ecf20Sopenharmony_ci .veroffs = 6, 808c2ecf20Sopenharmony_ci .maxblocks = 4, 818c2ecf20Sopenharmony_ci .pattern = mirror_pattern, 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section, 858c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (section) 908c2ecf20Sopenharmony_ci return -ERANGE; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci oobregion->offset = 8; 938c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.total; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section, 998c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (section > 1) 1048c2ecf20Sopenharmony_ci return -ERANGE; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (mtd->writesize == 512 && 1078c2ecf20Sopenharmony_ci !(chip->options & NAND_BUSWIDTH_16)) { 1088c2ecf20Sopenharmony_ci if (!section) { 1098c2ecf20Sopenharmony_ci oobregion->offset = 0; 1108c2ecf20Sopenharmony_ci oobregion->length = 5; 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci oobregion->offset = 6; 1138c2ecf20Sopenharmony_ci oobregion->length = 2; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (!section) { 1208c2ecf20Sopenharmony_ci oobregion->offset = 2; 1218c2ecf20Sopenharmony_ci oobregion->length = 6; 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci oobregion->offset = chip->ecc.total + 8; 1248c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = { 1318c2ecf20Sopenharmony_ci .ecc = fsl_ifc_ooblayout_ecc, 1328c2ecf20Sopenharmony_ci .free = fsl_ifc_ooblayout_free, 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * Set up the IFC hardware block and page address fields, and the ifc nand 1378c2ecf20Sopenharmony_ci * structure addr field to point to the correct IFC buffer in memory 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 1428c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 1438c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 1448c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 1458c2ecf20Sopenharmony_ci int buf_num; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ifc_nand_ctrl->page = page_addr; 1488c2ecf20Sopenharmony_ci /* Program ROW0/COL0 */ 1498c2ecf20Sopenharmony_ci ifc_out32(page_addr, &ifc->ifc_nand.row0); 1508c2ecf20Sopenharmony_ci ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci buf_num = page_addr & priv->bufnum_mask; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); 1558c2ecf20Sopenharmony_ci ifc_nand_ctrl->index = column; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* for OOB data point to the second half of the buffer */ 1588c2ecf20Sopenharmony_ci if (oob) 1598c2ecf20Sopenharmony_ci ifc_nand_ctrl->index += mtd->writesize; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* returns nonzero if entire page is blank */ 1638c2ecf20Sopenharmony_cistatic int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, 1648c2ecf20Sopenharmony_ci u32 eccstat, unsigned int bufnum) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return (eccstat >> ((3 - bufnum % 4) * 8)) & 15; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * execute IFC NAND command and wait for it to complete 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic void fsl_ifc_run_command(struct mtd_info *mtd) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 1758c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 1768c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 1778c2ecf20Sopenharmony_ci struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; 1788c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 1798c2ecf20Sopenharmony_ci u32 eccstat; 1808c2ecf20Sopenharmony_ci int i; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* set the chip select for NAND Transaction */ 1838c2ecf20Sopenharmony_ci ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, 1848c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_csel); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci dev_vdbg(priv->dev, 1878c2ecf20Sopenharmony_ci "%s: fir0=%08x fcr0=%08x\n", 1888c2ecf20Sopenharmony_ci __func__, 1898c2ecf20Sopenharmony_ci ifc_in32(&ifc->ifc_nand.nand_fir0), 1908c2ecf20Sopenharmony_ci ifc_in32(&ifc->ifc_nand.nand_fcr0)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ctrl->nand_stat = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* start read/write seq */ 1958c2ecf20Sopenharmony_ci ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* wait for command complete flag or timeout */ 1988c2ecf20Sopenharmony_ci wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, 1998c2ecf20Sopenharmony_ci msecs_to_jiffies(IFC_TIMEOUT_MSECS)); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* ctrl->nand_stat will be updated from IRQ context */ 2028c2ecf20Sopenharmony_ci if (!ctrl->nand_stat) 2038c2ecf20Sopenharmony_ci dev_err(priv->dev, "Controller is not responding\n"); 2048c2ecf20Sopenharmony_ci if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) 2058c2ecf20Sopenharmony_ci dev_err(priv->dev, "NAND Flash Timeout Error\n"); 2068c2ecf20Sopenharmony_ci if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) 2078c2ecf20Sopenharmony_ci dev_err(priv->dev, "NAND Flash Write Protect Error\n"); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci nctrl->max_bitflips = 0; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (nctrl->eccread) { 2128c2ecf20Sopenharmony_ci int errors; 2138c2ecf20Sopenharmony_ci int bufnum = nctrl->page & priv->bufnum_mask; 2148c2ecf20Sopenharmony_ci int sector_start = bufnum * chip->ecc.steps; 2158c2ecf20Sopenharmony_ci int sector_end = sector_start + chip->ecc.steps - 1; 2168c2ecf20Sopenharmony_ci __be32 __iomem *eccstat_regs; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci eccstat_regs = ifc->ifc_nand.nand_eccstat; 2198c2ecf20Sopenharmony_ci eccstat = ifc_in32(&eccstat_regs[sector_start / 4]); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci for (i = sector_start; i <= sector_end; i++) { 2228c2ecf20Sopenharmony_ci if (i != sector_start && !(i % 4)) 2238c2ecf20Sopenharmony_ci eccstat = ifc_in32(&eccstat_regs[i / 4]); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci errors = check_read_ecc(mtd, ctrl, eccstat, i); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (errors == 15) { 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * Uncorrectable error. 2308c2ecf20Sopenharmony_ci * We'll check for blank pages later. 2318c2ecf20Sopenharmony_ci * 2328c2ecf20Sopenharmony_ci * We disable ECCER reporting due to... 2338c2ecf20Sopenharmony_ci * erratum IFC-A002770 -- so report it now if we 2348c2ecf20Sopenharmony_ci * see an uncorrectable error in ECCSTAT. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER; 2378c2ecf20Sopenharmony_ci continue; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += errors; 2418c2ecf20Sopenharmony_ci nctrl->max_bitflips = max_t(unsigned int, 2428c2ecf20Sopenharmony_ci nctrl->max_bitflips, 2438c2ecf20Sopenharmony_ci errors); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci nctrl->eccread = 0; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void fsl_ifc_do_read(struct nand_chip *chip, 2518c2ecf20Sopenharmony_ci int oob, 2528c2ecf20Sopenharmony_ci struct mtd_info *mtd) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 2558c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 2568c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ 2598c2ecf20Sopenharmony_ci if (mtd->writesize > 512) { 2608c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 2618c2ecf20Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | 2628c2ecf20Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | 2638c2ecf20Sopenharmony_ci (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | 2648c2ecf20Sopenharmony_ci (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), 2658c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 2668c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | 2698c2ecf20Sopenharmony_ci (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), 2708c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 2738c2ecf20Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | 2748c2ecf20Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | 2758c2ecf20Sopenharmony_ci (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), 2768c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 2778c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (oob) 2808c2ecf20Sopenharmony_ci ifc_out32(NAND_CMD_READOOB << 2818c2ecf20Sopenharmony_ci IFC_NAND_FCR0_CMD0_SHIFT, 2828c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci ifc_out32(NAND_CMD_READ0 << 2858c2ecf20Sopenharmony_ci IFC_NAND_FCR0_CMD0_SHIFT, 2868c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* cmdfunc send commands to the IFC NAND Machine */ 2918c2ecf20Sopenharmony_cistatic void fsl_ifc_cmdfunc(struct nand_chip *chip, unsigned int command, 2928c2ecf20Sopenharmony_ci int column, int page_addr) { 2938c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 2948c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 2958c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 2968c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* clear the read buffer */ 2998c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = 0; 3008c2ecf20Sopenharmony_ci if (command != NAND_CMD_PAGEPROG) 3018c2ecf20Sopenharmony_ci ifc_nand_ctrl->index = 0; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci switch (command) { 3048c2ecf20Sopenharmony_ci /* READ0 read the entire buffer to use hardware ECC. */ 3058c2ecf20Sopenharmony_ci case NAND_CMD_READ0: 3068c2ecf20Sopenharmony_ci ifc_out32(0, &ifc->ifc_nand.nand_fbcr); 3078c2ecf20Sopenharmony_ci set_addr(mtd, 0, page_addr, 0); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; 3108c2ecf20Sopenharmony_ci ifc_nand_ctrl->index += column; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) 3138c2ecf20Sopenharmony_ci ifc_nand_ctrl->eccread = 1; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci fsl_ifc_do_read(chip, 0, mtd); 3168c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 3178c2ecf20Sopenharmony_ci return; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* READOOB reads only the OOB because no ECC is performed. */ 3208c2ecf20Sopenharmony_ci case NAND_CMD_READOOB: 3218c2ecf20Sopenharmony_ci ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); 3228c2ecf20Sopenharmony_ci set_addr(mtd, column, page_addr, 1); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci fsl_ifc_do_read(chip, 1, mtd); 3278c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci case NAND_CMD_READID: 3328c2ecf20Sopenharmony_ci case NAND_CMD_PARAM: { 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * For READID, read 8 bytes that are currently used. 3358c2ecf20Sopenharmony_ci * For PARAM, read all 3 copies of 256-bytes pages. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci int len = 8; 3388c2ecf20Sopenharmony_ci int timing = IFC_FIR_OP_RB; 3398c2ecf20Sopenharmony_ci if (command == NAND_CMD_PARAM) { 3408c2ecf20Sopenharmony_ci timing = IFC_FIR_OP_RBCD; 3418c2ecf20Sopenharmony_ci len = 256 * 3; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 3458c2ecf20Sopenharmony_ci (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | 3468c2ecf20Sopenharmony_ci (timing << IFC_NAND_FIR0_OP2_SHIFT), 3478c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 3488c2ecf20Sopenharmony_ci ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, 3498c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 3508c2ecf20Sopenharmony_ci ifc_out32(column, &ifc->ifc_nand.row3); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ifc_out32(len, &ifc->ifc_nand.nand_fbcr); 3538c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = len; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci set_addr(mtd, 0, 0, 0); 3568c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 3578c2ecf20Sopenharmony_ci return; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* ERASE1 stores the block and page address */ 3618c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 3628c2ecf20Sopenharmony_ci set_addr(mtd, 0, page_addr, 0); 3638c2ecf20Sopenharmony_ci return; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* ERASE2 uses the block and page address from ERASE1 */ 3668c2ecf20Sopenharmony_ci case NAND_CMD_ERASE2: 3678c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 3688c2ecf20Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | 3698c2ecf20Sopenharmony_ci (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), 3708c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | 3738c2ecf20Sopenharmony_ci (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), 3748c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ifc_out32(0, &ifc->ifc_nand.nand_fbcr); 3778c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = 0; 3788c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* SEQIN sets up the addr buffer and all registers except the length */ 3828c2ecf20Sopenharmony_ci case NAND_CMD_SEQIN: { 3838c2ecf20Sopenharmony_ci u32 nand_fcr0; 3848c2ecf20Sopenharmony_ci ifc_nand_ctrl->column = column; 3858c2ecf20Sopenharmony_ci ifc_nand_ctrl->oob = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (mtd->writesize > 512) { 3888c2ecf20Sopenharmony_ci nand_fcr0 = 3898c2ecf20Sopenharmony_ci (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | 3908c2ecf20Sopenharmony_ci (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | 3918c2ecf20Sopenharmony_ci (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ifc_out32( 3948c2ecf20Sopenharmony_ci (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 3958c2ecf20Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | 3968c2ecf20Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | 3978c2ecf20Sopenharmony_ci (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | 3988c2ecf20Sopenharmony_ci (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), 3998c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 4008c2ecf20Sopenharmony_ci ifc_out32( 4018c2ecf20Sopenharmony_ci (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | 4028c2ecf20Sopenharmony_ci (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | 4038c2ecf20Sopenharmony_ci (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), 4048c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir1); 4058c2ecf20Sopenharmony_ci } else { 4068c2ecf20Sopenharmony_ci nand_fcr0 = ((NAND_CMD_PAGEPROG << 4078c2ecf20Sopenharmony_ci IFC_NAND_FCR0_CMD1_SHIFT) | 4088c2ecf20Sopenharmony_ci (NAND_CMD_SEQIN << 4098c2ecf20Sopenharmony_ci IFC_NAND_FCR0_CMD2_SHIFT) | 4108c2ecf20Sopenharmony_ci (NAND_CMD_STATUS << 4118c2ecf20Sopenharmony_ci IFC_NAND_FCR0_CMD3_SHIFT)); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ifc_out32( 4148c2ecf20Sopenharmony_ci (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 4158c2ecf20Sopenharmony_ci (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | 4168c2ecf20Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | 4178c2ecf20Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | 4188c2ecf20Sopenharmony_ci (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), 4198c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 4208c2ecf20Sopenharmony_ci ifc_out32( 4218c2ecf20Sopenharmony_ci (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | 4228c2ecf20Sopenharmony_ci (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | 4238c2ecf20Sopenharmony_ci (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | 4248c2ecf20Sopenharmony_ci (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), 4258c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir1); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (column >= mtd->writesize) 4288c2ecf20Sopenharmony_ci nand_fcr0 |= 4298c2ecf20Sopenharmony_ci NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; 4308c2ecf20Sopenharmony_ci else 4318c2ecf20Sopenharmony_ci nand_fcr0 |= 4328c2ecf20Sopenharmony_ci NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (column >= mtd->writesize) { 4368c2ecf20Sopenharmony_ci /* OOB area --> READOOB */ 4378c2ecf20Sopenharmony_ci column -= mtd->writesize; 4388c2ecf20Sopenharmony_ci ifc_nand_ctrl->oob = 1; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); 4418c2ecf20Sopenharmony_ci set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ 4468c2ecf20Sopenharmony_ci case NAND_CMD_PAGEPROG: { 4478c2ecf20Sopenharmony_ci if (ifc_nand_ctrl->oob) { 4488c2ecf20Sopenharmony_ci ifc_out32(ifc_nand_ctrl->index - 4498c2ecf20Sopenharmony_ci ifc_nand_ctrl->column, 4508c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fbcr); 4518c2ecf20Sopenharmony_ci } else { 4528c2ecf20Sopenharmony_ci ifc_out32(0, &ifc->ifc_nand.nand_fbcr); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 4568c2ecf20Sopenharmony_ci return; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: { 4608c2ecf20Sopenharmony_ci void __iomem *addr; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 4638c2ecf20Sopenharmony_ci (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), 4648c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 4658c2ecf20Sopenharmony_ci ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, 4668c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 4678c2ecf20Sopenharmony_ci ifc_out32(1, &ifc->ifc_nand.nand_fbcr); 4688c2ecf20Sopenharmony_ci set_addr(mtd, 0, 0, 0); 4698c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = 1; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * The chip always seems to report that it is 4758c2ecf20Sopenharmony_ci * write-protected, even when it is not. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci addr = ifc_nand_ctrl->addr; 4788c2ecf20Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 4798c2ecf20Sopenharmony_ci ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); 4808c2ecf20Sopenharmony_ci else 4818c2ecf20Sopenharmony_ci ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); 4828c2ecf20Sopenharmony_ci return; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci case NAND_CMD_RESET: 4868c2ecf20Sopenharmony_ci ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, 4878c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 4888c2ecf20Sopenharmony_ci ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, 4898c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 4908c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 4918c2ecf20Sopenharmony_ci return; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci default: 4948c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", 4958c2ecf20Sopenharmony_ci __func__, command); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic void fsl_ifc_select_chip(struct nand_chip *chip, int cs) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci /* The hardware does not seem to support multiple 5028c2ecf20Sopenharmony_ci * chips per bank. 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* 5078c2ecf20Sopenharmony_ci * Write buf to the IFC NAND Controller Data Buffer 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_cistatic void fsl_ifc_write_buf(struct nand_chip *chip, const u8 *buf, int len) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 5128c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 5138c2ecf20Sopenharmony_ci unsigned int bufsize = mtd->writesize + mtd->oobsize; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (len <= 0) { 5168c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: len %d bytes", __func__, len); 5178c2ecf20Sopenharmony_ci return; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { 5218c2ecf20Sopenharmony_ci dev_err(priv->dev, 5228c2ecf20Sopenharmony_ci "%s: beyond end of buffer (%d requested, %u available)\n", 5238c2ecf20Sopenharmony_ci __func__, len, bufsize - ifc_nand_ctrl->index); 5248c2ecf20Sopenharmony_ci len = bufsize - ifc_nand_ctrl->index; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len); 5288c2ecf20Sopenharmony_ci ifc_nand_ctrl->index += len; 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* 5328c2ecf20Sopenharmony_ci * Read a byte from either the IFC hardware buffer 5338c2ecf20Sopenharmony_ci * read function for 8-bit buswidth 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_cistatic uint8_t fsl_ifc_read_byte(struct nand_chip *chip) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 5388c2ecf20Sopenharmony_ci unsigned int offset; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* 5418c2ecf20Sopenharmony_ci * If there are still bytes in the IFC buffer, then use the 5428c2ecf20Sopenharmony_ci * next byte. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { 5458c2ecf20Sopenharmony_ci offset = ifc_nand_ctrl->index++; 5468c2ecf20Sopenharmony_ci return ifc_in8(ifc_nand_ctrl->addr + offset); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); 5508c2ecf20Sopenharmony_ci return ERR_BYTE; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci/* 5548c2ecf20Sopenharmony_ci * Read two bytes from the IFC hardware buffer 5558c2ecf20Sopenharmony_ci * read function for 16-bit buswith 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_cistatic uint8_t fsl_ifc_read_byte16(struct nand_chip *chip) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 5608c2ecf20Sopenharmony_ci uint16_t data; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* 5638c2ecf20Sopenharmony_ci * If there are still bytes in the IFC buffer, then use the 5648c2ecf20Sopenharmony_ci * next byte. 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { 5678c2ecf20Sopenharmony_ci data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); 5688c2ecf20Sopenharmony_ci ifc_nand_ctrl->index += 2; 5698c2ecf20Sopenharmony_ci return (uint8_t) data; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); 5738c2ecf20Sopenharmony_ci return ERR_BYTE; 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/* 5778c2ecf20Sopenharmony_ci * Read from the IFC Controller Data Buffer 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_cistatic void fsl_ifc_read_buf(struct nand_chip *chip, u8 *buf, int len) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 5828c2ecf20Sopenharmony_ci int avail; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (len < 0) { 5858c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: len %d bytes", __func__, len); 5868c2ecf20Sopenharmony_ci return; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci avail = min((unsigned int)len, 5908c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); 5918c2ecf20Sopenharmony_ci memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail); 5928c2ecf20Sopenharmony_ci ifc_nand_ctrl->index += avail; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (len > avail) 5958c2ecf20Sopenharmony_ci dev_err(priv->dev, 5968c2ecf20Sopenharmony_ci "%s: beyond end of buffer (%d requested, %d available)\n", 5978c2ecf20Sopenharmony_ci __func__, len, avail); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci/* 6018c2ecf20Sopenharmony_ci * This function is called after Program and Erase Operations to 6028c2ecf20Sopenharmony_ci * check for success or failure. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_cistatic int fsl_ifc_wait(struct nand_chip *chip) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6078c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 6088c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 6098c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 6108c2ecf20Sopenharmony_ci u32 nand_fsr; 6118c2ecf20Sopenharmony_ci int status; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* Use READ_STATUS command, but wait for the device to be ready */ 6148c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 6158c2ecf20Sopenharmony_ci (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), 6168c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 6178c2ecf20Sopenharmony_ci ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, 6188c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 6198c2ecf20Sopenharmony_ci ifc_out32(1, &ifc->ifc_nand.nand_fbcr); 6208c2ecf20Sopenharmony_ci set_addr(mtd, 0, 0, 0); 6218c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = 1; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci fsl_ifc_run_command(mtd); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); 6268c2ecf20Sopenharmony_ci status = nand_fsr >> 24; 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * The chip always seems to report that it is 6298c2ecf20Sopenharmony_ci * write-protected, even when it is not. 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci return status | NAND_STATUS_WP; 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/* 6358c2ecf20Sopenharmony_ci * The controller does not check for bitflips in erased pages, 6368c2ecf20Sopenharmony_ci * therefore software must check instead. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_cistatic int check_erased_page(struct nand_chip *chip, u8 *buf) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6418c2ecf20Sopenharmony_ci u8 *ecc = chip->oob_poi; 6428c2ecf20Sopenharmony_ci const int ecc_size = chip->ecc.bytes; 6438c2ecf20Sopenharmony_ci const int pkt_size = chip->ecc.size; 6448c2ecf20Sopenharmony_ci int i, res, bitflips = 0; 6458c2ecf20Sopenharmony_ci struct mtd_oob_region oobregion = { }; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &oobregion); 6488c2ecf20Sopenharmony_ci ecc += oobregion.offset; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci for (i = 0; i < chip->ecc.steps; ++i) { 6518c2ecf20Sopenharmony_ci res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, 6528c2ecf20Sopenharmony_ci NULL, 0, 6538c2ecf20Sopenharmony_ci chip->ecc.strength); 6548c2ecf20Sopenharmony_ci if (res < 0) 6558c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 6568c2ecf20Sopenharmony_ci else 6578c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += res; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci bitflips = max(res, bitflips); 6608c2ecf20Sopenharmony_ci buf += pkt_size; 6618c2ecf20Sopenharmony_ci ecc += ecc_size; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return bitflips; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf, 6688c2ecf20Sopenharmony_ci int oob_required, int page) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6718c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 6728c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 6738c2ecf20Sopenharmony_ci struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci nand_read_page_op(chip, page, 0, buf, mtd->writesize); 6768c2ecf20Sopenharmony_ci if (oob_required) 6778c2ecf20Sopenharmony_ci fsl_ifc_read_buf(chip, chip->oob_poi, mtd->oobsize); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) { 6808c2ecf20Sopenharmony_ci if (!oob_required) 6818c2ecf20Sopenharmony_ci fsl_ifc_read_buf(chip, chip->oob_poi, mtd->oobsize); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return check_erased_page(chip, buf); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) 6878c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return nctrl->max_bitflips; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in 6938c2ecf20Sopenharmony_ci * waitfunc. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_cistatic int fsl_ifc_write_page(struct nand_chip *chip, const uint8_t *buf, 6968c2ecf20Sopenharmony_ci int oob_required, int page) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); 7018c2ecf20Sopenharmony_ci fsl_ifc_write_buf(chip, chip->oob_poi, mtd->oobsize); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int fsl_ifc_attach_chip(struct nand_chip *chip) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7098c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 7108c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 7118c2ecf20Sopenharmony_ci struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; 7128c2ecf20Sopenharmony_ci u32 csor; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ 7178c2ecf20Sopenharmony_ci if (csor & CSOR_NAND_ECC_DEC_EN) { 7188c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 7198c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* Hardware generates ECC per 512 Bytes */ 7228c2ecf20Sopenharmony_ci chip->ecc.size = 512; 7238c2ecf20Sopenharmony_ci if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { 7248c2ecf20Sopenharmony_ci chip->ecc.bytes = 8; 7258c2ecf20Sopenharmony_ci chip->ecc.strength = 4; 7268c2ecf20Sopenharmony_ci } else { 7278c2ecf20Sopenharmony_ci chip->ecc.bytes = 16; 7288c2ecf20Sopenharmony_ci chip->ecc.strength = 8; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci } else { 7318c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 7328c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, 7368c2ecf20Sopenharmony_ci nanddev_ntargets(&chip->base)); 7378c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, 7388c2ecf20Sopenharmony_ci nanddev_target_size(&chip->base)); 7398c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, 7408c2ecf20Sopenharmony_ci chip->pagemask); 7418c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->legacy.chip_delay = %d\n", __func__, 7428c2ecf20Sopenharmony_ci chip->legacy.chip_delay); 7438c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, 7448c2ecf20Sopenharmony_ci chip->badblockpos); 7458c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, 7468c2ecf20Sopenharmony_ci chip->chip_shift); 7478c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, 7488c2ecf20Sopenharmony_ci chip->page_shift); 7498c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, 7508c2ecf20Sopenharmony_ci chip->phys_erase_shift); 7518c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.engine_type = %d\n", __func__, 7528c2ecf20Sopenharmony_ci chip->ecc.engine_type); 7538c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, 7548c2ecf20Sopenharmony_ci chip->ecc.steps); 7558c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, 7568c2ecf20Sopenharmony_ci chip->ecc.bytes); 7578c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, 7588c2ecf20Sopenharmony_ci chip->ecc.total); 7598c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__, 7608c2ecf20Sopenharmony_ci mtd->ooblayout); 7618c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); 7628c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); 7638c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, 7648c2ecf20Sopenharmony_ci mtd->erasesize); 7658c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, 7668c2ecf20Sopenharmony_ci mtd->writesize); 7678c2ecf20Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, 7688c2ecf20Sopenharmony_ci mtd->oobsize); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic const struct nand_controller_ops fsl_ifc_controller_ops = { 7748c2ecf20Sopenharmony_ci .attach_chip = fsl_ifc_attach_chip, 7758c2ecf20Sopenharmony_ci}; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_cistatic int fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 7808c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; 7818c2ecf20Sopenharmony_ci struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; 7828c2ecf20Sopenharmony_ci uint32_t csor = 0, csor_8k = 0, csor_ext = 0; 7838c2ecf20Sopenharmony_ci uint32_t cs = priv->bank; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (ctrl->version < FSL_IFC_VERSION_1_1_0) 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (ctrl->version > FSL_IFC_VERSION_1_1_0) { 7898c2ecf20Sopenharmony_ci u32 ncfgr, status; 7908c2ecf20Sopenharmony_ci int ret; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Trigger auto initialization */ 7938c2ecf20Sopenharmony_ci ncfgr = ifc_in32(&ifc_runtime->ifc_nand.ncfgr); 7948c2ecf20Sopenharmony_ci ifc_out32(ncfgr | IFC_NAND_NCFGR_SRAM_INIT_EN, &ifc_runtime->ifc_nand.ncfgr); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Wait until done */ 7978c2ecf20Sopenharmony_ci ret = readx_poll_timeout(ifc_in32, &ifc_runtime->ifc_nand.ncfgr, 7988c2ecf20Sopenharmony_ci status, !(status & IFC_NAND_NCFGR_SRAM_INIT_EN), 7998c2ecf20Sopenharmony_ci 10, IFC_TIMEOUT_MSECS * 1000); 8008c2ecf20Sopenharmony_ci if (ret) 8018c2ecf20Sopenharmony_ci dev_err(priv->dev, "Failed to initialize SRAM!\n"); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Save CSOR and CSOR_ext */ 8078c2ecf20Sopenharmony_ci csor = ifc_in32(&ifc_global->csor_cs[cs].csor); 8088c2ecf20Sopenharmony_ci csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* chage PageSize 8K and SpareSize 1K*/ 8118c2ecf20Sopenharmony_ci csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; 8128c2ecf20Sopenharmony_ci ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); 8138c2ecf20Sopenharmony_ci ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci /* READID */ 8168c2ecf20Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 8178c2ecf20Sopenharmony_ci (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | 8188c2ecf20Sopenharmony_ci (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), 8198c2ecf20Sopenharmony_ci &ifc_runtime->ifc_nand.nand_fir0); 8208c2ecf20Sopenharmony_ci ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, 8218c2ecf20Sopenharmony_ci &ifc_runtime->ifc_nand.nand_fcr0); 8228c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* Program ROW0/COL0 */ 8278c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); 8288c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* set the chip select for NAND Transaction */ 8318c2ecf20Sopenharmony_ci ifc_out32(cs << IFC_NAND_CSEL_SHIFT, 8328c2ecf20Sopenharmony_ci &ifc_runtime->ifc_nand.nand_csel); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci /* start read seq */ 8358c2ecf20Sopenharmony_ci ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, 8368c2ecf20Sopenharmony_ci &ifc_runtime->ifc_nand.nandseq_strt); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* wait for command complete flag or timeout */ 8398c2ecf20Sopenharmony_ci wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, 8408c2ecf20Sopenharmony_ci msecs_to_jiffies(IFC_TIMEOUT_MSECS)); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) { 8438c2ecf20Sopenharmony_ci pr_err("fsl-ifc: Failed to Initialise SRAM\n"); 8448c2ecf20Sopenharmony_ci return -ETIMEDOUT; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* Restore CSOR and CSOR_ext */ 8488c2ecf20Sopenharmony_ci ifc_out32(csor, &ifc_global->csor_cs[cs].csor); 8498c2ecf20Sopenharmony_ci ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return 0; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 8578c2ecf20Sopenharmony_ci struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; 8588c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; 8598c2ecf20Sopenharmony_ci struct nand_chip *chip = &priv->chip; 8608c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&priv->chip); 8618c2ecf20Sopenharmony_ci u32 csor; 8628c2ecf20Sopenharmony_ci int ret; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci /* Fill in fsl_ifc_mtd structure */ 8658c2ecf20Sopenharmony_ci mtd->dev.parent = priv->dev; 8668c2ecf20Sopenharmony_ci nand_set_flash_node(chip, priv->dev->of_node); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* fill in nand_chip structure */ 8698c2ecf20Sopenharmony_ci /* set up function call table */ 8708c2ecf20Sopenharmony_ci if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) 8718c2ecf20Sopenharmony_ci & CSPR_PORT_SIZE_16) 8728c2ecf20Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte16; 8738c2ecf20Sopenharmony_ci else 8748c2ecf20Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci chip->legacy.write_buf = fsl_ifc_write_buf; 8778c2ecf20Sopenharmony_ci chip->legacy.read_buf = fsl_ifc_read_buf; 8788c2ecf20Sopenharmony_ci chip->legacy.select_chip = fsl_ifc_select_chip; 8798c2ecf20Sopenharmony_ci chip->legacy.cmdfunc = fsl_ifc_cmdfunc; 8808c2ecf20Sopenharmony_ci chip->legacy.waitfunc = fsl_ifc_wait; 8818c2ecf20Sopenharmony_ci chip->legacy.set_features = nand_get_set_features_notsupp; 8828c2ecf20Sopenharmony_ci chip->legacy.get_features = nand_get_set_features_notsupp; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci chip->bbt_td = &bbt_main_descr; 8858c2ecf20Sopenharmony_ci chip->bbt_md = &bbt_mirror_descr; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci /* set up nand options */ 8908c2ecf20Sopenharmony_ci chip->bbt_options = NAND_BBT_USE_FLASH; 8918c2ecf20Sopenharmony_ci chip->options = NAND_NO_SUBPAGE_WRITE; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) 8948c2ecf20Sopenharmony_ci & CSPR_PORT_SIZE_16) { 8958c2ecf20Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte16; 8968c2ecf20Sopenharmony_ci chip->options |= NAND_BUSWIDTH_16; 8978c2ecf20Sopenharmony_ci } else { 8988c2ecf20Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci chip->controller = &ifc_nand_ctrl->controller; 9028c2ecf20Sopenharmony_ci nand_set_controller_data(chip, priv); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci chip->ecc.read_page = fsl_ifc_read_page; 9058c2ecf20Sopenharmony_ci chip->ecc.write_page = fsl_ifc_write_page; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci switch (csor & CSOR_NAND_PGS_MASK) { 9108c2ecf20Sopenharmony_ci case CSOR_NAND_PGS_512: 9118c2ecf20Sopenharmony_ci if (!(chip->options & NAND_BUSWIDTH_16)) { 9128c2ecf20Sopenharmony_ci /* Avoid conflict with bad block marker */ 9138c2ecf20Sopenharmony_ci bbt_main_descr.offs = 0; 9148c2ecf20Sopenharmony_ci bbt_mirror_descr.offs = 0; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci priv->bufnum_mask = 15; 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci case CSOR_NAND_PGS_2K: 9218c2ecf20Sopenharmony_ci priv->bufnum_mask = 3; 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci case CSOR_NAND_PGS_4K: 9258c2ecf20Sopenharmony_ci priv->bufnum_mask = 1; 9268c2ecf20Sopenharmony_ci break; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci case CSOR_NAND_PGS_8K: 9298c2ecf20Sopenharmony_ci priv->bufnum_mask = 0; 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci default: 9338c2ecf20Sopenharmony_ci dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); 9348c2ecf20Sopenharmony_ci return -ENODEV; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci ret = fsl_ifc_sram_init(priv); 9388c2ecf20Sopenharmony_ci if (ret) 9398c2ecf20Sopenharmony_ci return ret; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* 9428c2ecf20Sopenharmony_ci * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older 9438c2ecf20Sopenharmony_ci * versions which had 8KB. Hence bufnum mask needs to be updated. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci if (ctrl->version >= FSL_IFC_VERSION_2_0_0) 9468c2ecf20Sopenharmony_ci priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci return 0; 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&priv->chip); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci kfree(mtd->name); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (priv->vbase) 9588c2ecf20Sopenharmony_ci iounmap(priv->vbase); 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci ifc_nand_ctrl->chips[priv->bank] = NULL; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci return 0; 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, 9668c2ecf20Sopenharmony_ci phys_addr_t addr) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (!(cspr & CSPR_V)) 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) 9738c2ecf20Sopenharmony_ci return 0; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return (cspr & CSPR_BA) == convert_ifc_address(addr); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(fsl_ifc_nand_mutex); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int fsl_ifc_nand_probe(struct platform_device *dev) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc; 9838c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv; 9848c2ecf20Sopenharmony_ci struct resource res; 9858c2ecf20Sopenharmony_ci static const char *part_probe_types[] 9868c2ecf20Sopenharmony_ci = { "cmdlinepart", "RedBoot", "ofpart", NULL }; 9878c2ecf20Sopenharmony_ci int ret; 9888c2ecf20Sopenharmony_ci int bank; 9898c2ecf20Sopenharmony_ci struct device_node *node = dev->dev.of_node; 9908c2ecf20Sopenharmony_ci struct mtd_info *mtd; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) 9938c2ecf20Sopenharmony_ci return -ENODEV; 9948c2ecf20Sopenharmony_ci ifc = fsl_ifc_ctrl_dev->rregs; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* get, allocate and map the memory resource */ 9978c2ecf20Sopenharmony_ci ret = of_address_to_resource(node, 0, &res); 9988c2ecf20Sopenharmony_ci if (ret) { 9998c2ecf20Sopenharmony_ci dev_err(&dev->dev, "%s: failed to get resource\n", __func__); 10008c2ecf20Sopenharmony_ci return ret; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* find which chip select it is connected to */ 10048c2ecf20Sopenharmony_ci for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { 10058c2ecf20Sopenharmony_ci if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (bank >= fsl_ifc_ctrl_dev->banks) { 10108c2ecf20Sopenharmony_ci dev_err(&dev->dev, "%s: address did not match any chip selects\n", 10118c2ecf20Sopenharmony_ci __func__); 10128c2ecf20Sopenharmony_ci return -ENODEV; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); 10168c2ecf20Sopenharmony_ci if (!priv) 10178c2ecf20Sopenharmony_ci return -ENOMEM; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci mutex_lock(&fsl_ifc_nand_mutex); 10208c2ecf20Sopenharmony_ci if (!fsl_ifc_ctrl_dev->nand) { 10218c2ecf20Sopenharmony_ci ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); 10228c2ecf20Sopenharmony_ci if (!ifc_nand_ctrl) { 10238c2ecf20Sopenharmony_ci mutex_unlock(&fsl_ifc_nand_mutex); 10248c2ecf20Sopenharmony_ci return -ENOMEM; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci ifc_nand_ctrl->read_bytes = 0; 10288c2ecf20Sopenharmony_ci ifc_nand_ctrl->index = 0; 10298c2ecf20Sopenharmony_ci ifc_nand_ctrl->addr = NULL; 10308c2ecf20Sopenharmony_ci fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci nand_controller_init(&ifc_nand_ctrl->controller); 10338c2ecf20Sopenharmony_ci } else { 10348c2ecf20Sopenharmony_ci ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci mutex_unlock(&fsl_ifc_nand_mutex); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ifc_nand_ctrl->chips[bank] = priv; 10398c2ecf20Sopenharmony_ci priv->bank = bank; 10408c2ecf20Sopenharmony_ci priv->ctrl = fsl_ifc_ctrl_dev; 10418c2ecf20Sopenharmony_ci priv->dev = &dev->dev; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci priv->vbase = ioremap(res.start, resource_size(&res)); 10448c2ecf20Sopenharmony_ci if (!priv->vbase) { 10458c2ecf20Sopenharmony_ci dev_err(priv->dev, "%s: failed to map chip region\n", __func__); 10468c2ecf20Sopenharmony_ci ret = -ENOMEM; 10478c2ecf20Sopenharmony_ci goto err; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci dev_set_drvdata(priv->dev, priv); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | 10538c2ecf20Sopenharmony_ci IFC_NAND_EVTER_EN_FTOER_EN | 10548c2ecf20Sopenharmony_ci IFC_NAND_EVTER_EN_WPER_EN, 10558c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_evter_en); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* enable NAND Machine Interrupts */ 10588c2ecf20Sopenharmony_ci ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | 10598c2ecf20Sopenharmony_ci IFC_NAND_EVTER_INTR_FTOERIR_EN | 10608c2ecf20Sopenharmony_ci IFC_NAND_EVTER_INTR_WPERIR_EN, 10618c2ecf20Sopenharmony_ci &ifc->ifc_nand.nand_evter_intr_en); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci mtd = nand_to_mtd(&priv->chip); 10648c2ecf20Sopenharmony_ci mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); 10658c2ecf20Sopenharmony_ci if (!mtd->name) { 10668c2ecf20Sopenharmony_ci ret = -ENOMEM; 10678c2ecf20Sopenharmony_ci goto err; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci ret = fsl_ifc_chip_init(priv); 10718c2ecf20Sopenharmony_ci if (ret) 10728c2ecf20Sopenharmony_ci goto err; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci priv->chip.controller->ops = &fsl_ifc_controller_ops; 10758c2ecf20Sopenharmony_ci ret = nand_scan(&priv->chip, 1); 10768c2ecf20Sopenharmony_ci if (ret) 10778c2ecf20Sopenharmony_ci goto err; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* First look for RedBoot table or partitions on the command 10808c2ecf20Sopenharmony_ci * line, these take precedence over device tree information */ 10818c2ecf20Sopenharmony_ci ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); 10828c2ecf20Sopenharmony_ci if (ret) 10838c2ecf20Sopenharmony_ci goto cleanup_nand; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", 10868c2ecf20Sopenharmony_ci (unsigned long long)res.start, priv->bank); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return 0; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cicleanup_nand: 10918c2ecf20Sopenharmony_ci nand_cleanup(&priv->chip); 10928c2ecf20Sopenharmony_cierr: 10938c2ecf20Sopenharmony_ci fsl_ifc_chip_remove(priv); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci return ret; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic int fsl_ifc_nand_remove(struct platform_device *dev) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); 11018c2ecf20Sopenharmony_ci struct nand_chip *chip = &priv->chip; 11028c2ecf20Sopenharmony_ci int ret; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 11058c2ecf20Sopenharmony_ci WARN_ON(ret); 11068c2ecf20Sopenharmony_ci nand_cleanup(chip); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci fsl_ifc_chip_remove(priv); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci mutex_lock(&fsl_ifc_nand_mutex); 11118c2ecf20Sopenharmony_ci ifc_nand_ctrl->counter--; 11128c2ecf20Sopenharmony_ci if (!ifc_nand_ctrl->counter) { 11138c2ecf20Sopenharmony_ci fsl_ifc_ctrl_dev->nand = NULL; 11148c2ecf20Sopenharmony_ci kfree(ifc_nand_ctrl); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci mutex_unlock(&fsl_ifc_nand_mutex); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic const struct of_device_id fsl_ifc_nand_match[] = { 11228c2ecf20Sopenharmony_ci { 11238c2ecf20Sopenharmony_ci .compatible = "fsl,ifc-nand", 11248c2ecf20Sopenharmony_ci }, 11258c2ecf20Sopenharmony_ci {} 11268c2ecf20Sopenharmony_ci}; 11278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic struct platform_driver fsl_ifc_nand_driver = { 11308c2ecf20Sopenharmony_ci .driver = { 11318c2ecf20Sopenharmony_ci .name = "fsl,ifc-nand", 11328c2ecf20Sopenharmony_ci .of_match_table = fsl_ifc_nand_match, 11338c2ecf20Sopenharmony_ci }, 11348c2ecf20Sopenharmony_ci .probe = fsl_ifc_nand_probe, 11358c2ecf20Sopenharmony_ci .remove = fsl_ifc_nand_remove, 11368c2ecf20Sopenharmony_ci}; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cimodule_platform_driver(fsl_ifc_nand_driver); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale"); 11428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); 1143