162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale Integrated Flash Controller NAND driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011-2012 Freescale Semiconductor, Inc 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Dipen Dudhat <Dipen.Dudhat@freescale.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1762306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 1862306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1962306a36Sopenharmony_ci#include <linux/fsl_ifc.h> 2062306a36Sopenharmony_ci#include <linux/iopoll.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define ERR_BYTE 0xFF /* Value returned for read 2362306a36Sopenharmony_ci bytes when read failed */ 2462306a36Sopenharmony_ci#define IFC_TIMEOUT_MSECS 1000 /* Maximum timeout to wait 2562306a36Sopenharmony_ci for IFC NAND Machine */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct fsl_ifc_ctrl; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* mtd information per set */ 3062306a36Sopenharmony_cistruct fsl_ifc_mtd { 3162306a36Sopenharmony_ci struct nand_chip chip; 3262306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci struct device *dev; 3562306a36Sopenharmony_ci int bank; /* Chip select bank number */ 3662306a36Sopenharmony_ci unsigned int bufnum_mask; /* bufnum = page & bufnum_mask */ 3762306a36Sopenharmony_ci u8 __iomem *vbase; /* Chip select base virtual address */ 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* overview of the fsl ifc controller */ 4162306a36Sopenharmony_cistruct fsl_ifc_nand_ctrl { 4262306a36Sopenharmony_ci struct nand_controller controller; 4362306a36Sopenharmony_ci struct fsl_ifc_mtd *chips[FSL_IFC_BANK_COUNT]; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci void __iomem *addr; /* Address of assigned IFC buffer */ 4662306a36Sopenharmony_ci unsigned int page; /* Last page written to / read from */ 4762306a36Sopenharmony_ci unsigned int read_bytes;/* Number of bytes read during command */ 4862306a36Sopenharmony_ci unsigned int column; /* Saved column from SEQIN */ 4962306a36Sopenharmony_ci unsigned int index; /* Pointer to next byte to 'read' */ 5062306a36Sopenharmony_ci unsigned int oob; /* Non zero if operating on OOB data */ 5162306a36Sopenharmony_ci unsigned int eccread; /* Non zero for a full-page ECC read */ 5262306a36Sopenharmony_ci unsigned int counter; /* counter for the initializations */ 5362306a36Sopenharmony_ci unsigned int max_bitflips; /* Saved during READ0 cmd */ 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic struct fsl_ifc_nand_ctrl *ifc_nand_ctrl; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Generic flash bbt descriptors 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic u8 bbt_pattern[] = {'B', 'b', 't', '0' }; 6262306a36Sopenharmony_cistatic u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = { 6562306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | 6662306a36Sopenharmony_ci NAND_BBT_2BIT | NAND_BBT_VERSION, 6762306a36Sopenharmony_ci .offs = 2, /* 0 on 8-bit small page */ 6862306a36Sopenharmony_ci .len = 4, 6962306a36Sopenharmony_ci .veroffs = 6, 7062306a36Sopenharmony_ci .maxblocks = 4, 7162306a36Sopenharmony_ci .pattern = bbt_pattern, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = { 7562306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | 7662306a36Sopenharmony_ci NAND_BBT_2BIT | NAND_BBT_VERSION, 7762306a36Sopenharmony_ci .offs = 2, /* 0 on 8-bit small page */ 7862306a36Sopenharmony_ci .len = 4, 7962306a36Sopenharmony_ci .veroffs = 6, 8062306a36Sopenharmony_ci .maxblocks = 4, 8162306a36Sopenharmony_ci .pattern = mirror_pattern, 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int fsl_ifc_ooblayout_ecc(struct mtd_info *mtd, int section, 8562306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (section) 9062306a36Sopenharmony_ci return -ERANGE; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci oobregion->offset = 8; 9362306a36Sopenharmony_ci oobregion->length = chip->ecc.total; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int fsl_ifc_ooblayout_free(struct mtd_info *mtd, int section, 9962306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (section > 1) 10462306a36Sopenharmony_ci return -ERANGE; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (mtd->writesize == 512 && 10762306a36Sopenharmony_ci !(chip->options & NAND_BUSWIDTH_16)) { 10862306a36Sopenharmony_ci if (!section) { 10962306a36Sopenharmony_ci oobregion->offset = 0; 11062306a36Sopenharmony_ci oobregion->length = 5; 11162306a36Sopenharmony_ci } else { 11262306a36Sopenharmony_ci oobregion->offset = 6; 11362306a36Sopenharmony_ci oobregion->length = 2; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!section) { 12062306a36Sopenharmony_ci oobregion->offset = 2; 12162306a36Sopenharmony_ci oobregion->length = 6; 12262306a36Sopenharmony_ci } else { 12362306a36Sopenharmony_ci oobregion->offset = chip->ecc.total + 8; 12462306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsl_ifc_ooblayout_ops = { 13162306a36Sopenharmony_ci .ecc = fsl_ifc_ooblayout_ecc, 13262306a36Sopenharmony_ci .free = fsl_ifc_ooblayout_free, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Set up the IFC hardware block and page address fields, and the ifc nand 13762306a36Sopenharmony_ci * structure addr field to point to the correct IFC buffer in memory 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 14262306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 14362306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 14462306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 14562306a36Sopenharmony_ci int buf_num; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ifc_nand_ctrl->page = page_addr; 14862306a36Sopenharmony_ci /* Program ROW0/COL0 */ 14962306a36Sopenharmony_ci ifc_out32(page_addr, &ifc->ifc_nand.row0); 15062306a36Sopenharmony_ci ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci buf_num = page_addr & priv->bufnum_mask; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ifc_nand_ctrl->addr = priv->vbase + buf_num * (mtd->writesize * 2); 15562306a36Sopenharmony_ci ifc_nand_ctrl->index = column; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* for OOB data point to the second half of the buffer */ 15862306a36Sopenharmony_ci if (oob) 15962306a36Sopenharmony_ci ifc_nand_ctrl->index += mtd->writesize; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* returns nonzero if entire page is blank */ 16362306a36Sopenharmony_cistatic int check_read_ecc(struct mtd_info *mtd, struct fsl_ifc_ctrl *ctrl, 16462306a36Sopenharmony_ci u32 eccstat, unsigned int bufnum) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci return (eccstat >> ((3 - bufnum % 4) * 8)) & 15; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * execute IFC NAND command and wait for it to complete 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic void fsl_ifc_run_command(struct mtd_info *mtd) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 17562306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 17662306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 17762306a36Sopenharmony_ci struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; 17862306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 17962306a36Sopenharmony_ci u32 eccstat; 18062306a36Sopenharmony_ci int i; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* set the chip select for NAND Transaction */ 18362306a36Sopenharmony_ci ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, 18462306a36Sopenharmony_ci &ifc->ifc_nand.nand_csel); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dev_vdbg(priv->dev, 18762306a36Sopenharmony_ci "%s: fir0=%08x fcr0=%08x\n", 18862306a36Sopenharmony_ci __func__, 18962306a36Sopenharmony_ci ifc_in32(&ifc->ifc_nand.nand_fir0), 19062306a36Sopenharmony_ci ifc_in32(&ifc->ifc_nand.nand_fcr0)); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci ctrl->nand_stat = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* start read/write seq */ 19562306a36Sopenharmony_ci ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* wait for command complete flag or timeout */ 19862306a36Sopenharmony_ci wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, 19962306a36Sopenharmony_ci msecs_to_jiffies(IFC_TIMEOUT_MSECS)); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* ctrl->nand_stat will be updated from IRQ context */ 20262306a36Sopenharmony_ci if (!ctrl->nand_stat) 20362306a36Sopenharmony_ci dev_err(priv->dev, "Controller is not responding\n"); 20462306a36Sopenharmony_ci if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_FTOER) 20562306a36Sopenharmony_ci dev_err(priv->dev, "NAND Flash Timeout Error\n"); 20662306a36Sopenharmony_ci if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_WPER) 20762306a36Sopenharmony_ci dev_err(priv->dev, "NAND Flash Write Protect Error\n"); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci nctrl->max_bitflips = 0; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (nctrl->eccread) { 21262306a36Sopenharmony_ci int errors; 21362306a36Sopenharmony_ci int bufnum = nctrl->page & priv->bufnum_mask; 21462306a36Sopenharmony_ci int sector_start = bufnum * chip->ecc.steps; 21562306a36Sopenharmony_ci int sector_end = sector_start + chip->ecc.steps - 1; 21662306a36Sopenharmony_ci __be32 __iomem *eccstat_regs; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci eccstat_regs = ifc->ifc_nand.nand_eccstat; 21962306a36Sopenharmony_ci eccstat = ifc_in32(&eccstat_regs[sector_start / 4]); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for (i = sector_start; i <= sector_end; i++) { 22262306a36Sopenharmony_ci if (i != sector_start && !(i % 4)) 22362306a36Sopenharmony_ci eccstat = ifc_in32(&eccstat_regs[i / 4]); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci errors = check_read_ecc(mtd, ctrl, eccstat, i); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (errors == 15) { 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Uncorrectable error. 23062306a36Sopenharmony_ci * We'll check for blank pages later. 23162306a36Sopenharmony_ci * 23262306a36Sopenharmony_ci * We disable ECCER reporting due to... 23362306a36Sopenharmony_ci * erratum IFC-A002770 -- so report it now if we 23462306a36Sopenharmony_ci * see an uncorrectable error in ECCSTAT. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci ctrl->nand_stat |= IFC_NAND_EVTER_STAT_ECCER; 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mtd->ecc_stats.corrected += errors; 24162306a36Sopenharmony_ci nctrl->max_bitflips = max_t(unsigned int, 24262306a36Sopenharmony_ci nctrl->max_bitflips, 24362306a36Sopenharmony_ci errors); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci nctrl->eccread = 0; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void fsl_ifc_do_read(struct nand_chip *chip, 25162306a36Sopenharmony_ci int oob, 25262306a36Sopenharmony_ci struct mtd_info *mtd) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 25562306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 25662306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ 25962306a36Sopenharmony_ci if (mtd->writesize > 512) { 26062306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 26162306a36Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | 26262306a36Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | 26362306a36Sopenharmony_ci (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | 26462306a36Sopenharmony_ci (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), 26562306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 26662306a36Sopenharmony_ci ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | 26962306a36Sopenharmony_ci (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), 27062306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 27362306a36Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | 27462306a36Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | 27562306a36Sopenharmony_ci (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), 27662306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 27762306a36Sopenharmony_ci ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (oob) 28062306a36Sopenharmony_ci ifc_out32(NAND_CMD_READOOB << 28162306a36Sopenharmony_ci IFC_NAND_FCR0_CMD0_SHIFT, 28262306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci ifc_out32(NAND_CMD_READ0 << 28562306a36Sopenharmony_ci IFC_NAND_FCR0_CMD0_SHIFT, 28662306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* cmdfunc send commands to the IFC NAND Machine */ 29162306a36Sopenharmony_cistatic void fsl_ifc_cmdfunc(struct nand_chip *chip, unsigned int command, 29262306a36Sopenharmony_ci int column, int page_addr) { 29362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 29462306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 29562306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 29662306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* clear the read buffer */ 29962306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = 0; 30062306a36Sopenharmony_ci if (command != NAND_CMD_PAGEPROG) 30162306a36Sopenharmony_ci ifc_nand_ctrl->index = 0; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci switch (command) { 30462306a36Sopenharmony_ci /* READ0 read the entire buffer to use hardware ECC. */ 30562306a36Sopenharmony_ci case NAND_CMD_READ0: 30662306a36Sopenharmony_ci ifc_out32(0, &ifc->ifc_nand.nand_fbcr); 30762306a36Sopenharmony_ci set_addr(mtd, 0, page_addr, 0); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; 31062306a36Sopenharmony_ci ifc_nand_ctrl->index += column; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) 31362306a36Sopenharmony_ci ifc_nand_ctrl->eccread = 1; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci fsl_ifc_do_read(chip, 0, mtd); 31662306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 31762306a36Sopenharmony_ci return; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* READOOB reads only the OOB because no ECC is performed. */ 32062306a36Sopenharmony_ci case NAND_CMD_READOOB: 32162306a36Sopenharmony_ci ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); 32262306a36Sopenharmony_ci set_addr(mtd, column, page_addr, 1); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci fsl_ifc_do_read(chip, 1, mtd); 32762306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci case NAND_CMD_READID: 33262306a36Sopenharmony_ci case NAND_CMD_PARAM: { 33362306a36Sopenharmony_ci /* 33462306a36Sopenharmony_ci * For READID, read 8 bytes that are currently used. 33562306a36Sopenharmony_ci * For PARAM, read all 3 copies of 256-bytes pages. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci int len = 8; 33862306a36Sopenharmony_ci int timing = IFC_FIR_OP_RB; 33962306a36Sopenharmony_ci if (command == NAND_CMD_PARAM) { 34062306a36Sopenharmony_ci timing = IFC_FIR_OP_RBCD; 34162306a36Sopenharmony_ci len = 256 * 3; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 34562306a36Sopenharmony_ci (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | 34662306a36Sopenharmony_ci (timing << IFC_NAND_FIR0_OP2_SHIFT), 34762306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 34862306a36Sopenharmony_ci ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, 34962306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 35062306a36Sopenharmony_ci ifc_out32(column, &ifc->ifc_nand.row3); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ifc_out32(len, &ifc->ifc_nand.nand_fbcr); 35362306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = len; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci set_addr(mtd, 0, 0, 0); 35662306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* ERASE1 stores the block and page address */ 36162306a36Sopenharmony_ci case NAND_CMD_ERASE1: 36262306a36Sopenharmony_ci set_addr(mtd, 0, page_addr, 0); 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci /* ERASE2 uses the block and page address from ERASE1 */ 36662306a36Sopenharmony_ci case NAND_CMD_ERASE2: 36762306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 36862306a36Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | 36962306a36Sopenharmony_ci (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), 37062306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | 37362306a36Sopenharmony_ci (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), 37462306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci ifc_out32(0, &ifc->ifc_nand.nand_fbcr); 37762306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = 0; 37862306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 37962306a36Sopenharmony_ci return; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* SEQIN sets up the addr buffer and all registers except the length */ 38262306a36Sopenharmony_ci case NAND_CMD_SEQIN: { 38362306a36Sopenharmony_ci u32 nand_fcr0; 38462306a36Sopenharmony_ci ifc_nand_ctrl->column = column; 38562306a36Sopenharmony_ci ifc_nand_ctrl->oob = 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (mtd->writesize > 512) { 38862306a36Sopenharmony_ci nand_fcr0 = 38962306a36Sopenharmony_ci (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | 39062306a36Sopenharmony_ci (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | 39162306a36Sopenharmony_ci (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci ifc_out32( 39462306a36Sopenharmony_ci (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 39562306a36Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | 39662306a36Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | 39762306a36Sopenharmony_ci (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | 39862306a36Sopenharmony_ci (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), 39962306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 40062306a36Sopenharmony_ci ifc_out32( 40162306a36Sopenharmony_ci (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | 40262306a36Sopenharmony_ci (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | 40362306a36Sopenharmony_ci (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), 40462306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir1); 40562306a36Sopenharmony_ci } else { 40662306a36Sopenharmony_ci nand_fcr0 = ((NAND_CMD_PAGEPROG << 40762306a36Sopenharmony_ci IFC_NAND_FCR0_CMD1_SHIFT) | 40862306a36Sopenharmony_ci (NAND_CMD_SEQIN << 40962306a36Sopenharmony_ci IFC_NAND_FCR0_CMD2_SHIFT) | 41062306a36Sopenharmony_ci (NAND_CMD_STATUS << 41162306a36Sopenharmony_ci IFC_NAND_FCR0_CMD3_SHIFT)); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ifc_out32( 41462306a36Sopenharmony_ci (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 41562306a36Sopenharmony_ci (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | 41662306a36Sopenharmony_ci (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | 41762306a36Sopenharmony_ci (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | 41862306a36Sopenharmony_ci (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), 41962306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 42062306a36Sopenharmony_ci ifc_out32( 42162306a36Sopenharmony_ci (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | 42262306a36Sopenharmony_ci (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | 42362306a36Sopenharmony_ci (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | 42462306a36Sopenharmony_ci (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), 42562306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir1); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (column >= mtd->writesize) 42862306a36Sopenharmony_ci nand_fcr0 |= 42962306a36Sopenharmony_ci NAND_CMD_READOOB << IFC_NAND_FCR0_CMD0_SHIFT; 43062306a36Sopenharmony_ci else 43162306a36Sopenharmony_ci nand_fcr0 |= 43262306a36Sopenharmony_ci NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (column >= mtd->writesize) { 43662306a36Sopenharmony_ci /* OOB area --> READOOB */ 43762306a36Sopenharmony_ci column -= mtd->writesize; 43862306a36Sopenharmony_ci ifc_nand_ctrl->oob = 1; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); 44162306a36Sopenharmony_ci set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); 44262306a36Sopenharmony_ci return; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ 44662306a36Sopenharmony_ci case NAND_CMD_PAGEPROG: { 44762306a36Sopenharmony_ci if (ifc_nand_ctrl->oob) { 44862306a36Sopenharmony_ci ifc_out32(ifc_nand_ctrl->index - 44962306a36Sopenharmony_ci ifc_nand_ctrl->column, 45062306a36Sopenharmony_ci &ifc->ifc_nand.nand_fbcr); 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci ifc_out32(0, &ifc->ifc_nand.nand_fbcr); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 45662306a36Sopenharmony_ci return; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci case NAND_CMD_STATUS: { 46062306a36Sopenharmony_ci void __iomem *addr; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 46362306a36Sopenharmony_ci (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), 46462306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 46562306a36Sopenharmony_ci ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, 46662306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 46762306a36Sopenharmony_ci ifc_out32(1, &ifc->ifc_nand.nand_fbcr); 46862306a36Sopenharmony_ci set_addr(mtd, 0, 0, 0); 46962306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = 1; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * The chip always seems to report that it is 47562306a36Sopenharmony_ci * write-protected, even when it is not. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci addr = ifc_nand_ctrl->addr; 47862306a36Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 47962306a36Sopenharmony_ci ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); 48062306a36Sopenharmony_ci else 48162306a36Sopenharmony_ci ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); 48262306a36Sopenharmony_ci return; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci case NAND_CMD_RESET: 48662306a36Sopenharmony_ci ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, 48762306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 48862306a36Sopenharmony_ci ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, 48962306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 49062306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 49162306a36Sopenharmony_ci return; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci default: 49462306a36Sopenharmony_ci dev_err(priv->dev, "%s: error, unsupported command 0x%x.\n", 49562306a36Sopenharmony_ci __func__, command); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic void fsl_ifc_select_chip(struct nand_chip *chip, int cs) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci /* The hardware does not seem to support multiple 50262306a36Sopenharmony_ci * chips per bank. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * Write buf to the IFC NAND Controller Data Buffer 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_cistatic void fsl_ifc_write_buf(struct nand_chip *chip, const u8 *buf, int len) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 51262306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 51362306a36Sopenharmony_ci unsigned int bufsize = mtd->writesize + mtd->oobsize; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (len <= 0) { 51662306a36Sopenharmony_ci dev_err(priv->dev, "%s: len %d bytes", __func__, len); 51762306a36Sopenharmony_ci return; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if ((unsigned int)len > bufsize - ifc_nand_ctrl->index) { 52162306a36Sopenharmony_ci dev_err(priv->dev, 52262306a36Sopenharmony_ci "%s: beyond end of buffer (%d requested, %u available)\n", 52362306a36Sopenharmony_ci __func__, len, bufsize - ifc_nand_ctrl->index); 52462306a36Sopenharmony_ci len = bufsize - ifc_nand_ctrl->index; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci memcpy_toio(ifc_nand_ctrl->addr + ifc_nand_ctrl->index, buf, len); 52862306a36Sopenharmony_ci ifc_nand_ctrl->index += len; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/* 53262306a36Sopenharmony_ci * Read a byte from either the IFC hardware buffer 53362306a36Sopenharmony_ci * read function for 8-bit buswidth 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_cistatic uint8_t fsl_ifc_read_byte(struct nand_chip *chip) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 53862306a36Sopenharmony_ci unsigned int offset; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* 54162306a36Sopenharmony_ci * If there are still bytes in the IFC buffer, then use the 54262306a36Sopenharmony_ci * next byte. 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_ci if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { 54562306a36Sopenharmony_ci offset = ifc_nand_ctrl->index++; 54662306a36Sopenharmony_ci return ifc_in8(ifc_nand_ctrl->addr + offset); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); 55062306a36Sopenharmony_ci return ERR_BYTE; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/* 55462306a36Sopenharmony_ci * Read two bytes from the IFC hardware buffer 55562306a36Sopenharmony_ci * read function for 16-bit buswith 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_cistatic uint8_t fsl_ifc_read_byte16(struct nand_chip *chip) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 56062306a36Sopenharmony_ci uint16_t data; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * If there are still bytes in the IFC buffer, then use the 56462306a36Sopenharmony_ci * next byte. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { 56762306a36Sopenharmony_ci data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); 56862306a36Sopenharmony_ci ifc_nand_ctrl->index += 2; 56962306a36Sopenharmony_ci return (uint8_t) data; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); 57362306a36Sopenharmony_ci return ERR_BYTE; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* 57762306a36Sopenharmony_ci * Read from the IFC Controller Data Buffer 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_cistatic void fsl_ifc_read_buf(struct nand_chip *chip, u8 *buf, int len) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 58262306a36Sopenharmony_ci int avail; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (len < 0) { 58562306a36Sopenharmony_ci dev_err(priv->dev, "%s: len %d bytes", __func__, len); 58662306a36Sopenharmony_ci return; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci avail = min((unsigned int)len, 59062306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes - ifc_nand_ctrl->index); 59162306a36Sopenharmony_ci memcpy_fromio(buf, ifc_nand_ctrl->addr + ifc_nand_ctrl->index, avail); 59262306a36Sopenharmony_ci ifc_nand_ctrl->index += avail; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (len > avail) 59562306a36Sopenharmony_ci dev_err(priv->dev, 59662306a36Sopenharmony_ci "%s: beyond end of buffer (%d requested, %d available)\n", 59762306a36Sopenharmony_ci __func__, len, avail); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* 60162306a36Sopenharmony_ci * This function is called after Program and Erase Operations to 60262306a36Sopenharmony_ci * check for success or failure. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_cistatic int fsl_ifc_wait(struct nand_chip *chip) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 60762306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 60862306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 60962306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc = ctrl->rregs; 61062306a36Sopenharmony_ci u32 nand_fsr; 61162306a36Sopenharmony_ci int status; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* Use READ_STATUS command, but wait for the device to be ready */ 61462306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 61562306a36Sopenharmony_ci (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), 61662306a36Sopenharmony_ci &ifc->ifc_nand.nand_fir0); 61762306a36Sopenharmony_ci ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, 61862306a36Sopenharmony_ci &ifc->ifc_nand.nand_fcr0); 61962306a36Sopenharmony_ci ifc_out32(1, &ifc->ifc_nand.nand_fbcr); 62062306a36Sopenharmony_ci set_addr(mtd, 0, 0, 0); 62162306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = 1; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci fsl_ifc_run_command(mtd); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); 62662306a36Sopenharmony_ci status = nand_fsr >> 24; 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * The chip always seems to report that it is 62962306a36Sopenharmony_ci * write-protected, even when it is not. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci return status | NAND_STATUS_WP; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* 63562306a36Sopenharmony_ci * The controller does not check for bitflips in erased pages, 63662306a36Sopenharmony_ci * therefore software must check instead. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_cistatic int check_erased_page(struct nand_chip *chip, u8 *buf) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 64162306a36Sopenharmony_ci u8 *ecc = chip->oob_poi; 64262306a36Sopenharmony_ci const int ecc_size = chip->ecc.bytes; 64362306a36Sopenharmony_ci const int pkt_size = chip->ecc.size; 64462306a36Sopenharmony_ci int i, res, bitflips = 0; 64562306a36Sopenharmony_ci struct mtd_oob_region oobregion = { }; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &oobregion); 64862306a36Sopenharmony_ci ecc += oobregion.offset; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for (i = 0; i < chip->ecc.steps; ++i) { 65162306a36Sopenharmony_ci res = nand_check_erased_ecc_chunk(buf, pkt_size, ecc, ecc_size, 65262306a36Sopenharmony_ci NULL, 0, 65362306a36Sopenharmony_ci chip->ecc.strength); 65462306a36Sopenharmony_ci if (res < 0) 65562306a36Sopenharmony_ci mtd->ecc_stats.failed++; 65662306a36Sopenharmony_ci else 65762306a36Sopenharmony_ci mtd->ecc_stats.corrected += res; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci bitflips = max(res, bitflips); 66062306a36Sopenharmony_ci buf += pkt_size; 66162306a36Sopenharmony_ci ecc += ecc_size; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return bitflips; 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic int fsl_ifc_read_page(struct nand_chip *chip, uint8_t *buf, 66862306a36Sopenharmony_ci int oob_required, int page) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 67162306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 67262306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 67362306a36Sopenharmony_ci struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci nand_read_page_op(chip, page, 0, buf, mtd->writesize); 67662306a36Sopenharmony_ci if (oob_required) 67762306a36Sopenharmony_ci fsl_ifc_read_buf(chip, chip->oob_poi, mtd->oobsize); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (ctrl->nand_stat & IFC_NAND_EVTER_STAT_ECCER) { 68062306a36Sopenharmony_ci if (!oob_required) 68162306a36Sopenharmony_ci fsl_ifc_read_buf(chip, chip->oob_poi, mtd->oobsize); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return check_erased_page(chip, buf); 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) 68762306a36Sopenharmony_ci mtd->ecc_stats.failed++; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return nctrl->max_bitflips; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in 69362306a36Sopenharmony_ci * waitfunc. 69462306a36Sopenharmony_ci */ 69562306a36Sopenharmony_cistatic int fsl_ifc_write_page(struct nand_chip *chip, const uint8_t *buf, 69662306a36Sopenharmony_ci int oob_required, int page) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); 70162306a36Sopenharmony_ci fsl_ifc_write_buf(chip, chip->oob_poi, mtd->oobsize); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int fsl_ifc_attach_chip(struct nand_chip *chip) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 70962306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = nand_get_controller_data(chip); 71062306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 71162306a36Sopenharmony_ci struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; 71262306a36Sopenharmony_ci u32 csor; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Must also set CSOR_NAND_ECC_ENC_EN if DEC_EN set */ 71762306a36Sopenharmony_ci if (csor & CSOR_NAND_ECC_DEC_EN) { 71862306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 71962306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &fsl_ifc_ooblayout_ops); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Hardware generates ECC per 512 Bytes */ 72262306a36Sopenharmony_ci chip->ecc.size = 512; 72362306a36Sopenharmony_ci if ((csor & CSOR_NAND_ECC_MODE_MASK) == CSOR_NAND_ECC_MODE_4) { 72462306a36Sopenharmony_ci chip->ecc.bytes = 8; 72562306a36Sopenharmony_ci chip->ecc.strength = 4; 72662306a36Sopenharmony_ci } else { 72762306a36Sopenharmony_ci chip->ecc.bytes = 16; 72862306a36Sopenharmony_ci chip->ecc.strength = 8; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci } else { 73162306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 73262306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->numchips = %d\n", __func__, 73662306a36Sopenharmony_ci nanddev_ntargets(&chip->base)); 73762306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->chipsize = %lld\n", __func__, 73862306a36Sopenharmony_ci nanddev_target_size(&chip->base)); 73962306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->pagemask = %8x\n", __func__, 74062306a36Sopenharmony_ci chip->pagemask); 74162306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->legacy.chip_delay = %d\n", __func__, 74262306a36Sopenharmony_ci chip->legacy.chip_delay); 74362306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->badblockpos = %d\n", __func__, 74462306a36Sopenharmony_ci chip->badblockpos); 74562306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->chip_shift = %d\n", __func__, 74662306a36Sopenharmony_ci chip->chip_shift); 74762306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->page_shift = %d\n", __func__, 74862306a36Sopenharmony_ci chip->page_shift); 74962306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, 75062306a36Sopenharmony_ci chip->phys_erase_shift); 75162306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.engine_type = %d\n", __func__, 75262306a36Sopenharmony_ci chip->ecc.engine_type); 75362306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, 75462306a36Sopenharmony_ci chip->ecc.steps); 75562306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.bytes = %d\n", __func__, 75662306a36Sopenharmony_ci chip->ecc.bytes); 75762306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: nand->ecc.total = %d\n", __func__, 75862306a36Sopenharmony_ci chip->ecc.total); 75962306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->ooblayout = %p\n", __func__, 76062306a36Sopenharmony_ci mtd->ooblayout); 76162306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->flags = %08x\n", __func__, mtd->flags); 76262306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->size = %lld\n", __func__, mtd->size); 76362306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->erasesize = %d\n", __func__, 76462306a36Sopenharmony_ci mtd->erasesize); 76562306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->writesize = %d\n", __func__, 76662306a36Sopenharmony_ci mtd->writesize); 76762306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: mtd->oobsize = %d\n", __func__, 76862306a36Sopenharmony_ci mtd->oobsize); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic const struct nand_controller_ops fsl_ifc_controller_ops = { 77462306a36Sopenharmony_ci .attach_chip = fsl_ifc_attach_chip, 77562306a36Sopenharmony_ci}; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 78062306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; 78162306a36Sopenharmony_ci struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; 78262306a36Sopenharmony_ci uint32_t csor = 0, csor_8k = 0, csor_ext = 0; 78362306a36Sopenharmony_ci uint32_t cs = priv->bank; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (ctrl->version < FSL_IFC_VERSION_1_1_0) 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (ctrl->version > FSL_IFC_VERSION_1_1_0) { 78962306a36Sopenharmony_ci u32 ncfgr, status; 79062306a36Sopenharmony_ci int ret; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* Trigger auto initialization */ 79362306a36Sopenharmony_ci ncfgr = ifc_in32(&ifc_runtime->ifc_nand.ncfgr); 79462306a36Sopenharmony_ci ifc_out32(ncfgr | IFC_NAND_NCFGR_SRAM_INIT_EN, &ifc_runtime->ifc_nand.ncfgr); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* Wait until done */ 79762306a36Sopenharmony_ci ret = readx_poll_timeout(ifc_in32, &ifc_runtime->ifc_nand.ncfgr, 79862306a36Sopenharmony_ci status, !(status & IFC_NAND_NCFGR_SRAM_INIT_EN), 79962306a36Sopenharmony_ci 10, IFC_TIMEOUT_MSECS * 1000); 80062306a36Sopenharmony_ci if (ret) 80162306a36Sopenharmony_ci dev_err(priv->dev, "Failed to initialize SRAM!\n"); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return ret; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Save CSOR and CSOR_ext */ 80762306a36Sopenharmony_ci csor = ifc_in32(&ifc_global->csor_cs[cs].csor); 80862306a36Sopenharmony_ci csor_ext = ifc_in32(&ifc_global->csor_cs[cs].csor_ext); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* chage PageSize 8K and SpareSize 1K*/ 81162306a36Sopenharmony_ci csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; 81262306a36Sopenharmony_ci ifc_out32(csor_8k, &ifc_global->csor_cs[cs].csor); 81362306a36Sopenharmony_ci ifc_out32(0x0000400, &ifc_global->csor_cs[cs].csor_ext); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* READID */ 81662306a36Sopenharmony_ci ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | 81762306a36Sopenharmony_ci (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | 81862306a36Sopenharmony_ci (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), 81962306a36Sopenharmony_ci &ifc_runtime->ifc_nand.nand_fir0); 82062306a36Sopenharmony_ci ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, 82162306a36Sopenharmony_ci &ifc_runtime->ifc_nand.nand_fcr0); 82262306a36Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.row3); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.nand_fbcr); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* Program ROW0/COL0 */ 82762306a36Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.row0); 82862306a36Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.col0); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* set the chip select for NAND Transaction */ 83162306a36Sopenharmony_ci ifc_out32(cs << IFC_NAND_CSEL_SHIFT, 83262306a36Sopenharmony_ci &ifc_runtime->ifc_nand.nand_csel); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* start read seq */ 83562306a36Sopenharmony_ci ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, 83662306a36Sopenharmony_ci &ifc_runtime->ifc_nand.nandseq_strt); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* wait for command complete flag or timeout */ 83962306a36Sopenharmony_ci wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, 84062306a36Sopenharmony_ci msecs_to_jiffies(IFC_TIMEOUT_MSECS)); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (ctrl->nand_stat != IFC_NAND_EVTER_STAT_OPC) { 84362306a36Sopenharmony_ci pr_err("fsl-ifc: Failed to Initialise SRAM\n"); 84462306a36Sopenharmony_ci return -ETIMEDOUT; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Restore CSOR and CSOR_ext */ 84862306a36Sopenharmony_ci ifc_out32(csor, &ifc_global->csor_cs[cs].csor); 84962306a36Sopenharmony_ci ifc_out32(csor_ext, &ifc_global->csor_cs[cs].csor_ext); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct fsl_ifc_ctrl *ctrl = priv->ctrl; 85762306a36Sopenharmony_ci struct fsl_ifc_global __iomem *ifc_global = ctrl->gregs; 85862306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc_runtime = ctrl->rregs; 85962306a36Sopenharmony_ci struct nand_chip *chip = &priv->chip; 86062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&priv->chip); 86162306a36Sopenharmony_ci u32 csor; 86262306a36Sopenharmony_ci int ret; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci /* Fill in fsl_ifc_mtd structure */ 86562306a36Sopenharmony_ci mtd->dev.parent = priv->dev; 86662306a36Sopenharmony_ci nand_set_flash_node(chip, priv->dev->of_node); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* fill in nand_chip structure */ 86962306a36Sopenharmony_ci /* set up function call table */ 87062306a36Sopenharmony_ci if ((ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr)) 87162306a36Sopenharmony_ci & CSPR_PORT_SIZE_16) 87262306a36Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte16; 87362306a36Sopenharmony_ci else 87462306a36Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci chip->legacy.write_buf = fsl_ifc_write_buf; 87762306a36Sopenharmony_ci chip->legacy.read_buf = fsl_ifc_read_buf; 87862306a36Sopenharmony_ci chip->legacy.select_chip = fsl_ifc_select_chip; 87962306a36Sopenharmony_ci chip->legacy.cmdfunc = fsl_ifc_cmdfunc; 88062306a36Sopenharmony_ci chip->legacy.waitfunc = fsl_ifc_wait; 88162306a36Sopenharmony_ci chip->legacy.set_features = nand_get_set_features_notsupp; 88262306a36Sopenharmony_ci chip->legacy.get_features = nand_get_set_features_notsupp; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci chip->bbt_td = &bbt_main_descr; 88562306a36Sopenharmony_ci chip->bbt_md = &bbt_mirror_descr; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci ifc_out32(0x0, &ifc_runtime->ifc_nand.ncfgr); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* set up nand options */ 89062306a36Sopenharmony_ci chip->bbt_options = NAND_BBT_USE_FLASH; 89162306a36Sopenharmony_ci chip->options = NAND_NO_SUBPAGE_WRITE; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (ifc_in32(&ifc_global->cspr_cs[priv->bank].cspr) 89462306a36Sopenharmony_ci & CSPR_PORT_SIZE_16) { 89562306a36Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte16; 89662306a36Sopenharmony_ci chip->options |= NAND_BUSWIDTH_16; 89762306a36Sopenharmony_ci } else { 89862306a36Sopenharmony_ci chip->legacy.read_byte = fsl_ifc_read_byte; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci chip->controller = &ifc_nand_ctrl->controller; 90262306a36Sopenharmony_ci nand_set_controller_data(chip, priv); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci chip->ecc.read_page = fsl_ifc_read_page; 90562306a36Sopenharmony_ci chip->ecc.write_page = fsl_ifc_write_page; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci csor = ifc_in32(&ifc_global->csor_cs[priv->bank].csor); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci switch (csor & CSOR_NAND_PGS_MASK) { 91062306a36Sopenharmony_ci case CSOR_NAND_PGS_512: 91162306a36Sopenharmony_ci if (!(chip->options & NAND_BUSWIDTH_16)) { 91262306a36Sopenharmony_ci /* Avoid conflict with bad block marker */ 91362306a36Sopenharmony_ci bbt_main_descr.offs = 0; 91462306a36Sopenharmony_ci bbt_mirror_descr.offs = 0; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci priv->bufnum_mask = 15; 91862306a36Sopenharmony_ci break; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci case CSOR_NAND_PGS_2K: 92162306a36Sopenharmony_ci priv->bufnum_mask = 3; 92262306a36Sopenharmony_ci break; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci case CSOR_NAND_PGS_4K: 92562306a36Sopenharmony_ci priv->bufnum_mask = 1; 92662306a36Sopenharmony_ci break; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci case CSOR_NAND_PGS_8K: 92962306a36Sopenharmony_ci priv->bufnum_mask = 0; 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci default: 93362306a36Sopenharmony_ci dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); 93462306a36Sopenharmony_ci return -ENODEV; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci ret = fsl_ifc_sram_init(priv); 93862306a36Sopenharmony_ci if (ret) 93962306a36Sopenharmony_ci return ret; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* 94262306a36Sopenharmony_ci * As IFC version 2.0.0 has 16KB of internal SRAM as compared to older 94362306a36Sopenharmony_ci * versions which had 8KB. Hence bufnum mask needs to be updated. 94462306a36Sopenharmony_ci */ 94562306a36Sopenharmony_ci if (ctrl->version >= FSL_IFC_VERSION_2_0_0) 94662306a36Sopenharmony_ci priv->bufnum_mask = (priv->bufnum_mask * 2) + 1; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&priv->chip); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci kfree(mtd->name); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (priv->vbase) 95862306a36Sopenharmony_ci iounmap(priv->vbase); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ifc_nand_ctrl->chips[priv->bank] = NULL; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci return 0; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int match_bank(struct fsl_ifc_global __iomem *ifc_global, int bank, 96662306a36Sopenharmony_ci phys_addr_t addr) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci u32 cspr = ifc_in32(&ifc_global->cspr_cs[bank].cspr); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (!(cspr & CSPR_V)) 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci if ((cspr & CSPR_MSEL) != CSPR_MSEL_NAND) 97362306a36Sopenharmony_ci return 0; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return (cspr & CSPR_BA) == convert_ifc_address(addr); 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic DEFINE_MUTEX(fsl_ifc_nand_mutex); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic int fsl_ifc_nand_probe(struct platform_device *dev) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct fsl_ifc_runtime __iomem *ifc; 98362306a36Sopenharmony_ci struct fsl_ifc_mtd *priv; 98462306a36Sopenharmony_ci struct resource res; 98562306a36Sopenharmony_ci static const char *part_probe_types[] 98662306a36Sopenharmony_ci = { "cmdlinepart", "RedBoot", "ofpart", NULL }; 98762306a36Sopenharmony_ci int ret; 98862306a36Sopenharmony_ci int bank; 98962306a36Sopenharmony_ci struct device_node *node = dev->dev.of_node; 99062306a36Sopenharmony_ci struct mtd_info *mtd; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs) 99362306a36Sopenharmony_ci return -ENODEV; 99462306a36Sopenharmony_ci ifc = fsl_ifc_ctrl_dev->rregs; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* get, allocate and map the memory resource */ 99762306a36Sopenharmony_ci ret = of_address_to_resource(node, 0, &res); 99862306a36Sopenharmony_ci if (ret) { 99962306a36Sopenharmony_ci dev_err(&dev->dev, "%s: failed to get resource\n", __func__); 100062306a36Sopenharmony_ci return ret; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* find which chip select it is connected to */ 100462306a36Sopenharmony_ci for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) { 100562306a36Sopenharmony_ci if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start)) 100662306a36Sopenharmony_ci break; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci if (bank >= fsl_ifc_ctrl_dev->banks) { 101062306a36Sopenharmony_ci dev_err(&dev->dev, "%s: address did not match any chip selects\n", 101162306a36Sopenharmony_ci __func__); 101262306a36Sopenharmony_ci return -ENODEV; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); 101662306a36Sopenharmony_ci if (!priv) 101762306a36Sopenharmony_ci return -ENOMEM; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci mutex_lock(&fsl_ifc_nand_mutex); 102062306a36Sopenharmony_ci if (!fsl_ifc_ctrl_dev->nand) { 102162306a36Sopenharmony_ci ifc_nand_ctrl = kzalloc(sizeof(*ifc_nand_ctrl), GFP_KERNEL); 102262306a36Sopenharmony_ci if (!ifc_nand_ctrl) { 102362306a36Sopenharmony_ci mutex_unlock(&fsl_ifc_nand_mutex); 102462306a36Sopenharmony_ci return -ENOMEM; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci ifc_nand_ctrl->read_bytes = 0; 102862306a36Sopenharmony_ci ifc_nand_ctrl->index = 0; 102962306a36Sopenharmony_ci ifc_nand_ctrl->addr = NULL; 103062306a36Sopenharmony_ci fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci nand_controller_init(&ifc_nand_ctrl->controller); 103362306a36Sopenharmony_ci } else { 103462306a36Sopenharmony_ci ifc_nand_ctrl = fsl_ifc_ctrl_dev->nand; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci mutex_unlock(&fsl_ifc_nand_mutex); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci ifc_nand_ctrl->chips[bank] = priv; 103962306a36Sopenharmony_ci priv->bank = bank; 104062306a36Sopenharmony_ci priv->ctrl = fsl_ifc_ctrl_dev; 104162306a36Sopenharmony_ci priv->dev = &dev->dev; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci priv->vbase = ioremap(res.start, resource_size(&res)); 104462306a36Sopenharmony_ci if (!priv->vbase) { 104562306a36Sopenharmony_ci dev_err(priv->dev, "%s: failed to map chip region\n", __func__); 104662306a36Sopenharmony_ci ret = -ENOMEM; 104762306a36Sopenharmony_ci goto err; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci dev_set_drvdata(priv->dev, priv); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | 105362306a36Sopenharmony_ci IFC_NAND_EVTER_EN_FTOER_EN | 105462306a36Sopenharmony_ci IFC_NAND_EVTER_EN_WPER_EN, 105562306a36Sopenharmony_ci &ifc->ifc_nand.nand_evter_en); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* enable NAND Machine Interrupts */ 105862306a36Sopenharmony_ci ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | 105962306a36Sopenharmony_ci IFC_NAND_EVTER_INTR_FTOERIR_EN | 106062306a36Sopenharmony_ci IFC_NAND_EVTER_INTR_WPERIR_EN, 106162306a36Sopenharmony_ci &ifc->ifc_nand.nand_evter_intr_en); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci mtd = nand_to_mtd(&priv->chip); 106462306a36Sopenharmony_ci mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); 106562306a36Sopenharmony_ci if (!mtd->name) { 106662306a36Sopenharmony_ci ret = -ENOMEM; 106762306a36Sopenharmony_ci goto err; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci ret = fsl_ifc_chip_init(priv); 107162306a36Sopenharmony_ci if (ret) 107262306a36Sopenharmony_ci goto err; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci priv->chip.controller->ops = &fsl_ifc_controller_ops; 107562306a36Sopenharmony_ci ret = nand_scan(&priv->chip, 1); 107662306a36Sopenharmony_ci if (ret) 107762306a36Sopenharmony_ci goto err; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci /* First look for RedBoot table or partitions on the command 108062306a36Sopenharmony_ci * line, these take precedence over device tree information */ 108162306a36Sopenharmony_ci ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); 108262306a36Sopenharmony_ci if (ret) 108362306a36Sopenharmony_ci goto cleanup_nand; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n", 108662306a36Sopenharmony_ci (unsigned long long)res.start, priv->bank); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return 0; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cicleanup_nand: 109162306a36Sopenharmony_ci nand_cleanup(&priv->chip); 109262306a36Sopenharmony_cierr: 109362306a36Sopenharmony_ci fsl_ifc_chip_remove(priv); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return ret; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic void fsl_ifc_nand_remove(struct platform_device *dev) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci struct fsl_ifc_mtd *priv = dev_get_drvdata(&dev->dev); 110162306a36Sopenharmony_ci struct nand_chip *chip = &priv->chip; 110262306a36Sopenharmony_ci int ret; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 110562306a36Sopenharmony_ci WARN_ON(ret); 110662306a36Sopenharmony_ci nand_cleanup(chip); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci fsl_ifc_chip_remove(priv); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci mutex_lock(&fsl_ifc_nand_mutex); 111162306a36Sopenharmony_ci ifc_nand_ctrl->counter--; 111262306a36Sopenharmony_ci if (!ifc_nand_ctrl->counter) { 111362306a36Sopenharmony_ci fsl_ifc_ctrl_dev->nand = NULL; 111462306a36Sopenharmony_ci kfree(ifc_nand_ctrl); 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci mutex_unlock(&fsl_ifc_nand_mutex); 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic const struct of_device_id fsl_ifc_nand_match[] = { 112062306a36Sopenharmony_ci { 112162306a36Sopenharmony_ci .compatible = "fsl,ifc-nand", 112262306a36Sopenharmony_ci }, 112362306a36Sopenharmony_ci {} 112462306a36Sopenharmony_ci}; 112562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_ifc_nand_match); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic struct platform_driver fsl_ifc_nand_driver = { 112862306a36Sopenharmony_ci .driver = { 112962306a36Sopenharmony_ci .name = "fsl,ifc-nand", 113062306a36Sopenharmony_ci .of_match_table = fsl_ifc_nand_match, 113162306a36Sopenharmony_ci }, 113262306a36Sopenharmony_ci .probe = fsl_ifc_nand_probe, 113362306a36Sopenharmony_ci .remove_new = fsl_ifc_nand_remove, 113462306a36Sopenharmony_ci}; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_cimodule_platform_driver(fsl_ifc_nand_driver); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 113962306a36Sopenharmony_ciMODULE_AUTHOR("Freescale"); 114062306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Integrated Flash Controller MTD NAND driver"); 1141