162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Freescale Enhanced Local Bus Controller NAND driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright © 2006-2007, 2010 Freescale Semiconductor 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: Nick Spence <nick.spence@freescale.com>, 762306a36Sopenharmony_ci * Scott Wood <scottwood@freescale.com> 862306a36Sopenharmony_ci * Jack Lan <jack.lan@freescale.com> 962306a36Sopenharmony_ci * Roy Zang <tie-fei.zang@freescale.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/types.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/ioport.h> 1762306a36Sopenharmony_ci#include <linux/of_address.h> 1862306a36Sopenharmony_ci#include <linux/of_platform.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2462306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 2562306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/io.h> 2862306a36Sopenharmony_ci#include <asm/fsl_lbc.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define MAX_BANKS 8 3162306a36Sopenharmony_ci#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ 3262306a36Sopenharmony_ci#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* mtd information per set */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistruct fsl_elbc_mtd { 3762306a36Sopenharmony_ci struct nand_chip chip; 3862306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci struct device *dev; 4162306a36Sopenharmony_ci int bank; /* Chip select bank number */ 4262306a36Sopenharmony_ci u8 __iomem *vbase; /* Chip select base virtual address */ 4362306a36Sopenharmony_ci int page_size; /* NAND page size (0=512, 1=2048) */ 4462306a36Sopenharmony_ci unsigned int fmr; /* FCM Flash Mode Register value */ 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Freescale eLBC FCM controller information */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct fsl_elbc_fcm_ctrl { 5062306a36Sopenharmony_ci struct nand_controller controller; 5162306a36Sopenharmony_ci struct fsl_elbc_mtd *chips[MAX_BANKS]; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci u8 __iomem *addr; /* Address of assigned FCM buffer */ 5462306a36Sopenharmony_ci unsigned int page; /* Last page written to / read from */ 5562306a36Sopenharmony_ci unsigned int read_bytes; /* Number of bytes read during command */ 5662306a36Sopenharmony_ci unsigned int column; /* Saved column from SEQIN */ 5762306a36Sopenharmony_ci unsigned int index; /* Pointer to next byte to 'read' */ 5862306a36Sopenharmony_ci unsigned int status; /* status read from LTESR after last op */ 5962306a36Sopenharmony_ci unsigned int mdr; /* UPM/FCM Data Register value */ 6062306a36Sopenharmony_ci unsigned int use_mdr; /* Non zero if the MDR is to be set */ 6162306a36Sopenharmony_ci unsigned int oob; /* Non zero if operating on OOB data */ 6262306a36Sopenharmony_ci unsigned int counter; /* counter for the initializations */ 6362306a36Sopenharmony_ci unsigned int max_bitflips; /* Saved during READ0 cmd */ 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* These map to the positions used by the FCM hardware ECC generator */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int fsl_elbc_ooblayout_ecc(struct mtd_info *mtd, int section, 6962306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 7262306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (section >= chip->ecc.steps) 7562306a36Sopenharmony_ci return -ERANGE; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci oobregion->offset = (16 * section) + 6; 7862306a36Sopenharmony_ci if (priv->fmr & FMR_ECCM) 7962306a36Sopenharmony_ci oobregion->offset += 2; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci oobregion->length = chip->ecc.bytes; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int fsl_elbc_ooblayout_free(struct mtd_info *mtd, int section, 8762306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 9062306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (section > chip->ecc.steps) 9362306a36Sopenharmony_ci return -ERANGE; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!section) { 9662306a36Sopenharmony_ci oobregion->offset = 0; 9762306a36Sopenharmony_ci if (mtd->writesize > 512) 9862306a36Sopenharmony_ci oobregion->offset++; 9962306a36Sopenharmony_ci oobregion->length = (priv->fmr & FMR_ECCM) ? 7 : 5; 10062306a36Sopenharmony_ci } else { 10162306a36Sopenharmony_ci oobregion->offset = (16 * section) - 10262306a36Sopenharmony_ci ((priv->fmr & FMR_ECCM) ? 5 : 7); 10362306a36Sopenharmony_ci if (section < chip->ecc.steps) 10462306a36Sopenharmony_ci oobregion->length = 13; 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsl_elbc_ooblayout_ops = { 11362306a36Sopenharmony_ci .ecc = fsl_elbc_ooblayout_ecc, 11462306a36Sopenharmony_ci .free = fsl_elbc_ooblayout_free, 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, 11962306a36Sopenharmony_ci * interfere with ECC positions, that's why we implement our own descriptors. 12062306a36Sopenharmony_ci * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic u8 bbt_pattern[] = {'B', 'b', 't', '0' }; 12362306a36Sopenharmony_cistatic u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_main_descr = { 12662306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | 12762306a36Sopenharmony_ci NAND_BBT_2BIT | NAND_BBT_VERSION, 12862306a36Sopenharmony_ci .offs = 11, 12962306a36Sopenharmony_ci .len = 4, 13062306a36Sopenharmony_ci .veroffs = 15, 13162306a36Sopenharmony_ci .maxblocks = 4, 13262306a36Sopenharmony_ci .pattern = bbt_pattern, 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_mirror_descr = { 13662306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | 13762306a36Sopenharmony_ci NAND_BBT_2BIT | NAND_BBT_VERSION, 13862306a36Sopenharmony_ci .offs = 11, 13962306a36Sopenharmony_ci .len = 4, 14062306a36Sopenharmony_ci .veroffs = 15, 14162306a36Sopenharmony_ci .maxblocks = 4, 14262306a36Sopenharmony_ci .pattern = mirror_pattern, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/*=================================*/ 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * Set up the FCM hardware block and page address fields, and the fcm 14962306a36Sopenharmony_ci * structure addr field to point to the correct FCM buffer in memory 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 15462306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 15562306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 15662306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 15762306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; 15862306a36Sopenharmony_ci int buf_num; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci elbc_fcm_ctrl->page = page_addr; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (priv->page_size) { 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * large page size chip : FPAR[PI] save the lowest 6 bits, 16562306a36Sopenharmony_ci * FBAR[BLK] save the other bits. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci out_be32(&lbc->fbar, page_addr >> 6); 16862306a36Sopenharmony_ci out_be32(&lbc->fpar, 16962306a36Sopenharmony_ci ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | 17062306a36Sopenharmony_ci (oob ? FPAR_LP_MS : 0) | column); 17162306a36Sopenharmony_ci buf_num = (page_addr & 1) << 2; 17262306a36Sopenharmony_ci } else { 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * small page size chip : FPAR[PI] save the lowest 5 bits, 17562306a36Sopenharmony_ci * FBAR[BLK] save the other bits. 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci out_be32(&lbc->fbar, page_addr >> 5); 17862306a36Sopenharmony_ci out_be32(&lbc->fpar, 17962306a36Sopenharmony_ci ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | 18062306a36Sopenharmony_ci (oob ? FPAR_SP_MS : 0) | column); 18162306a36Sopenharmony_ci buf_num = page_addr & 7; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024; 18562306a36Sopenharmony_ci elbc_fcm_ctrl->index = column; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* for OOB data point to the second half of the buffer */ 18862306a36Sopenharmony_ci if (oob) 18962306a36Sopenharmony_ci elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci dev_vdbg(priv->dev, "set_addr: bank=%d, " 19262306a36Sopenharmony_ci "elbc_fcm_ctrl->addr=0x%p (0x%p), " 19362306a36Sopenharmony_ci "index %x, pes %d ps %d\n", 19462306a36Sopenharmony_ci buf_num, elbc_fcm_ctrl->addr, priv->vbase, 19562306a36Sopenharmony_ci elbc_fcm_ctrl->index, 19662306a36Sopenharmony_ci chip->phys_erase_shift, chip->page_shift); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci/* 20062306a36Sopenharmony_ci * execute FCM command and wait for it to complete 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic int fsl_elbc_run_command(struct mtd_info *mtd) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 20562306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 20662306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 20762306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; 20862306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Setup the FMR[OP] to execute without write protection */ 21162306a36Sopenharmony_ci out_be32(&lbc->fmr, priv->fmr | 3); 21262306a36Sopenharmony_ci if (elbc_fcm_ctrl->use_mdr) 21362306a36Sopenharmony_ci out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci dev_vdbg(priv->dev, 21662306a36Sopenharmony_ci "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", 21762306a36Sopenharmony_ci in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); 21862306a36Sopenharmony_ci dev_vdbg(priv->dev, 21962306a36Sopenharmony_ci "fsl_elbc_run_command: fbar=%08x fpar=%08x " 22062306a36Sopenharmony_ci "fbcr=%08x bank=%d\n", 22162306a36Sopenharmony_ci in_be32(&lbc->fbar), in_be32(&lbc->fpar), 22262306a36Sopenharmony_ci in_be32(&lbc->fbcr), priv->bank); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ctrl->irq_status = 0; 22562306a36Sopenharmony_ci /* execute special operation */ 22662306a36Sopenharmony_ci out_be32(&lbc->lsor, priv->bank); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* wait for FCM complete flag or timeout */ 22962306a36Sopenharmony_ci wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, 23062306a36Sopenharmony_ci FCM_TIMEOUT_MSECS * HZ/1000); 23162306a36Sopenharmony_ci elbc_fcm_ctrl->status = ctrl->irq_status; 23262306a36Sopenharmony_ci /* store mdr value in case it was needed */ 23362306a36Sopenharmony_ci if (elbc_fcm_ctrl->use_mdr) 23462306a36Sopenharmony_ci elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci elbc_fcm_ctrl->use_mdr = 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (elbc_fcm_ctrl->status != LTESR_CC) { 23962306a36Sopenharmony_ci dev_info(priv->dev, 24062306a36Sopenharmony_ci "command failed: fir %x fcr %x status %x mdr %x\n", 24162306a36Sopenharmony_ci in_be32(&lbc->fir), in_be32(&lbc->fcr), 24262306a36Sopenharmony_ci elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr); 24362306a36Sopenharmony_ci return -EIO; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci elbc_fcm_ctrl->max_bitflips = 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (elbc_fcm_ctrl->read_bytes == mtd->writesize + mtd->oobsize) { 25262306a36Sopenharmony_ci uint32_t lteccr = in_be32(&lbc->lteccr); 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * if command was a full page read and the ELBC 25562306a36Sopenharmony_ci * has the LTECCR register, then bits 12-15 (ppc order) of 25662306a36Sopenharmony_ci * LTECCR indicates which 512 byte sub-pages had fixed errors. 25762306a36Sopenharmony_ci * bits 28-31 are uncorrectable errors, marked elsewhere. 25862306a36Sopenharmony_ci * for small page nand only 1 bit is used. 25962306a36Sopenharmony_ci * if the ELBC doesn't have the lteccr register it reads 0 26062306a36Sopenharmony_ci * FIXME: 4 bits can be corrected on NANDs with 2k pages, so 26162306a36Sopenharmony_ci * count the number of sub-pages with bitflips and update 26262306a36Sopenharmony_ci * ecc_stats.corrected accordingly. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci if (lteccr & 0x000F000F) 26562306a36Sopenharmony_ci out_be32(&lbc->lteccr, 0x000F000F); /* clear lteccr */ 26662306a36Sopenharmony_ci if (lteccr & 0x000F0000) { 26762306a36Sopenharmony_ci mtd->ecc_stats.corrected++; 26862306a36Sopenharmony_ci elbc_fcm_ctrl->max_bitflips = 1; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void fsl_elbc_do_read(struct nand_chip *chip, int oob) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 27862306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 27962306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (priv->page_size) { 28262306a36Sopenharmony_ci out_be32(&lbc->fir, 28362306a36Sopenharmony_ci (FIR_OP_CM0 << FIR_OP0_SHIFT) | 28462306a36Sopenharmony_ci (FIR_OP_CA << FIR_OP1_SHIFT) | 28562306a36Sopenharmony_ci (FIR_OP_PA << FIR_OP2_SHIFT) | 28662306a36Sopenharmony_ci (FIR_OP_CM1 << FIR_OP3_SHIFT) | 28762306a36Sopenharmony_ci (FIR_OP_RBW << FIR_OP4_SHIFT)); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | 29062306a36Sopenharmony_ci (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); 29162306a36Sopenharmony_ci } else { 29262306a36Sopenharmony_ci out_be32(&lbc->fir, 29362306a36Sopenharmony_ci (FIR_OP_CM0 << FIR_OP0_SHIFT) | 29462306a36Sopenharmony_ci (FIR_OP_CA << FIR_OP1_SHIFT) | 29562306a36Sopenharmony_ci (FIR_OP_PA << FIR_OP2_SHIFT) | 29662306a36Sopenharmony_ci (FIR_OP_RBW << FIR_OP3_SHIFT)); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (oob) 29962306a36Sopenharmony_ci out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT); 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* cmdfunc send commands to the FCM */ 30662306a36Sopenharmony_cistatic void fsl_elbc_cmdfunc(struct nand_chip *chip, unsigned int command, 30762306a36Sopenharmony_ci int column, int page_addr) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 31062306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 31162306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 31262306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; 31362306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci elbc_fcm_ctrl->use_mdr = 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* clear the read buffer */ 31862306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes = 0; 31962306a36Sopenharmony_ci if (command != NAND_CMD_PAGEPROG) 32062306a36Sopenharmony_ci elbc_fcm_ctrl->index = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci switch (command) { 32362306a36Sopenharmony_ci /* READ0 and READ1 read the entire buffer to use hardware ECC. */ 32462306a36Sopenharmony_ci case NAND_CMD_READ1: 32562306a36Sopenharmony_ci column += 256; 32662306a36Sopenharmony_ci fallthrough; 32762306a36Sopenharmony_ci case NAND_CMD_READ0: 32862306a36Sopenharmony_ci dev_dbg(priv->dev, 32962306a36Sopenharmony_ci "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" 33062306a36Sopenharmony_ci " 0x%x, column: 0x%x.\n", page_addr, column); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ 33462306a36Sopenharmony_ci set_addr(mtd, 0, page_addr, 0); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; 33762306a36Sopenharmony_ci elbc_fcm_ctrl->index += column; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci fsl_elbc_do_read(chip, 0); 34062306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* RNDOUT moves the pointer inside the page */ 34462306a36Sopenharmony_ci case NAND_CMD_RNDOUT: 34562306a36Sopenharmony_ci dev_dbg(priv->dev, 34662306a36Sopenharmony_ci "fsl_elbc_cmdfunc: NAND_CMD_RNDOUT, column: 0x%x.\n", 34762306a36Sopenharmony_ci column); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci elbc_fcm_ctrl->index = column; 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* READOOB reads only the OOB because no ECC is performed. */ 35362306a36Sopenharmony_ci case NAND_CMD_READOOB: 35462306a36Sopenharmony_ci dev_vdbg(priv->dev, 35562306a36Sopenharmony_ci "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" 35662306a36Sopenharmony_ci " 0x%x, column: 0x%x.\n", page_addr, column); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci out_be32(&lbc->fbcr, mtd->oobsize - column); 35962306a36Sopenharmony_ci set_addr(mtd, column, page_addr, 1); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci fsl_elbc_do_read(chip, 1); 36462306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 36562306a36Sopenharmony_ci return; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci case NAND_CMD_READID: 36862306a36Sopenharmony_ci case NAND_CMD_PARAM: 36962306a36Sopenharmony_ci dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD %x\n", command); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | 37262306a36Sopenharmony_ci (FIR_OP_UA << FIR_OP1_SHIFT) | 37362306a36Sopenharmony_ci (FIR_OP_RBW << FIR_OP2_SHIFT)); 37462306a36Sopenharmony_ci out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * although currently it's 8 bytes for READID, we always read 37762306a36Sopenharmony_ci * the maximum 256 bytes(for PARAM) 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci out_be32(&lbc->fbcr, 256); 38062306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes = 256; 38162306a36Sopenharmony_ci elbc_fcm_ctrl->use_mdr = 1; 38262306a36Sopenharmony_ci elbc_fcm_ctrl->mdr = column; 38362306a36Sopenharmony_ci set_addr(mtd, 0, 0, 0); 38462306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 38562306a36Sopenharmony_ci return; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* ERASE1 stores the block and page address */ 38862306a36Sopenharmony_ci case NAND_CMD_ERASE1: 38962306a36Sopenharmony_ci dev_vdbg(priv->dev, 39062306a36Sopenharmony_ci "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " 39162306a36Sopenharmony_ci "page_addr: 0x%x.\n", page_addr); 39262306a36Sopenharmony_ci set_addr(mtd, 0, page_addr, 0); 39362306a36Sopenharmony_ci return; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* ERASE2 uses the block and page address from ERASE1 */ 39662306a36Sopenharmony_ci case NAND_CMD_ERASE2: 39762306a36Sopenharmony_ci dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci out_be32(&lbc->fir, 40062306a36Sopenharmony_ci (FIR_OP_CM0 << FIR_OP0_SHIFT) | 40162306a36Sopenharmony_ci (FIR_OP_PA << FIR_OP1_SHIFT) | 40262306a36Sopenharmony_ci (FIR_OP_CM2 << FIR_OP2_SHIFT) | 40362306a36Sopenharmony_ci (FIR_OP_CW1 << FIR_OP3_SHIFT) | 40462306a36Sopenharmony_ci (FIR_OP_RS << FIR_OP4_SHIFT)); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci out_be32(&lbc->fcr, 40762306a36Sopenharmony_ci (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | 40862306a36Sopenharmony_ci (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | 40962306a36Sopenharmony_ci (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci out_be32(&lbc->fbcr, 0); 41262306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes = 0; 41362306a36Sopenharmony_ci elbc_fcm_ctrl->use_mdr = 1; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 41662306a36Sopenharmony_ci return; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* SEQIN sets up the addr buffer and all registers except the length */ 41962306a36Sopenharmony_ci case NAND_CMD_SEQIN: { 42062306a36Sopenharmony_ci __be32 fcr; 42162306a36Sopenharmony_ci dev_vdbg(priv->dev, 42262306a36Sopenharmony_ci "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " 42362306a36Sopenharmony_ci "page_addr: 0x%x, column: 0x%x.\n", 42462306a36Sopenharmony_ci page_addr, column); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci elbc_fcm_ctrl->column = column; 42762306a36Sopenharmony_ci elbc_fcm_ctrl->use_mdr = 1; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (column >= mtd->writesize) { 43062306a36Sopenharmony_ci /* OOB area */ 43162306a36Sopenharmony_ci column -= mtd->writesize; 43262306a36Sopenharmony_ci elbc_fcm_ctrl->oob = 1; 43362306a36Sopenharmony_ci } else { 43462306a36Sopenharmony_ci WARN_ON(column != 0); 43562306a36Sopenharmony_ci elbc_fcm_ctrl->oob = 0; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | 43962306a36Sopenharmony_ci (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | 44062306a36Sopenharmony_ci (NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (priv->page_size) { 44362306a36Sopenharmony_ci out_be32(&lbc->fir, 44462306a36Sopenharmony_ci (FIR_OP_CM2 << FIR_OP0_SHIFT) | 44562306a36Sopenharmony_ci (FIR_OP_CA << FIR_OP1_SHIFT) | 44662306a36Sopenharmony_ci (FIR_OP_PA << FIR_OP2_SHIFT) | 44762306a36Sopenharmony_ci (FIR_OP_WB << FIR_OP3_SHIFT) | 44862306a36Sopenharmony_ci (FIR_OP_CM3 << FIR_OP4_SHIFT) | 44962306a36Sopenharmony_ci (FIR_OP_CW1 << FIR_OP5_SHIFT) | 45062306a36Sopenharmony_ci (FIR_OP_RS << FIR_OP6_SHIFT)); 45162306a36Sopenharmony_ci } else { 45262306a36Sopenharmony_ci out_be32(&lbc->fir, 45362306a36Sopenharmony_ci (FIR_OP_CM0 << FIR_OP0_SHIFT) | 45462306a36Sopenharmony_ci (FIR_OP_CM2 << FIR_OP1_SHIFT) | 45562306a36Sopenharmony_ci (FIR_OP_CA << FIR_OP2_SHIFT) | 45662306a36Sopenharmony_ci (FIR_OP_PA << FIR_OP3_SHIFT) | 45762306a36Sopenharmony_ci (FIR_OP_WB << FIR_OP4_SHIFT) | 45862306a36Sopenharmony_ci (FIR_OP_CM3 << FIR_OP5_SHIFT) | 45962306a36Sopenharmony_ci (FIR_OP_CW1 << FIR_OP6_SHIFT) | 46062306a36Sopenharmony_ci (FIR_OP_RS << FIR_OP7_SHIFT)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (elbc_fcm_ctrl->oob) 46362306a36Sopenharmony_ci /* OOB area --> READOOB */ 46462306a36Sopenharmony_ci fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; 46562306a36Sopenharmony_ci else 46662306a36Sopenharmony_ci /* First 256 bytes --> READ0 */ 46762306a36Sopenharmony_ci fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci out_be32(&lbc->fcr, fcr); 47162306a36Sopenharmony_ci set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob); 47262306a36Sopenharmony_ci return; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ 47662306a36Sopenharmony_ci case NAND_CMD_PAGEPROG: { 47762306a36Sopenharmony_ci dev_vdbg(priv->dev, 47862306a36Sopenharmony_ci "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " 47962306a36Sopenharmony_ci "writing %d bytes.\n", elbc_fcm_ctrl->index); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* if the write did not start at 0 or is not a full page 48262306a36Sopenharmony_ci * then set the exact length, otherwise use a full page 48362306a36Sopenharmony_ci * write so the HW generates the ECC. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 || 48662306a36Sopenharmony_ci elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) 48762306a36Sopenharmony_ci out_be32(&lbc->fbcr, 48862306a36Sopenharmony_ci elbc_fcm_ctrl->index - elbc_fcm_ctrl->column); 48962306a36Sopenharmony_ci else 49062306a36Sopenharmony_ci out_be32(&lbc->fbcr, 0); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* CMD_STATUS must read the status byte while CEB is active */ 49762306a36Sopenharmony_ci /* Note - it does not wait for the ready line */ 49862306a36Sopenharmony_ci case NAND_CMD_STATUS: 49962306a36Sopenharmony_ci out_be32(&lbc->fir, 50062306a36Sopenharmony_ci (FIR_OP_CM0 << FIR_OP0_SHIFT) | 50162306a36Sopenharmony_ci (FIR_OP_RBW << FIR_OP1_SHIFT)); 50262306a36Sopenharmony_ci out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); 50362306a36Sopenharmony_ci out_be32(&lbc->fbcr, 1); 50462306a36Sopenharmony_ci set_addr(mtd, 0, 0, 0); 50562306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes = 1; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* The chip always seems to report that it is 51062306a36Sopenharmony_ci * write-protected, even when it is not. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP); 51362306a36Sopenharmony_ci return; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* RESET without waiting for the ready line */ 51662306a36Sopenharmony_ci case NAND_CMD_RESET: 51762306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); 51862306a36Sopenharmony_ci out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); 51962306a36Sopenharmony_ci out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); 52062306a36Sopenharmony_ci fsl_elbc_run_command(mtd); 52162306a36Sopenharmony_ci return; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci default: 52462306a36Sopenharmony_ci dev_err(priv->dev, 52562306a36Sopenharmony_ci "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", 52662306a36Sopenharmony_ci command); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic void fsl_elbc_select_chip(struct nand_chip *chip, int cs) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci /* The hardware does not seem to support multiple 53362306a36Sopenharmony_ci * chips per bank. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/* 53862306a36Sopenharmony_ci * Write buf to the FCM Controller Data Buffer 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic void fsl_elbc_write_buf(struct nand_chip *chip, const u8 *buf, int len) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 54362306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 54462306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; 54562306a36Sopenharmony_ci unsigned int bufsize = mtd->writesize + mtd->oobsize; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (len <= 0) { 54862306a36Sopenharmony_ci dev_err(priv->dev, "write_buf of %d bytes", len); 54962306a36Sopenharmony_ci elbc_fcm_ctrl->status = 0; 55062306a36Sopenharmony_ci return; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) { 55462306a36Sopenharmony_ci dev_err(priv->dev, 55562306a36Sopenharmony_ci "write_buf beyond end of buffer " 55662306a36Sopenharmony_ci "(%d requested, %u available)\n", 55762306a36Sopenharmony_ci len, bufsize - elbc_fcm_ctrl->index); 55862306a36Sopenharmony_ci len = bufsize - elbc_fcm_ctrl->index; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len); 56262306a36Sopenharmony_ci /* 56362306a36Sopenharmony_ci * This is workaround for the weird elbc hangs during nand write, 56462306a36Sopenharmony_ci * Scott Wood says: "...perhaps difference in how long it takes a 56562306a36Sopenharmony_ci * write to make it through the localbus compared to a write to IMMR 56662306a36Sopenharmony_ci * is causing problems, and sync isn't helping for some reason." 56762306a36Sopenharmony_ci * Reading back the last byte helps though. 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_ci in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci elbc_fcm_ctrl->index += len; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/* 57562306a36Sopenharmony_ci * read a byte from either the FCM hardware buffer if it has any data left 57662306a36Sopenharmony_ci * otherwise issue a command to read a single byte. 57762306a36Sopenharmony_ci */ 57862306a36Sopenharmony_cistatic u8 fsl_elbc_read_byte(struct nand_chip *chip) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 58162306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* If there are still bytes in the FCM, then use the next byte. */ 58462306a36Sopenharmony_ci if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes) 58562306a36Sopenharmony_ci return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci dev_err(priv->dev, "read_byte beyond end of buffer\n"); 58862306a36Sopenharmony_ci return ERR_BYTE; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/* 59262306a36Sopenharmony_ci * Read from the FCM Controller Data Buffer 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_cistatic void fsl_elbc_read_buf(struct nand_chip *chip, u8 *buf, int len) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 59762306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; 59862306a36Sopenharmony_ci int avail; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (len < 0) 60162306a36Sopenharmony_ci return; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci avail = min((unsigned int)len, 60462306a36Sopenharmony_ci elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index); 60562306a36Sopenharmony_ci memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail); 60662306a36Sopenharmony_ci elbc_fcm_ctrl->index += avail; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (len > avail) 60962306a36Sopenharmony_ci dev_err(priv->dev, 61062306a36Sopenharmony_ci "read_buf beyond end of buffer " 61162306a36Sopenharmony_ci "(%d requested, %d available)\n", 61262306a36Sopenharmony_ci len, avail); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/* This function is called after Program and Erase Operations to 61662306a36Sopenharmony_ci * check for success or failure. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic int fsl_elbc_wait(struct nand_chip *chip) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 62162306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (elbc_fcm_ctrl->status != LTESR_CC) 62462306a36Sopenharmony_ci return NAND_STATUS_FAIL; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* The chip always seems to report that it is 62762306a36Sopenharmony_ci * write-protected, even when it is not. 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int fsl_elbc_read_page(struct nand_chip *chip, uint8_t *buf, 63362306a36Sopenharmony_ci int oob_required, int page) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 63662306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 63762306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 63862306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci nand_read_page_op(chip, page, 0, buf, mtd->writesize); 64162306a36Sopenharmony_ci if (oob_required) 64262306a36Sopenharmony_ci fsl_elbc_read_buf(chip, chip->oob_poi, mtd->oobsize); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (fsl_elbc_wait(chip) & NAND_STATUS_FAIL) 64562306a36Sopenharmony_ci mtd->ecc_stats.failed++; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return elbc_fcm_ctrl->max_bitflips; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in 65162306a36Sopenharmony_ci * waitfunc. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_cistatic int fsl_elbc_write_page(struct nand_chip *chip, const uint8_t *buf, 65462306a36Sopenharmony_ci int oob_required, int page) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); 65962306a36Sopenharmony_ci fsl_elbc_write_buf(chip, chip->oob_poi, mtd->oobsize); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* ECC will be calculated automatically, and errors will be detected in 66562306a36Sopenharmony_ci * waitfunc. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_cistatic int fsl_elbc_write_subpage(struct nand_chip *chip, uint32_t offset, 66862306a36Sopenharmony_ci uint32_t data_len, const uint8_t *buf, 66962306a36Sopenharmony_ci int oob_required, int page) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 67462306a36Sopenharmony_ci fsl_elbc_write_buf(chip, buf, mtd->writesize); 67562306a36Sopenharmony_ci fsl_elbc_write_buf(chip, chip->oob_poi, mtd->oobsize); 67662306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 68262306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 68362306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; 68462306a36Sopenharmony_ci struct nand_chip *chip = &priv->chip; 68562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* Fill in fsl_elbc_mtd structure */ 69062306a36Sopenharmony_ci mtd->dev.parent = priv->dev; 69162306a36Sopenharmony_ci nand_set_flash_node(chip, priv->dev->of_node); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* set timeout to maximum */ 69462306a36Sopenharmony_ci priv->fmr = 15 << FMR_CWTO_SHIFT; 69562306a36Sopenharmony_ci if (in_be32(&lbc->bank[priv->bank].or) & OR_FCM_PGS) 69662306a36Sopenharmony_ci priv->fmr |= FMR_ECCM; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* fill in nand_chip structure */ 69962306a36Sopenharmony_ci /* set up function call table */ 70062306a36Sopenharmony_ci chip->legacy.read_byte = fsl_elbc_read_byte; 70162306a36Sopenharmony_ci chip->legacy.write_buf = fsl_elbc_write_buf; 70262306a36Sopenharmony_ci chip->legacy.read_buf = fsl_elbc_read_buf; 70362306a36Sopenharmony_ci chip->legacy.select_chip = fsl_elbc_select_chip; 70462306a36Sopenharmony_ci chip->legacy.cmdfunc = fsl_elbc_cmdfunc; 70562306a36Sopenharmony_ci chip->legacy.waitfunc = fsl_elbc_wait; 70662306a36Sopenharmony_ci chip->legacy.set_features = nand_get_set_features_notsupp; 70762306a36Sopenharmony_ci chip->legacy.get_features = nand_get_set_features_notsupp; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci chip->bbt_td = &bbt_main_descr; 71062306a36Sopenharmony_ci chip->bbt_md = &bbt_mirror_descr; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* set up nand options */ 71362306a36Sopenharmony_ci chip->bbt_options = NAND_BBT_USE_FLASH; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci chip->controller = &elbc_fcm_ctrl->controller; 71662306a36Sopenharmony_ci nand_set_controller_data(chip, priv); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci return 0; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int fsl_elbc_attach_chip(struct nand_chip *chip) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 72462306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = nand_get_controller_data(chip); 72562306a36Sopenharmony_ci struct fsl_lbc_ctrl *ctrl = priv->ctrl; 72662306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc = ctrl->regs; 72762306a36Sopenharmony_ci unsigned int al; 72862306a36Sopenharmony_ci u32 br; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci * if ECC was not chosen in DT, decide whether to use HW or SW ECC from 73262306a36Sopenharmony_ci * CS Base Register 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) { 73562306a36Sopenharmony_ci /* If CS Base Register selects full hardware ECC then use it */ 73662306a36Sopenharmony_ci if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == 73762306a36Sopenharmony_ci BR_DECC_CHK_GEN) { 73862306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 73962306a36Sopenharmony_ci } else { 74062306a36Sopenharmony_ci /* otherwise fall back to default software ECC */ 74162306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 74262306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci switch (chip->ecc.engine_type) { 74762306a36Sopenharmony_ci /* if HW ECC was chosen, setup ecc and oob layout */ 74862306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 74962306a36Sopenharmony_ci chip->ecc.read_page = fsl_elbc_read_page; 75062306a36Sopenharmony_ci chip->ecc.write_page = fsl_elbc_write_page; 75162306a36Sopenharmony_ci chip->ecc.write_subpage = fsl_elbc_write_subpage; 75262306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &fsl_elbc_ooblayout_ops); 75362306a36Sopenharmony_ci chip->ecc.size = 512; 75462306a36Sopenharmony_ci chip->ecc.bytes = 3; 75562306a36Sopenharmony_ci chip->ecc.strength = 1; 75662306a36Sopenharmony_ci break; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* if none or SW ECC was chosen, we do not need to set anything here */ 75962306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 76062306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 76162306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_DIE: 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci default: 76562306a36Sopenharmony_ci return -EINVAL; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* enable/disable HW ECC checking and generating based on if HW ECC was chosen */ 76962306a36Sopenharmony_ci br = in_be32(&lbc->bank[priv->bank].br) & ~BR_DECC; 77062306a36Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) 77162306a36Sopenharmony_ci out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_CHK_GEN); 77262306a36Sopenharmony_ci else 77362306a36Sopenharmony_ci out_be32(&lbc->bank[priv->bank].br, br | BR_DECC_OFF); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* calculate FMR Address Length field */ 77662306a36Sopenharmony_ci al = 0; 77762306a36Sopenharmony_ci if (chip->pagemask & 0xffff0000) 77862306a36Sopenharmony_ci al++; 77962306a36Sopenharmony_ci if (chip->pagemask & 0xff000000) 78062306a36Sopenharmony_ci al++; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci priv->fmr |= al << FMR_AL_SHIFT; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n", 78562306a36Sopenharmony_ci nanddev_ntargets(&chip->base)); 78662306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n", 78762306a36Sopenharmony_ci nanddev_target_size(&chip->base)); 78862306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n", 78962306a36Sopenharmony_ci chip->pagemask); 79062306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->legacy.chip_delay = %d\n", 79162306a36Sopenharmony_ci chip->legacy.chip_delay); 79262306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n", 79362306a36Sopenharmony_ci chip->badblockpos); 79462306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n", 79562306a36Sopenharmony_ci chip->chip_shift); 79662306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n", 79762306a36Sopenharmony_ci chip->page_shift); 79862306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", 79962306a36Sopenharmony_ci chip->phys_erase_shift); 80062306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.engine_type = %d\n", 80162306a36Sopenharmony_ci chip->ecc.engine_type); 80262306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", 80362306a36Sopenharmony_ci chip->ecc.steps); 80462306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", 80562306a36Sopenharmony_ci chip->ecc.bytes); 80662306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n", 80762306a36Sopenharmony_ci chip->ecc.total); 80862306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: mtd->ooblayout = %p\n", 80962306a36Sopenharmony_ci mtd->ooblayout); 81062306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); 81162306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); 81262306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n", 81362306a36Sopenharmony_ci mtd->erasesize); 81462306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n", 81562306a36Sopenharmony_ci mtd->writesize); 81662306a36Sopenharmony_ci dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n", 81762306a36Sopenharmony_ci mtd->oobsize); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* adjust Option Register and ECC to match Flash page size */ 82062306a36Sopenharmony_ci if (mtd->writesize == 512) { 82162306a36Sopenharmony_ci priv->page_size = 0; 82262306a36Sopenharmony_ci clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); 82362306a36Sopenharmony_ci } else if (mtd->writesize == 2048) { 82462306a36Sopenharmony_ci priv->page_size = 1; 82562306a36Sopenharmony_ci setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); 82662306a36Sopenharmony_ci } else { 82762306a36Sopenharmony_ci dev_err(priv->dev, 82862306a36Sopenharmony_ci "fsl_elbc_init: page size %d is not supported\n", 82962306a36Sopenharmony_ci mtd->writesize); 83062306a36Sopenharmony_ci return -ENOTSUPP; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci return 0; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic const struct nand_controller_ops fsl_elbc_controller_ops = { 83762306a36Sopenharmony_ci .attach_chip = fsl_elbc_attach_chip, 83862306a36Sopenharmony_ci}; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand; 84362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&priv->chip); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci kfree(mtd->name); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (priv->vbase) 84862306a36Sopenharmony_ci iounmap(priv->vbase); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci elbc_fcm_ctrl->chips[priv->bank] = NULL; 85162306a36Sopenharmony_ci kfree(priv); 85262306a36Sopenharmony_ci return 0; 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic DEFINE_MUTEX(fsl_elbc_nand_mutex); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int fsl_elbc_nand_probe(struct platform_device *pdev) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct fsl_lbc_regs __iomem *lbc; 86062306a36Sopenharmony_ci struct fsl_elbc_mtd *priv; 86162306a36Sopenharmony_ci struct resource res; 86262306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl; 86362306a36Sopenharmony_ci static const char *part_probe_types[] 86462306a36Sopenharmony_ci = { "cmdlinepart", "RedBoot", "ofpart", NULL }; 86562306a36Sopenharmony_ci int ret; 86662306a36Sopenharmony_ci int bank; 86762306a36Sopenharmony_ci struct device *dev; 86862306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 86962306a36Sopenharmony_ci struct mtd_info *mtd; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs) 87262306a36Sopenharmony_ci return -ENODEV; 87362306a36Sopenharmony_ci lbc = fsl_lbc_ctrl_dev->regs; 87462306a36Sopenharmony_ci dev = fsl_lbc_ctrl_dev->dev; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* get, allocate and map the memory resource */ 87762306a36Sopenharmony_ci ret = of_address_to_resource(node, 0, &res); 87862306a36Sopenharmony_ci if (ret) { 87962306a36Sopenharmony_ci dev_err(dev, "failed to get resource\n"); 88062306a36Sopenharmony_ci return ret; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* find which chip select it is connected to */ 88462306a36Sopenharmony_ci for (bank = 0; bank < MAX_BANKS; bank++) 88562306a36Sopenharmony_ci if ((in_be32(&lbc->bank[bank].br) & BR_V) && 88662306a36Sopenharmony_ci (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && 88762306a36Sopenharmony_ci (in_be32(&lbc->bank[bank].br) & 88862306a36Sopenharmony_ci in_be32(&lbc->bank[bank].or) & BR_BA) 88962306a36Sopenharmony_ci == fsl_lbc_addr(res.start)) 89062306a36Sopenharmony_ci break; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (bank >= MAX_BANKS) { 89362306a36Sopenharmony_ci dev_err(dev, "address did not match any chip selects\n"); 89462306a36Sopenharmony_ci return -ENODEV; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 89862306a36Sopenharmony_ci if (!priv) 89962306a36Sopenharmony_ci return -ENOMEM; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci mutex_lock(&fsl_elbc_nand_mutex); 90262306a36Sopenharmony_ci if (!fsl_lbc_ctrl_dev->nand) { 90362306a36Sopenharmony_ci elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL); 90462306a36Sopenharmony_ci if (!elbc_fcm_ctrl) { 90562306a36Sopenharmony_ci mutex_unlock(&fsl_elbc_nand_mutex); 90662306a36Sopenharmony_ci ret = -ENOMEM; 90762306a36Sopenharmony_ci goto err; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci elbc_fcm_ctrl->counter++; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci nand_controller_init(&elbc_fcm_ctrl->controller); 91262306a36Sopenharmony_ci fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl; 91362306a36Sopenharmony_ci } else { 91462306a36Sopenharmony_ci elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci mutex_unlock(&fsl_elbc_nand_mutex); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci elbc_fcm_ctrl->chips[bank] = priv; 91962306a36Sopenharmony_ci priv->bank = bank; 92062306a36Sopenharmony_ci priv->ctrl = fsl_lbc_ctrl_dev; 92162306a36Sopenharmony_ci priv->dev = &pdev->dev; 92262306a36Sopenharmony_ci dev_set_drvdata(priv->dev, priv); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci priv->vbase = ioremap(res.start, resource_size(&res)); 92562306a36Sopenharmony_ci if (!priv->vbase) { 92662306a36Sopenharmony_ci dev_err(dev, "failed to map chip region\n"); 92762306a36Sopenharmony_ci ret = -ENOMEM; 92862306a36Sopenharmony_ci goto err; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci mtd = nand_to_mtd(&priv->chip); 93262306a36Sopenharmony_ci mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); 93362306a36Sopenharmony_ci if (!nand_to_mtd(&priv->chip)->name) { 93462306a36Sopenharmony_ci ret = -ENOMEM; 93562306a36Sopenharmony_ci goto err; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci ret = fsl_elbc_chip_init(priv); 93962306a36Sopenharmony_ci if (ret) 94062306a36Sopenharmony_ci goto err; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci priv->chip.controller->ops = &fsl_elbc_controller_ops; 94362306a36Sopenharmony_ci ret = nand_scan(&priv->chip, 1); 94462306a36Sopenharmony_ci if (ret) 94562306a36Sopenharmony_ci goto err; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* First look for RedBoot table or partitions on the command 94862306a36Sopenharmony_ci * line, these take precedence over device tree information */ 94962306a36Sopenharmony_ci ret = mtd_device_parse_register(mtd, part_probe_types, NULL, NULL, 0); 95062306a36Sopenharmony_ci if (ret) 95162306a36Sopenharmony_ci goto cleanup_nand; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci pr_info("eLBC NAND device at 0x%llx, bank %d\n", 95462306a36Sopenharmony_ci (unsigned long long)res.start, priv->bank); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return 0; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_cicleanup_nand: 95962306a36Sopenharmony_ci nand_cleanup(&priv->chip); 96062306a36Sopenharmony_cierr: 96162306a36Sopenharmony_ci fsl_elbc_chip_remove(priv); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci return ret; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic void fsl_elbc_nand_remove(struct platform_device *pdev) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand; 96962306a36Sopenharmony_ci struct fsl_elbc_mtd *priv = dev_get_drvdata(&pdev->dev); 97062306a36Sopenharmony_ci struct nand_chip *chip = &priv->chip; 97162306a36Sopenharmony_ci int ret; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 97462306a36Sopenharmony_ci WARN_ON(ret); 97562306a36Sopenharmony_ci nand_cleanup(chip); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci fsl_elbc_chip_remove(priv); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci mutex_lock(&fsl_elbc_nand_mutex); 98062306a36Sopenharmony_ci elbc_fcm_ctrl->counter--; 98162306a36Sopenharmony_ci if (!elbc_fcm_ctrl->counter) { 98262306a36Sopenharmony_ci fsl_lbc_ctrl_dev->nand = NULL; 98362306a36Sopenharmony_ci kfree(elbc_fcm_ctrl); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci mutex_unlock(&fsl_elbc_nand_mutex); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic const struct of_device_id fsl_elbc_nand_match[] = { 99062306a36Sopenharmony_ci { .compatible = "fsl,elbc-fcm-nand", }, 99162306a36Sopenharmony_ci {} 99262306a36Sopenharmony_ci}; 99362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsl_elbc_nand_match); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cistatic struct platform_driver fsl_elbc_nand_driver = { 99662306a36Sopenharmony_ci .driver = { 99762306a36Sopenharmony_ci .name = "fsl,elbc-fcm-nand", 99862306a36Sopenharmony_ci .of_match_table = fsl_elbc_nand_match, 99962306a36Sopenharmony_ci }, 100062306a36Sopenharmony_ci .probe = fsl_elbc_nand_probe, 100162306a36Sopenharmony_ci .remove_new = fsl_elbc_nand_remove, 100262306a36Sopenharmony_ci}; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_cimodule_platform_driver(fsl_elbc_nand_driver); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 100762306a36Sopenharmony_ciMODULE_AUTHOR("Freescale"); 100862306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver"); 1009