162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Freescale GPMI NAND Flash Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2008 Embedded Alley Solutions, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1862306a36Sopenharmony_ci#include <linux/dma/mxs-dma.h> 1962306a36Sopenharmony_ci#include "gpmi-nand.h" 2062306a36Sopenharmony_ci#include "gpmi-regs.h" 2162306a36Sopenharmony_ci#include "bch-regs.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Resource names for the GPMI NAND driver. */ 2462306a36Sopenharmony_ci#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" 2562306a36Sopenharmony_ci#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" 2662306a36Sopenharmony_ci#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Converts time to clock cycles */ 2962306a36Sopenharmony_ci#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MXS_SET_ADDR 0x4 3262306a36Sopenharmony_ci#define MXS_CLR_ADDR 0x8 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * Clear the bit and poll it cleared. This is usually called with 3562306a36Sopenharmony_ci * a reset address and mask being either SFTRST(bit 31) or CLKGATE 3662306a36Sopenharmony_ci * (bit 30). 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic int clear_poll_bit(void __iomem *addr, u32 mask) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int timeout = 0x400; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* clear the bit */ 4362306a36Sopenharmony_ci writel(mask, addr + MXS_CLR_ADDR); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * SFTRST needs 3 GPMI clocks to settle, the reference manual 4762306a36Sopenharmony_ci * recommends to wait 1us. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci udelay(1); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* poll the bit becoming clear */ 5262306a36Sopenharmony_ci while ((readl(addr) & mask) && --timeout) 5362306a36Sopenharmony_ci /* nothing */; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return !timeout; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define MODULE_CLKGATE (1 << 30) 5962306a36Sopenharmony_ci#define MODULE_SFTRST (1 << 31) 6062306a36Sopenharmony_ci/* 6162306a36Sopenharmony_ci * The current mxs_reset_block() will do two things: 6262306a36Sopenharmony_ci * [1] enable the module. 6362306a36Sopenharmony_ci * [2] reset the module. 6462306a36Sopenharmony_ci * 6562306a36Sopenharmony_ci * In most of the cases, it's ok. 6662306a36Sopenharmony_ci * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). 6762306a36Sopenharmony_ci * If you try to soft reset the BCH block, it becomes unusable until 6862306a36Sopenharmony_ci * the next hard reset. This case occurs in the NAND boot mode. When the board 6962306a36Sopenharmony_ci * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. 7062306a36Sopenharmony_ci * So If the driver tries to reset the BCH again, the BCH will not work anymore. 7162306a36Sopenharmony_ci * You will see a DMA timeout in this case. The bug has been fixed 7262306a36Sopenharmony_ci * in the following chips, such as MX28. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * To avoid this bug, just add a new parameter `just_enable` for 7562306a36Sopenharmony_ci * the mxs_reset_block(), and rewrite it here. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int ret; 8062306a36Sopenharmony_ci int timeout = 0x400; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* clear and poll SFTRST */ 8362306a36Sopenharmony_ci ret = clear_poll_bit(reset_addr, MODULE_SFTRST); 8462306a36Sopenharmony_ci if (unlikely(ret)) 8562306a36Sopenharmony_ci goto error; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* clear CLKGATE */ 8862306a36Sopenharmony_ci writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (!just_enable) { 9162306a36Sopenharmony_ci /* set SFTRST to reset the block */ 9262306a36Sopenharmony_ci writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); 9362306a36Sopenharmony_ci udelay(1); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* poll CLKGATE becoming set */ 9662306a36Sopenharmony_ci while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) 9762306a36Sopenharmony_ci /* nothing */; 9862306a36Sopenharmony_ci if (unlikely(!timeout)) 9962306a36Sopenharmony_ci goto error; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* clear and poll SFTRST */ 10362306a36Sopenharmony_ci ret = clear_poll_bit(reset_addr, MODULE_SFTRST); 10462306a36Sopenharmony_ci if (unlikely(ret)) 10562306a36Sopenharmony_ci goto error; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* clear and poll CLKGATE */ 10862306a36Sopenharmony_ci ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); 10962306a36Sopenharmony_ci if (unlikely(ret)) 11062306a36Sopenharmony_ci goto error; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cierror: 11562306a36Sopenharmony_ci pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); 11662306a36Sopenharmony_ci return -ETIMEDOUT; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct clk *clk; 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci for (i = 0; i < GPMI_CLK_MAX; i++) { 12662306a36Sopenharmony_ci clk = this->resources.clock[i]; 12762306a36Sopenharmony_ci if (!clk) 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (v) { 13162306a36Sopenharmony_ci ret = clk_prepare_enable(clk); 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci goto err_clk; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci clk_disable_unprepare(clk); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cierr_clk: 14162306a36Sopenharmony_ci for (; i > 0; i--) 14262306a36Sopenharmony_ci clk_disable_unprepare(this->resources.clock[i - 1]); 14362306a36Sopenharmony_ci return ret; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int gpmi_init(struct gpmi_nand_data *this) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct resources *r = &this->resources; 14962306a36Sopenharmony_ci int ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(this->dev); 15262306a36Sopenharmony_ci if (ret < 0) 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = gpmi_reset_block(r->gpmi_regs, false); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci goto err_out; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * Reset BCH here, too. We got failures otherwise :( 16162306a36Sopenharmony_ci * See later BCH reset for explanation of MX23 and MX28 handling 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); 16462306a36Sopenharmony_ci if (ret) 16562306a36Sopenharmony_ci goto err_out; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Choose NAND mode. */ 16862306a36Sopenharmony_ci writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Set the IRQ polarity. */ 17162306a36Sopenharmony_ci writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, 17262306a36Sopenharmony_ci r->gpmi_regs + HW_GPMI_CTRL1_SET); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Disable Write-Protection. */ 17562306a36Sopenharmony_ci writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Select BCH ECC. */ 17862306a36Sopenharmony_ci writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Decouple the chip select from dma channel. We use dma0 for all 18262306a36Sopenharmony_ci * the chips, force all NAND RDY_BUSY inputs to be sourced from 18362306a36Sopenharmony_ci * RDY_BUSY0. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci writel(BM_GPMI_CTRL1_DECOUPLE_CS | BM_GPMI_CTRL1_GANGED_RDYBUSY, 18662306a36Sopenharmony_ci r->gpmi_regs + HW_GPMI_CTRL1_SET); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cierr_out: 18962306a36Sopenharmony_ci pm_runtime_mark_last_busy(this->dev); 19062306a36Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 19162306a36Sopenharmony_ci return ret; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* This function is very useful. It is called only when the bug occur. */ 19562306a36Sopenharmony_cistatic void gpmi_dump_info(struct gpmi_nand_data *this) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct resources *r = &this->resources; 19862306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 19962306a36Sopenharmony_ci u32 reg; 20062306a36Sopenharmony_ci int i; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci dev_err(this->dev, "Show GPMI registers :\n"); 20362306a36Sopenharmony_ci for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { 20462306a36Sopenharmony_ci reg = readl(r->gpmi_regs + i * 0x10); 20562306a36Sopenharmony_ci dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* start to print out the BCH info */ 20962306a36Sopenharmony_ci dev_err(this->dev, "Show BCH registers :\n"); 21062306a36Sopenharmony_ci for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { 21162306a36Sopenharmony_ci reg = readl(r->bch_regs + i * 0x10); 21262306a36Sopenharmony_ci dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci dev_err(this->dev, "BCH Geometry :\n" 21562306a36Sopenharmony_ci "GF length : %u\n" 21662306a36Sopenharmony_ci "ECC Strength : %u\n" 21762306a36Sopenharmony_ci "Page Size in Bytes : %u\n" 21862306a36Sopenharmony_ci "Metadata Size in Bytes : %u\n" 21962306a36Sopenharmony_ci "ECC0 Chunk Size in Bytes: %u\n" 22062306a36Sopenharmony_ci "ECCn Chunk Size in Bytes: %u\n" 22162306a36Sopenharmony_ci "ECC Chunk Count : %u\n" 22262306a36Sopenharmony_ci "Payload Size in Bytes : %u\n" 22362306a36Sopenharmony_ci "Auxiliary Size in Bytes: %u\n" 22462306a36Sopenharmony_ci "Auxiliary Status Offset: %u\n" 22562306a36Sopenharmony_ci "Block Mark Byte Offset : %u\n" 22662306a36Sopenharmony_ci "Block Mark Bit Offset : %u\n", 22762306a36Sopenharmony_ci geo->gf_len, 22862306a36Sopenharmony_ci geo->ecc_strength, 22962306a36Sopenharmony_ci geo->page_size, 23062306a36Sopenharmony_ci geo->metadata_size, 23162306a36Sopenharmony_ci geo->ecc0_chunk_size, 23262306a36Sopenharmony_ci geo->eccn_chunk_size, 23362306a36Sopenharmony_ci geo->ecc_chunk_count, 23462306a36Sopenharmony_ci geo->payload_size, 23562306a36Sopenharmony_ci geo->auxiliary_size, 23662306a36Sopenharmony_ci geo->auxiliary_status_offset, 23762306a36Sopenharmony_ci geo->block_mark_byte_offset, 23862306a36Sopenharmony_ci geo->block_mark_bit_offset); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic bool gpmi_check_ecc(struct gpmi_nand_data *this) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 24462306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 24562306a36Sopenharmony_ci struct nand_device *nand = &chip->base; 24662306a36Sopenharmony_ci struct nand_ecc_props *conf = &nand->ecc.ctx.conf; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci conf->step_size = geo->eccn_chunk_size; 24962306a36Sopenharmony_ci conf->strength = geo->ecc_strength; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Do the sanity check. */ 25262306a36Sopenharmony_ci if (GPMI_IS_MXS(this)) { 25362306a36Sopenharmony_ci /* The mx23/mx28 only support the GF13. */ 25462306a36Sopenharmony_ci if (geo->gf_len == 14) 25562306a36Sopenharmony_ci return false; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (geo->ecc_strength > this->devdata->bch_max_ecc_strength) 25962306a36Sopenharmony_ci return false; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (!nand_ecc_is_strong_enough(nand)) 26262306a36Sopenharmony_ci return false; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return true; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* check if bbm locates in data chunk rather than ecc chunk */ 26862306a36Sopenharmony_cistatic bool bbm_in_data_chunk(struct gpmi_nand_data *this, 26962306a36Sopenharmony_ci unsigned int *chunk_num) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 27262306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 27362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 27462306a36Sopenharmony_ci unsigned int i, j; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (geo->ecc0_chunk_size != geo->eccn_chunk_size) { 27762306a36Sopenharmony_ci dev_err(this->dev, 27862306a36Sopenharmony_ci "The size of ecc0_chunk must equal to eccn_chunk\n"); 27962306a36Sopenharmony_ci return false; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci i = (mtd->writesize * 8 - geo->metadata_size * 8) / 28362306a36Sopenharmony_ci (geo->gf_len * geo->ecc_strength + 28462306a36Sopenharmony_ci geo->eccn_chunk_size * 8); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci j = (mtd->writesize * 8 - geo->metadata_size * 8) - 28762306a36Sopenharmony_ci (geo->gf_len * geo->ecc_strength + 28862306a36Sopenharmony_ci geo->eccn_chunk_size * 8) * i; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (j < geo->eccn_chunk_size * 8) { 29162306a36Sopenharmony_ci *chunk_num = i+1; 29262306a36Sopenharmony_ci dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n", 29362306a36Sopenharmony_ci geo->ecc_strength, *chunk_num); 29462306a36Sopenharmony_ci return true; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return false; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* 30162306a36Sopenharmony_ci * If we can get the ECC information from the nand chip, we do not 30262306a36Sopenharmony_ci * need to calculate them ourselves. 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * We may have available oob space in this case. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_cistatic int set_geometry_by_ecc_info(struct gpmi_nand_data *this, 30762306a36Sopenharmony_ci unsigned int ecc_strength, 30862306a36Sopenharmony_ci unsigned int ecc_step) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 31162306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 31262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 31362306a36Sopenharmony_ci unsigned int block_mark_bit_offset; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci switch (ecc_step) { 31662306a36Sopenharmony_ci case SZ_512: 31762306a36Sopenharmony_ci geo->gf_len = 13; 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case SZ_1K: 32062306a36Sopenharmony_ci geo->gf_len = 14; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci default: 32362306a36Sopenharmony_ci dev_err(this->dev, 32462306a36Sopenharmony_ci "unsupported nand chip. ecc bits : %d, ecc size : %d\n", 32562306a36Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base)->strength, 32662306a36Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base)->step_size); 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci geo->ecc0_chunk_size = ecc_step; 33062306a36Sopenharmony_ci geo->eccn_chunk_size = ecc_step; 33162306a36Sopenharmony_ci geo->ecc_strength = round_up(ecc_strength, 2); 33262306a36Sopenharmony_ci if (!gpmi_check_ecc(this)) 33362306a36Sopenharmony_ci return -EINVAL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Keep the C >= O */ 33662306a36Sopenharmony_ci if (geo->eccn_chunk_size < mtd->oobsize) { 33762306a36Sopenharmony_ci dev_err(this->dev, 33862306a36Sopenharmony_ci "unsupported nand chip. ecc size: %d, oob size : %d\n", 33962306a36Sopenharmony_ci ecc_step, mtd->oobsize); 34062306a36Sopenharmony_ci return -EINVAL; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* The default value, see comment in the legacy_set_geometry(). */ 34462306a36Sopenharmony_ci geo->metadata_size = 10; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * | P | 35262306a36Sopenharmony_ci * |<----------------------------------------------------->| 35362306a36Sopenharmony_ci * | | 35462306a36Sopenharmony_ci * | (Block Mark) | 35562306a36Sopenharmony_ci * | P' | | | | 35662306a36Sopenharmony_ci * |<-------------------------------------------->| D | | O' | 35762306a36Sopenharmony_ci * | |<---->| |<--->| 35862306a36Sopenharmony_ci * V V V V V 35962306a36Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+-----+ 36062306a36Sopenharmony_ci * | M | data |E| data |E| data |E| data |E| | 36162306a36Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+-----+ 36262306a36Sopenharmony_ci * ^ ^ 36362306a36Sopenharmony_ci * | O | 36462306a36Sopenharmony_ci * |<------------>| 36562306a36Sopenharmony_ci * | | 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * P : the page size for BCH module. 36862306a36Sopenharmony_ci * E : The ECC strength. 36962306a36Sopenharmony_ci * G : the length of Galois Field. 37062306a36Sopenharmony_ci * N : The chunk count of per page. 37162306a36Sopenharmony_ci * M : the metasize of per page. 37262306a36Sopenharmony_ci * C : the ecc chunk size, aka the "data" above. 37362306a36Sopenharmony_ci * P': the nand chip's page size. 37462306a36Sopenharmony_ci * O : the nand chip's oob size. 37562306a36Sopenharmony_ci * O': the free oob. 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * The formula for P is : 37862306a36Sopenharmony_ci * 37962306a36Sopenharmony_ci * E * G * N 38062306a36Sopenharmony_ci * P = ------------ + P' + M 38162306a36Sopenharmony_ci * 8 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * The position of block mark moves forward in the ECC-based view 38462306a36Sopenharmony_ci * of page, and the delta is: 38562306a36Sopenharmony_ci * 38662306a36Sopenharmony_ci * E * G * (N - 1) 38762306a36Sopenharmony_ci * D = (---------------- + M) 38862306a36Sopenharmony_ci * 8 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Please see the comment in legacy_set_geometry(). 39162306a36Sopenharmony_ci * With the condition C >= O , we still can get same result. 39262306a36Sopenharmony_ci * So the bit position of the physical block mark within the ECC-based 39362306a36Sopenharmony_ci * view of the page is : 39462306a36Sopenharmony_ci * (P' - D) * 8 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci geo->page_size = mtd->writesize + geo->metadata_size + 39762306a36Sopenharmony_ci (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci geo->payload_size = mtd->writesize; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); 40262306a36Sopenharmony_ci geo->auxiliary_size = ALIGN(geo->metadata_size, 4) 40362306a36Sopenharmony_ci + ALIGN(geo->ecc_chunk_count, 4); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!this->swap_block_mark) 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* For bit swap. */ 40962306a36Sopenharmony_ci block_mark_bit_offset = mtd->writesize * 8 - 41062306a36Sopenharmony_ci (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) 41162306a36Sopenharmony_ci + geo->metadata_size * 8); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci geo->block_mark_byte_offset = block_mark_bit_offset / 8; 41462306a36Sopenharmony_ci geo->block_mark_bit_offset = block_mark_bit_offset % 8; 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* 41962306a36Sopenharmony_ci * Calculate the ECC strength by hand: 42062306a36Sopenharmony_ci * E : The ECC strength. 42162306a36Sopenharmony_ci * G : the length of Galois Field. 42262306a36Sopenharmony_ci * N : The chunk count of per page. 42362306a36Sopenharmony_ci * O : the oobsize of the NAND chip. 42462306a36Sopenharmony_ci * M : the metasize of per page. 42562306a36Sopenharmony_ci * 42662306a36Sopenharmony_ci * The formula is : 42762306a36Sopenharmony_ci * E * G * N 42862306a36Sopenharmony_ci * ------------ <= (O - M) 42962306a36Sopenharmony_ci * 8 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * So, we get E by: 43262306a36Sopenharmony_ci * (O - M) * 8 43362306a36Sopenharmony_ci * E <= ------------- 43462306a36Sopenharmony_ci * G * N 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_cistatic inline int get_ecc_strength(struct gpmi_nand_data *this) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 43962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 44062306a36Sopenharmony_ci int ecc_strength; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) 44362306a36Sopenharmony_ci / (geo->gf_len * geo->ecc_chunk_count); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* We need the minor even number. */ 44662306a36Sopenharmony_ci return round_down(ecc_strength, 2); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int set_geometry_for_large_oob(struct gpmi_nand_data *this) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 45262306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 45362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 45462306a36Sopenharmony_ci const struct nand_ecc_props *requirements = 45562306a36Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 45662306a36Sopenharmony_ci unsigned int block_mark_bit_offset; 45762306a36Sopenharmony_ci unsigned int max_ecc; 45862306a36Sopenharmony_ci unsigned int bbm_chunk; 45962306a36Sopenharmony_ci unsigned int i; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* sanity check for the minimum ecc nand required */ 46262306a36Sopenharmony_ci if (!(requirements->strength > 0 && 46362306a36Sopenharmony_ci requirements->step_size > 0)) 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci geo->ecc_strength = requirements->strength; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* check if platform can support this nand */ 46862306a36Sopenharmony_ci if (!gpmi_check_ecc(this)) { 46962306a36Sopenharmony_ci dev_err(this->dev, 47062306a36Sopenharmony_ci "unsupported NAND chip, minimum ecc required %d\n", 47162306a36Sopenharmony_ci geo->ecc_strength); 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* calculate the maximum ecc platform can support*/ 47662306a36Sopenharmony_ci geo->metadata_size = 10; 47762306a36Sopenharmony_ci geo->gf_len = 14; 47862306a36Sopenharmony_ci geo->ecc0_chunk_size = 1024; 47962306a36Sopenharmony_ci geo->eccn_chunk_size = 1024; 48062306a36Sopenharmony_ci geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; 48162306a36Sopenharmony_ci max_ecc = min(get_ecc_strength(this), 48262306a36Sopenharmony_ci this->devdata->bch_max_ecc_strength); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * search a supported ecc strength that makes bbm 48662306a36Sopenharmony_ci * located in data chunk 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci geo->ecc_strength = max_ecc; 48962306a36Sopenharmony_ci while (!(geo->ecc_strength < requirements->strength)) { 49062306a36Sopenharmony_ci if (bbm_in_data_chunk(this, &bbm_chunk)) 49162306a36Sopenharmony_ci goto geo_setting; 49262306a36Sopenharmony_ci geo->ecc_strength -= 2; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci /* if none of them works, keep using the minimum ecc */ 49662306a36Sopenharmony_ci /* nand required but changing ecc page layout */ 49762306a36Sopenharmony_ci geo->ecc_strength = requirements->strength; 49862306a36Sopenharmony_ci /* add extra ecc for meta data */ 49962306a36Sopenharmony_ci geo->ecc0_chunk_size = 0; 50062306a36Sopenharmony_ci geo->ecc_chunk_count = (mtd->writesize / geo->eccn_chunk_size) + 1; 50162306a36Sopenharmony_ci geo->ecc_for_meta = 1; 50262306a36Sopenharmony_ci /* check if oob can afford this extra ecc chunk */ 50362306a36Sopenharmony_ci if (mtd->oobsize * 8 < geo->metadata_size * 8 + 50462306a36Sopenharmony_ci geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) { 50562306a36Sopenharmony_ci dev_err(this->dev, "unsupported NAND chip with new layout\n"); 50662306a36Sopenharmony_ci return -EINVAL; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* calculate in which chunk bbm located */ 51062306a36Sopenharmony_ci bbm_chunk = (mtd->writesize * 8 - geo->metadata_size * 8 - 51162306a36Sopenharmony_ci geo->gf_len * geo->ecc_strength) / 51262306a36Sopenharmony_ci (geo->gf_len * geo->ecc_strength + 51362306a36Sopenharmony_ci geo->eccn_chunk_size * 8) + 1; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cigeo_setting: 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci geo->page_size = mtd->writesize + geo->metadata_size + 51862306a36Sopenharmony_ci (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; 51962306a36Sopenharmony_ci geo->payload_size = mtd->writesize; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * The auxiliary buffer contains the metadata and the ECC status. The 52362306a36Sopenharmony_ci * metadata is padded to the nearest 32-bit boundary. The ECC status 52462306a36Sopenharmony_ci * contains one byte for every ECC chunk, and is also padded to the 52562306a36Sopenharmony_ci * nearest 32-bit boundary. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); 52862306a36Sopenharmony_ci geo->auxiliary_size = ALIGN(geo->metadata_size, 4) 52962306a36Sopenharmony_ci + ALIGN(geo->ecc_chunk_count, 4); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (!this->swap_block_mark) 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* calculate the number of ecc chunk behind the bbm */ 53562306a36Sopenharmony_ci i = (mtd->writesize / geo->eccn_chunk_size) - bbm_chunk + 1; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci block_mark_bit_offset = mtd->writesize * 8 - 53862306a36Sopenharmony_ci (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i) 53962306a36Sopenharmony_ci + geo->metadata_size * 8); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci geo->block_mark_byte_offset = block_mark_bit_offset / 8; 54262306a36Sopenharmony_ci geo->block_mark_bit_offset = block_mark_bit_offset % 8; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci dev_dbg(this->dev, "BCH Geometry :\n" 54562306a36Sopenharmony_ci "GF length : %u\n" 54662306a36Sopenharmony_ci "ECC Strength : %u\n" 54762306a36Sopenharmony_ci "Page Size in Bytes : %u\n" 54862306a36Sopenharmony_ci "Metadata Size in Bytes : %u\n" 54962306a36Sopenharmony_ci "ECC0 Chunk Size in Bytes: %u\n" 55062306a36Sopenharmony_ci "ECCn Chunk Size in Bytes: %u\n" 55162306a36Sopenharmony_ci "ECC Chunk Count : %u\n" 55262306a36Sopenharmony_ci "Payload Size in Bytes : %u\n" 55362306a36Sopenharmony_ci "Auxiliary Size in Bytes: %u\n" 55462306a36Sopenharmony_ci "Auxiliary Status Offset: %u\n" 55562306a36Sopenharmony_ci "Block Mark Byte Offset : %u\n" 55662306a36Sopenharmony_ci "Block Mark Bit Offset : %u\n" 55762306a36Sopenharmony_ci "Block Mark in chunk : %u\n" 55862306a36Sopenharmony_ci "Ecc for Meta data : %u\n", 55962306a36Sopenharmony_ci geo->gf_len, 56062306a36Sopenharmony_ci geo->ecc_strength, 56162306a36Sopenharmony_ci geo->page_size, 56262306a36Sopenharmony_ci geo->metadata_size, 56362306a36Sopenharmony_ci geo->ecc0_chunk_size, 56462306a36Sopenharmony_ci geo->eccn_chunk_size, 56562306a36Sopenharmony_ci geo->ecc_chunk_count, 56662306a36Sopenharmony_ci geo->payload_size, 56762306a36Sopenharmony_ci geo->auxiliary_size, 56862306a36Sopenharmony_ci geo->auxiliary_status_offset, 56962306a36Sopenharmony_ci geo->block_mark_byte_offset, 57062306a36Sopenharmony_ci geo->block_mark_bit_offset, 57162306a36Sopenharmony_ci bbm_chunk, 57262306a36Sopenharmony_ci geo->ecc_for_meta); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int legacy_set_geometry(struct gpmi_nand_data *this) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 58062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 58162306a36Sopenharmony_ci unsigned int metadata_size; 58262306a36Sopenharmony_ci unsigned int status_size; 58362306a36Sopenharmony_ci unsigned int block_mark_bit_offset; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * The size of the metadata can be changed, though we set it to 10 58762306a36Sopenharmony_ci * bytes now. But it can't be too large, because we have to save 58862306a36Sopenharmony_ci * enough space for BCH. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci geo->metadata_size = 10; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* The default for the length of Galois Field. */ 59362306a36Sopenharmony_ci geo->gf_len = 13; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* The default for chunk size. */ 59662306a36Sopenharmony_ci geo->ecc0_chunk_size = 512; 59762306a36Sopenharmony_ci geo->eccn_chunk_size = 512; 59862306a36Sopenharmony_ci while (geo->eccn_chunk_size < mtd->oobsize) { 59962306a36Sopenharmony_ci geo->ecc0_chunk_size *= 2; /* keep C >= O */ 60062306a36Sopenharmony_ci geo->eccn_chunk_size *= 2; /* keep C >= O */ 60162306a36Sopenharmony_ci geo->gf_len = 14; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci geo->ecc_chunk_count = mtd->writesize / geo->eccn_chunk_size; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* We use the same ECC strength for all chunks. */ 60762306a36Sopenharmony_ci geo->ecc_strength = get_ecc_strength(this); 60862306a36Sopenharmony_ci if (!gpmi_check_ecc(this)) { 60962306a36Sopenharmony_ci dev_err(this->dev, 61062306a36Sopenharmony_ci "ecc strength: %d cannot be supported by the controller (%d)\n" 61162306a36Sopenharmony_ci "try to use minimum ecc strength that NAND chip required\n", 61262306a36Sopenharmony_ci geo->ecc_strength, 61362306a36Sopenharmony_ci this->devdata->bch_max_ecc_strength); 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci geo->page_size = mtd->writesize + geo->metadata_size + 61862306a36Sopenharmony_ci (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; 61962306a36Sopenharmony_ci geo->payload_size = mtd->writesize; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * The auxiliary buffer contains the metadata and the ECC status. The 62362306a36Sopenharmony_ci * metadata is padded to the nearest 32-bit boundary. The ECC status 62462306a36Sopenharmony_ci * contains one byte for every ECC chunk, and is also padded to the 62562306a36Sopenharmony_ci * nearest 32-bit boundary. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci metadata_size = ALIGN(geo->metadata_size, 4); 62862306a36Sopenharmony_ci status_size = ALIGN(geo->ecc_chunk_count, 4); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci geo->auxiliary_size = metadata_size + status_size; 63162306a36Sopenharmony_ci geo->auxiliary_status_offset = metadata_size; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (!this->swap_block_mark) 63462306a36Sopenharmony_ci return 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* 63762306a36Sopenharmony_ci * We need to compute the byte and bit offsets of 63862306a36Sopenharmony_ci * the physical block mark within the ECC-based view of the page. 63962306a36Sopenharmony_ci * 64062306a36Sopenharmony_ci * NAND chip with 2K page shows below: 64162306a36Sopenharmony_ci * (Block Mark) 64262306a36Sopenharmony_ci * | | 64362306a36Sopenharmony_ci * | D | 64462306a36Sopenharmony_ci * |<---->| 64562306a36Sopenharmony_ci * V V 64662306a36Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+ 64762306a36Sopenharmony_ci * | M | data |E| data |E| data |E| data |E| 64862306a36Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+ 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci * The position of block mark moves forward in the ECC-based view 65162306a36Sopenharmony_ci * of page, and the delta is: 65262306a36Sopenharmony_ci * 65362306a36Sopenharmony_ci * E * G * (N - 1) 65462306a36Sopenharmony_ci * D = (---------------- + M) 65562306a36Sopenharmony_ci * 8 65662306a36Sopenharmony_ci * 65762306a36Sopenharmony_ci * With the formula to compute the ECC strength, and the condition 65862306a36Sopenharmony_ci * : C >= O (C is the ecc chunk size) 65962306a36Sopenharmony_ci * 66062306a36Sopenharmony_ci * It's easy to deduce to the following result: 66162306a36Sopenharmony_ci * 66262306a36Sopenharmony_ci * E * G (O - M) C - M C - M 66362306a36Sopenharmony_ci * ----------- <= ------- <= -------- < --------- 66462306a36Sopenharmony_ci * 8 N N (N - 1) 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * So, we get: 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * E * G * (N - 1) 66962306a36Sopenharmony_ci * D = (---------------- + M) < C 67062306a36Sopenharmony_ci * 8 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * The above inequality means the position of block mark 67362306a36Sopenharmony_ci * within the ECC-based view of the page is still in the data chunk, 67462306a36Sopenharmony_ci * and it's NOT in the ECC bits of the chunk. 67562306a36Sopenharmony_ci * 67662306a36Sopenharmony_ci * Use the following to compute the bit position of the 67762306a36Sopenharmony_ci * physical block mark within the ECC-based view of the page: 67862306a36Sopenharmony_ci * (page_size - D) * 8 67962306a36Sopenharmony_ci * 68062306a36Sopenharmony_ci * --Huang Shijie 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_ci block_mark_bit_offset = mtd->writesize * 8 - 68362306a36Sopenharmony_ci (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) 68462306a36Sopenharmony_ci + geo->metadata_size * 8); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci geo->block_mark_byte_offset = block_mark_bit_offset / 8; 68762306a36Sopenharmony_ci geo->block_mark_bit_offset = block_mark_bit_offset % 8; 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int common_nfc_set_geometry(struct gpmi_nand_data *this) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 69462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 69562306a36Sopenharmony_ci const struct nand_ecc_props *requirements = 69662306a36Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 69762306a36Sopenharmony_ci bool use_minimun_ecc; 69862306a36Sopenharmony_ci int err; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci use_minimun_ecc = of_property_read_bool(this->dev->of_node, 70162306a36Sopenharmony_ci "fsl,use-minimum-ecc"); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* use legacy bch geometry settings by default*/ 70462306a36Sopenharmony_ci if ((!use_minimun_ecc && mtd->oobsize < 1024) || 70562306a36Sopenharmony_ci !(requirements->strength > 0 && requirements->step_size > 0)) { 70662306a36Sopenharmony_ci dev_dbg(this->dev, "use legacy bch geometry\n"); 70762306a36Sopenharmony_ci err = legacy_set_geometry(this); 70862306a36Sopenharmony_ci if (!err) 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* for large oob nand */ 71362306a36Sopenharmony_ci if (mtd->oobsize > 1024) { 71462306a36Sopenharmony_ci dev_dbg(this->dev, "use large oob bch geometry\n"); 71562306a36Sopenharmony_ci err = set_geometry_for_large_oob(this); 71662306a36Sopenharmony_ci if (!err) 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* otherwise use the minimum ecc nand chip required */ 72162306a36Sopenharmony_ci dev_dbg(this->dev, "use minimum ecc bch geometry\n"); 72262306a36Sopenharmony_ci err = set_geometry_by_ecc_info(this, requirements->strength, 72362306a36Sopenharmony_ci requirements->step_size); 72462306a36Sopenharmony_ci if (err) 72562306a36Sopenharmony_ci dev_err(this->dev, "none of the bch geometry setting works\n"); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* Configures the geometry for BCH. */ 73162306a36Sopenharmony_cistatic int bch_set_geometry(struct gpmi_nand_data *this) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct resources *r = &this->resources; 73462306a36Sopenharmony_ci int ret; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci ret = common_nfc_set_geometry(this); 73762306a36Sopenharmony_ci if (ret) 73862306a36Sopenharmony_ci return ret; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci ret = pm_runtime_get_sync(this->dev); 74162306a36Sopenharmony_ci if (ret < 0) { 74262306a36Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 74362306a36Sopenharmony_ci return ret; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* 74762306a36Sopenharmony_ci * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this 74862306a36Sopenharmony_ci * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. 74962306a36Sopenharmony_ci * and MX28. 75062306a36Sopenharmony_ci */ 75162306a36Sopenharmony_ci ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); 75262306a36Sopenharmony_ci if (ret) 75362306a36Sopenharmony_ci goto err_out; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Set *all* chip selects to use layout 0. */ 75662306a36Sopenharmony_ci writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ret = 0; 75962306a36Sopenharmony_cierr_out: 76062306a36Sopenharmony_ci pm_runtime_mark_last_busy(this->dev); 76162306a36Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return ret; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci/* 76762306a36Sopenharmony_ci * <1> Firstly, we should know what's the GPMI-clock means. 76862306a36Sopenharmony_ci * The GPMI-clock is the internal clock in the gpmi nand controller. 76962306a36Sopenharmony_ci * If you set 100MHz to gpmi nand controller, the GPMI-clock's period 77062306a36Sopenharmony_ci * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. 77162306a36Sopenharmony_ci * 77262306a36Sopenharmony_ci * <2> Secondly, we should know what's the frequency on the nand chip pins. 77362306a36Sopenharmony_ci * The frequency on the nand chip pins is derived from the GPMI-clock. 77462306a36Sopenharmony_ci * We can get it from the following equation: 77562306a36Sopenharmony_ci * 77662306a36Sopenharmony_ci * F = G / (DS + DH) 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * F : the frequency on the nand chip pins. 77962306a36Sopenharmony_ci * G : the GPMI clock, such as 100MHz. 78062306a36Sopenharmony_ci * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP 78162306a36Sopenharmony_ci * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD 78262306a36Sopenharmony_ci * 78362306a36Sopenharmony_ci * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, 78462306a36Sopenharmony_ci * the nand EDO(extended Data Out) timing could be applied. 78562306a36Sopenharmony_ci * The GPMI implements a feedback read strobe to sample the read data. 78662306a36Sopenharmony_ci * The feedback read strobe can be delayed to support the nand EDO timing 78762306a36Sopenharmony_ci * where the read strobe may deasserts before the read data is valid, and 78862306a36Sopenharmony_ci * read data is valid for some time after read strobe. 78962306a36Sopenharmony_ci * 79062306a36Sopenharmony_ci * The following figure illustrates some aspects of a NAND Flash read: 79162306a36Sopenharmony_ci * 79262306a36Sopenharmony_ci * |<---tREA---->| 79362306a36Sopenharmony_ci * | | 79462306a36Sopenharmony_ci * | | | 79562306a36Sopenharmony_ci * |<--tRP-->| | 79662306a36Sopenharmony_ci * | | | 79762306a36Sopenharmony_ci * __ ___|__________________________________ 79862306a36Sopenharmony_ci * RDN \________/ | 79962306a36Sopenharmony_ci * | 80062306a36Sopenharmony_ci * /---------\ 80162306a36Sopenharmony_ci * Read Data --------------< >--------- 80262306a36Sopenharmony_ci * \---------/ 80362306a36Sopenharmony_ci * | | 80462306a36Sopenharmony_ci * |<-D->| 80562306a36Sopenharmony_ci * FeedbackRDN ________ ____________ 80662306a36Sopenharmony_ci * \___________/ 80762306a36Sopenharmony_ci * 80862306a36Sopenharmony_ci * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * 81162306a36Sopenharmony_ci * <4> Now, we begin to describe how to compute the right RDN_DELAY. 81262306a36Sopenharmony_ci * 81362306a36Sopenharmony_ci * 4.1) From the aspect of the nand chip pins: 81462306a36Sopenharmony_ci * Delay = (tREA + C - tRP) {1} 81562306a36Sopenharmony_ci * 81662306a36Sopenharmony_ci * tREA : the maximum read access time. 81762306a36Sopenharmony_ci * C : a constant to adjust the delay. default is 4000ps. 81862306a36Sopenharmony_ci * tRP : the read pulse width, which is exactly: 81962306a36Sopenharmony_ci * tRP = (GPMI-clock-period) * DATA_SETUP 82062306a36Sopenharmony_ci * 82162306a36Sopenharmony_ci * 4.2) From the aspect of the GPMI nand controller: 82262306a36Sopenharmony_ci * Delay = RDN_DELAY * 0.125 * RP {2} 82362306a36Sopenharmony_ci * 82462306a36Sopenharmony_ci * RP : the DLL reference period. 82562306a36Sopenharmony_ci * if (GPMI-clock-period > DLL_THRETHOLD) 82662306a36Sopenharmony_ci * RP = GPMI-clock-period / 2; 82762306a36Sopenharmony_ci * else 82862306a36Sopenharmony_ci * RP = GPMI-clock-period; 82962306a36Sopenharmony_ci * 83062306a36Sopenharmony_ci * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period 83162306a36Sopenharmony_ci * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD 83262306a36Sopenharmony_ci * is 16000ps, but in mx6q, we use 12000ps. 83362306a36Sopenharmony_ci * 83462306a36Sopenharmony_ci * 4.3) since {1} equals {2}, we get: 83562306a36Sopenharmony_ci * 83662306a36Sopenharmony_ci * (tREA + 4000 - tRP) * 8 83762306a36Sopenharmony_ci * RDN_DELAY = ----------------------- {3} 83862306a36Sopenharmony_ci * RP 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_cistatic int gpmi_nfc_compute_timings(struct gpmi_nand_data *this, 84162306a36Sopenharmony_ci const struct nand_sdr_timings *sdr) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct gpmi_nfc_hardware_timing *hw = &this->hw; 84462306a36Sopenharmony_ci struct resources *r = &this->resources; 84562306a36Sopenharmony_ci unsigned int dll_threshold_ps = this->devdata->max_chain_delay; 84662306a36Sopenharmony_ci unsigned int period_ps, reference_period_ps; 84762306a36Sopenharmony_ci unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; 84862306a36Sopenharmony_ci unsigned int tRP_ps; 84962306a36Sopenharmony_ci bool use_half_period; 85062306a36Sopenharmony_ci int sample_delay_ps, sample_delay_factor; 85162306a36Sopenharmony_ci unsigned int busy_timeout_cycles; 85262306a36Sopenharmony_ci u8 wrn_dly_sel; 85362306a36Sopenharmony_ci unsigned long clk_rate, min_rate; 85462306a36Sopenharmony_ci u64 busy_timeout_ps; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (sdr->tRC_min >= 30000) { 85762306a36Sopenharmony_ci /* ONFI non-EDO modes [0-3] */ 85862306a36Sopenharmony_ci hw->clk_rate = 22000000; 85962306a36Sopenharmony_ci min_rate = 0; 86062306a36Sopenharmony_ci wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; 86162306a36Sopenharmony_ci } else if (sdr->tRC_min >= 25000) { 86262306a36Sopenharmony_ci /* ONFI EDO mode 4 */ 86362306a36Sopenharmony_ci hw->clk_rate = 80000000; 86462306a36Sopenharmony_ci min_rate = 22000000; 86562306a36Sopenharmony_ci wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; 86662306a36Sopenharmony_ci } else { 86762306a36Sopenharmony_ci /* ONFI EDO mode 5 */ 86862306a36Sopenharmony_ci hw->clk_rate = 100000000; 86962306a36Sopenharmony_ci min_rate = 80000000; 87062306a36Sopenharmony_ci wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci clk_rate = clk_round_rate(r->clock[0], hw->clk_rate); 87462306a36Sopenharmony_ci if (clk_rate <= min_rate) { 87562306a36Sopenharmony_ci dev_err(this->dev, "clock setting: expected %ld, got %ld\n", 87662306a36Sopenharmony_ci hw->clk_rate, clk_rate); 87762306a36Sopenharmony_ci return -ENOTSUPP; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci hw->clk_rate = clk_rate; 88162306a36Sopenharmony_ci /* SDR core timings are given in picoseconds */ 88262306a36Sopenharmony_ci period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps); 88562306a36Sopenharmony_ci data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps); 88662306a36Sopenharmony_ci data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps); 88762306a36Sopenharmony_ci busy_timeout_ps = max(sdr->tBERS_max, sdr->tPROG_max); 88862306a36Sopenharmony_ci busy_timeout_cycles = TO_CYCLES(busy_timeout_ps, period_ps); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | 89162306a36Sopenharmony_ci BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | 89262306a36Sopenharmony_ci BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); 89362306a36Sopenharmony_ci hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(DIV_ROUND_UP(busy_timeout_cycles, 4096)); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* 89662306a36Sopenharmony_ci * Derive NFC ideal delay from {3}: 89762306a36Sopenharmony_ci * 89862306a36Sopenharmony_ci * (tREA + 4000 - tRP) * 8 89962306a36Sopenharmony_ci * RDN_DELAY = ----------------------- 90062306a36Sopenharmony_ci * RP 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_ci if (period_ps > dll_threshold_ps) { 90362306a36Sopenharmony_ci use_half_period = true; 90462306a36Sopenharmony_ci reference_period_ps = period_ps / 2; 90562306a36Sopenharmony_ci } else { 90662306a36Sopenharmony_ci use_half_period = false; 90762306a36Sopenharmony_ci reference_period_ps = period_ps; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci tRP_ps = data_setup_cycles * period_ps; 91162306a36Sopenharmony_ci sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8; 91262306a36Sopenharmony_ci if (sample_delay_ps > 0) 91362306a36Sopenharmony_ci sample_delay_factor = sample_delay_ps / reference_period_ps; 91462306a36Sopenharmony_ci else 91562306a36Sopenharmony_ci sample_delay_factor = 0; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel); 91862306a36Sopenharmony_ci if (sample_delay_factor) 91962306a36Sopenharmony_ci hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) | 92062306a36Sopenharmony_ci BM_GPMI_CTRL1_DLL_ENABLE | 92162306a36Sopenharmony_ci (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0); 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic int gpmi_nfc_apply_timings(struct gpmi_nand_data *this) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct gpmi_nfc_hardware_timing *hw = &this->hw; 92862306a36Sopenharmony_ci struct resources *r = &this->resources; 92962306a36Sopenharmony_ci void __iomem *gpmi_regs = r->gpmi_regs; 93062306a36Sopenharmony_ci unsigned int dll_wait_time_us; 93162306a36Sopenharmony_ci int ret; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Clock dividers do NOT guarantee a clean clock signal on its output 93462306a36Sopenharmony_ci * during the change of the divide factor on i.MX6Q/UL/SX. On i.MX7/8, 93562306a36Sopenharmony_ci * all clock dividers provide these guarantee. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ci if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) 93862306a36Sopenharmony_ci clk_disable_unprepare(r->clock[0]); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci ret = clk_set_rate(r->clock[0], hw->clk_rate); 94162306a36Sopenharmony_ci if (ret) { 94262306a36Sopenharmony_ci dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret); 94362306a36Sopenharmony_ci return ret; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) { 94762306a36Sopenharmony_ci ret = clk_prepare_enable(r->clock[0]); 94862306a36Sopenharmony_ci if (ret) 94962306a36Sopenharmony_ci return ret; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0); 95362306a36Sopenharmony_ci writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* 95662306a36Sopenharmony_ci * Clear several CTRL1 fields, DLL must be disabled when setting 95762306a36Sopenharmony_ci * RDN_DELAY or HALF_PERIOD. 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_ci writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR); 96062306a36Sopenharmony_ci writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Wait 64 clock cycles before using the GPMI after enabling the DLL */ 96362306a36Sopenharmony_ci dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64; 96462306a36Sopenharmony_ci if (!dll_wait_time_us) 96562306a36Sopenharmony_ci dll_wait_time_us = 1; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* Wait for the DLL to settle. */ 96862306a36Sopenharmony_ci udelay(dll_wait_time_us); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic int gpmi_setup_interface(struct nand_chip *chip, int chipnr, 97462306a36Sopenharmony_ci const struct nand_interface_config *conf) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 97762306a36Sopenharmony_ci const struct nand_sdr_timings *sdr; 97862306a36Sopenharmony_ci int ret; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Retrieve required NAND timings */ 98162306a36Sopenharmony_ci sdr = nand_get_sdr_timings(conf); 98262306a36Sopenharmony_ci if (IS_ERR(sdr)) 98362306a36Sopenharmony_ci return PTR_ERR(sdr); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* Only MX28/MX6 GPMI controller can reach EDO timings */ 98662306a36Sopenharmony_ci if (sdr->tRC_min <= 25000 && !GPMI_IS_MX28(this) && !GPMI_IS_MX6(this)) 98762306a36Sopenharmony_ci return -ENOTSUPP; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci /* Stop here if this call was just a check */ 99062306a36Sopenharmony_ci if (chipnr < 0) 99162306a36Sopenharmony_ci return 0; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* Do the actual derivation of the controller timings */ 99462306a36Sopenharmony_ci ret = gpmi_nfc_compute_timings(this, sdr); 99562306a36Sopenharmony_ci if (ret) 99662306a36Sopenharmony_ci return ret; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci this->hw.must_apply_timings = true; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci/* Clears a BCH interrupt. */ 100462306a36Sopenharmony_cistatic void gpmi_clear_bch(struct gpmi_nand_data *this) 100562306a36Sopenharmony_ci{ 100662306a36Sopenharmony_ci struct resources *r = &this->resources; 100762306a36Sopenharmony_ci writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci /* We use the DMA channel 0 to access all the nand chips. */ 101362306a36Sopenharmony_ci return this->dma_chans[0]; 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci/* This will be called after the DMA operation is finished. */ 101762306a36Sopenharmony_cistatic void dma_irq_callback(void *param) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci struct gpmi_nand_data *this = param; 102062306a36Sopenharmony_ci struct completion *dma_c = &this->dma_done; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci complete(dma_c); 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic irqreturn_t bch_irq(int irq, void *cookie) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct gpmi_nand_data *this = cookie; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci gpmi_clear_bch(this); 103062306a36Sopenharmony_ci complete(&this->bch_done); 103162306a36Sopenharmony_ci return IRQ_HANDLED; 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci /* 103762306a36Sopenharmony_ci * raw_len is the length to read/write including bch data which 103862306a36Sopenharmony_ci * we are passed in exec_op. Calculate the data length from it. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci if (this->bch) 104162306a36Sopenharmony_ci return ALIGN_DOWN(raw_len, this->bch_geometry.eccn_chunk_size); 104262306a36Sopenharmony_ci else 104362306a36Sopenharmony_ci return raw_len; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci/* Can we use the upper's buffer directly for DMA? */ 104762306a36Sopenharmony_cistatic bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, 104862306a36Sopenharmony_ci int raw_len, struct scatterlist *sgl, 104962306a36Sopenharmony_ci enum dma_data_direction dr) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci int ret; 105262306a36Sopenharmony_ci int len = gpmi_raw_len_to_len(this, raw_len); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* first try to map the upper buffer directly */ 105562306a36Sopenharmony_ci if (virt_addr_valid(buf) && !object_is_on_stack(buf)) { 105662306a36Sopenharmony_ci sg_init_one(sgl, buf, len); 105762306a36Sopenharmony_ci ret = dma_map_sg(this->dev, sgl, 1, dr); 105862306a36Sopenharmony_ci if (ret == 0) 105962306a36Sopenharmony_ci goto map_fail; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci return true; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cimap_fail: 106562306a36Sopenharmony_ci /* We have to use our own DMA buffer. */ 106662306a36Sopenharmony_ci sg_init_one(sgl, this->data_buffer_dma, len); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (dr == DMA_TO_DEVICE && buf != this->data_buffer_dma) 106962306a36Sopenharmony_ci memcpy(this->data_buffer_dma, buf, len); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci dma_map_sg(this->dev, sgl, 1, dr); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci return false; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci/* add our owner bbt descriptor */ 107762306a36Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff }; 107862306a36Sopenharmony_cistatic struct nand_bbt_descr gpmi_bbt_descr = { 107962306a36Sopenharmony_ci .options = 0, 108062306a36Sopenharmony_ci .offs = 0, 108162306a36Sopenharmony_ci .len = 1, 108262306a36Sopenharmony_ci .pattern = scan_ff_pattern 108362306a36Sopenharmony_ci}; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci/* 108662306a36Sopenharmony_ci * We may change the layout if we can get the ECC info from the datasheet, 108762306a36Sopenharmony_ci * else we will use all the (page + OOB). 108862306a36Sopenharmony_ci */ 108962306a36Sopenharmony_cistatic int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, 109062306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 109362306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 109462306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci if (section) 109762306a36Sopenharmony_ci return -ERANGE; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci oobregion->offset = 0; 110062306a36Sopenharmony_ci oobregion->length = geo->page_size - mtd->writesize; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic int gpmi_ooblayout_free(struct mtd_info *mtd, int section, 110662306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 110962306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 111062306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (section) 111362306a36Sopenharmony_ci return -ERANGE; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* The available oob size we have. */ 111662306a36Sopenharmony_ci if (geo->page_size < mtd->writesize + mtd->oobsize) { 111762306a36Sopenharmony_ci oobregion->offset = geo->page_size - mtd->writesize; 111862306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci return 0; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic const char * const gpmi_clks_for_mx2x[] = { 112562306a36Sopenharmony_ci "gpmi_io", 112662306a36Sopenharmony_ci}; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { 112962306a36Sopenharmony_ci .ecc = gpmi_ooblayout_ecc, 113062306a36Sopenharmony_ci .free = gpmi_ooblayout_free, 113162306a36Sopenharmony_ci}; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx23 = { 113462306a36Sopenharmony_ci .type = IS_MX23, 113562306a36Sopenharmony_ci .bch_max_ecc_strength = 20, 113662306a36Sopenharmony_ci .max_chain_delay = 16000, 113762306a36Sopenharmony_ci .clks = gpmi_clks_for_mx2x, 113862306a36Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), 113962306a36Sopenharmony_ci}; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx28 = { 114262306a36Sopenharmony_ci .type = IS_MX28, 114362306a36Sopenharmony_ci .bch_max_ecc_strength = 20, 114462306a36Sopenharmony_ci .max_chain_delay = 16000, 114562306a36Sopenharmony_ci .clks = gpmi_clks_for_mx2x, 114662306a36Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), 114762306a36Sopenharmony_ci}; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic const char * const gpmi_clks_for_mx6[] = { 115062306a36Sopenharmony_ci "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", 115162306a36Sopenharmony_ci}; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx6q = { 115462306a36Sopenharmony_ci .type = IS_MX6Q, 115562306a36Sopenharmony_ci .bch_max_ecc_strength = 40, 115662306a36Sopenharmony_ci .max_chain_delay = 12000, 115762306a36Sopenharmony_ci .clks = gpmi_clks_for_mx6, 115862306a36Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), 115962306a36Sopenharmony_ci}; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx6sx = { 116262306a36Sopenharmony_ci .type = IS_MX6SX, 116362306a36Sopenharmony_ci .bch_max_ecc_strength = 62, 116462306a36Sopenharmony_ci .max_chain_delay = 12000, 116562306a36Sopenharmony_ci .clks = gpmi_clks_for_mx6, 116662306a36Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), 116762306a36Sopenharmony_ci}; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic const char * const gpmi_clks_for_mx7d[] = { 117062306a36Sopenharmony_ci "gpmi_io", "gpmi_bch_apb", 117162306a36Sopenharmony_ci}; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx7d = { 117462306a36Sopenharmony_ci .type = IS_MX7D, 117562306a36Sopenharmony_ci .bch_max_ecc_strength = 62, 117662306a36Sopenharmony_ci .max_chain_delay = 12000, 117762306a36Sopenharmony_ci .clks = gpmi_clks_for_mx7d, 117862306a36Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), 117962306a36Sopenharmony_ci}; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cistatic int acquire_register_block(struct gpmi_nand_data *this, 118262306a36Sopenharmony_ci const char *res_name) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct platform_device *pdev = this->pdev; 118562306a36Sopenharmony_ci struct resources *res = &this->resources; 118662306a36Sopenharmony_ci void __iomem *p; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci p = devm_platform_ioremap_resource_byname(pdev, res_name); 118962306a36Sopenharmony_ci if (IS_ERR(p)) 119062306a36Sopenharmony_ci return PTR_ERR(p); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)) 119362306a36Sopenharmony_ci res->gpmi_regs = p; 119462306a36Sopenharmony_ci else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME)) 119562306a36Sopenharmony_ci res->bch_regs = p; 119662306a36Sopenharmony_ci else 119762306a36Sopenharmony_ci dev_err(this->dev, "unknown resource name : %s\n", res_name); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return 0; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct platform_device *pdev = this->pdev; 120562306a36Sopenharmony_ci const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME; 120662306a36Sopenharmony_ci int err; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci err = platform_get_irq_byname(pdev, res_name); 120962306a36Sopenharmony_ci if (err < 0) 121062306a36Sopenharmony_ci return err; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci err = devm_request_irq(this->dev, err, irq_h, 0, res_name, this); 121362306a36Sopenharmony_ci if (err) 121462306a36Sopenharmony_ci dev_err(this->dev, "error requesting BCH IRQ\n"); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return err; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic void release_dma_channels(struct gpmi_nand_data *this) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci unsigned int i; 122262306a36Sopenharmony_ci for (i = 0; i < DMA_CHANS; i++) 122362306a36Sopenharmony_ci if (this->dma_chans[i]) { 122462306a36Sopenharmony_ci dma_release_channel(this->dma_chans[i]); 122562306a36Sopenharmony_ci this->dma_chans[i] = NULL; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci} 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_cistatic int acquire_dma_channels(struct gpmi_nand_data *this) 123062306a36Sopenharmony_ci{ 123162306a36Sopenharmony_ci struct platform_device *pdev = this->pdev; 123262306a36Sopenharmony_ci struct dma_chan *dma_chan; 123362306a36Sopenharmony_ci int ret = 0; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci /* request dma channel */ 123662306a36Sopenharmony_ci dma_chan = dma_request_chan(&pdev->dev, "rx-tx"); 123762306a36Sopenharmony_ci if (IS_ERR(dma_chan)) { 123862306a36Sopenharmony_ci ret = dev_err_probe(this->dev, PTR_ERR(dma_chan), 123962306a36Sopenharmony_ci "DMA channel request failed\n"); 124062306a36Sopenharmony_ci release_dma_channels(this); 124162306a36Sopenharmony_ci } else { 124262306a36Sopenharmony_ci this->dma_chans[0] = dma_chan; 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci return ret; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic int gpmi_get_clks(struct gpmi_nand_data *this) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct resources *r = &this->resources; 125162306a36Sopenharmony_ci struct clk *clk; 125262306a36Sopenharmony_ci int err, i; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci for (i = 0; i < this->devdata->clks_count; i++) { 125562306a36Sopenharmony_ci clk = devm_clk_get(this->dev, this->devdata->clks[i]); 125662306a36Sopenharmony_ci if (IS_ERR(clk)) { 125762306a36Sopenharmony_ci err = PTR_ERR(clk); 125862306a36Sopenharmony_ci goto err_clock; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci r->clock[i] = clk; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci return 0; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cierr_clock: 126762306a36Sopenharmony_ci dev_dbg(this->dev, "failed in finding the clocks.\n"); 126862306a36Sopenharmony_ci return err; 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic int acquire_resources(struct gpmi_nand_data *this) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci int ret; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); 127662306a36Sopenharmony_ci if (ret) 127762306a36Sopenharmony_ci goto exit_regs; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME); 128062306a36Sopenharmony_ci if (ret) 128162306a36Sopenharmony_ci goto exit_regs; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci ret = acquire_bch_irq(this, bch_irq); 128462306a36Sopenharmony_ci if (ret) 128562306a36Sopenharmony_ci goto exit_regs; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci ret = acquire_dma_channels(this); 128862306a36Sopenharmony_ci if (ret) 128962306a36Sopenharmony_ci goto exit_regs; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci ret = gpmi_get_clks(this); 129262306a36Sopenharmony_ci if (ret) 129362306a36Sopenharmony_ci goto exit_clock; 129462306a36Sopenharmony_ci return 0; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ciexit_clock: 129762306a36Sopenharmony_ci release_dma_channels(this); 129862306a36Sopenharmony_ciexit_regs: 129962306a36Sopenharmony_ci return ret; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic void release_resources(struct gpmi_nand_data *this) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci release_dma_channels(this); 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic void gpmi_free_dma_buffer(struct gpmi_nand_data *this) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci struct device *dev = this->dev; 131062306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (this->auxiliary_virt && virt_addr_valid(this->auxiliary_virt)) 131362306a36Sopenharmony_ci dma_free_coherent(dev, geo->auxiliary_size, 131462306a36Sopenharmony_ci this->auxiliary_virt, 131562306a36Sopenharmony_ci this->auxiliary_phys); 131662306a36Sopenharmony_ci kfree(this->data_buffer_dma); 131762306a36Sopenharmony_ci kfree(this->raw_buffer); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci this->data_buffer_dma = NULL; 132062306a36Sopenharmony_ci this->raw_buffer = NULL; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci/* Allocate the DMA buffers */ 132462306a36Sopenharmony_cistatic int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) 132562306a36Sopenharmony_ci{ 132662306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 132762306a36Sopenharmony_ci struct device *dev = this->dev; 132862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* 133162306a36Sopenharmony_ci * [2] Allocate a read/write data buffer. 133262306a36Sopenharmony_ci * The gpmi_alloc_dma_buffer can be called twice. 133362306a36Sopenharmony_ci * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer 133462306a36Sopenharmony_ci * is called before the NAND identification; and we allocate a 133562306a36Sopenharmony_ci * buffer of the real NAND page size when the gpmi_alloc_dma_buffer 133662306a36Sopenharmony_ci * is called after. 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, 133962306a36Sopenharmony_ci GFP_DMA | GFP_KERNEL); 134062306a36Sopenharmony_ci if (this->data_buffer_dma == NULL) 134162306a36Sopenharmony_ci goto error_alloc; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci this->auxiliary_virt = dma_alloc_coherent(dev, geo->auxiliary_size, 134462306a36Sopenharmony_ci &this->auxiliary_phys, GFP_DMA); 134562306a36Sopenharmony_ci if (!this->auxiliary_virt) 134662306a36Sopenharmony_ci goto error_alloc; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci this->raw_buffer = kzalloc((mtd->writesize ?: PAGE_SIZE) + mtd->oobsize, GFP_KERNEL); 134962306a36Sopenharmony_ci if (!this->raw_buffer) 135062306a36Sopenharmony_ci goto error_alloc; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci return 0; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_cierror_alloc: 135562306a36Sopenharmony_ci gpmi_free_dma_buffer(this); 135662306a36Sopenharmony_ci return -ENOMEM; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci/* 136062306a36Sopenharmony_ci * Handles block mark swapping. 136162306a36Sopenharmony_ci * It can be called in swapping the block mark, or swapping it back, 136262306a36Sopenharmony_ci * because the operations are the same. 136362306a36Sopenharmony_ci */ 136462306a36Sopenharmony_cistatic void block_mark_swapping(struct gpmi_nand_data *this, 136562306a36Sopenharmony_ci void *payload, void *auxiliary) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 136862306a36Sopenharmony_ci unsigned char *p; 136962306a36Sopenharmony_ci unsigned char *a; 137062306a36Sopenharmony_ci unsigned int bit; 137162306a36Sopenharmony_ci unsigned char mask; 137262306a36Sopenharmony_ci unsigned char from_data; 137362306a36Sopenharmony_ci unsigned char from_oob; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (!this->swap_block_mark) 137662306a36Sopenharmony_ci return; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* 137962306a36Sopenharmony_ci * If control arrives here, we're swapping. Make some convenience 138062306a36Sopenharmony_ci * variables. 138162306a36Sopenharmony_ci */ 138262306a36Sopenharmony_ci bit = nfc_geo->block_mark_bit_offset; 138362306a36Sopenharmony_ci p = payload + nfc_geo->block_mark_byte_offset; 138462306a36Sopenharmony_ci a = auxiliary; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci /* 138762306a36Sopenharmony_ci * Get the byte from the data area that overlays the block mark. Since 138862306a36Sopenharmony_ci * the ECC engine applies its own view to the bits in the page, the 138962306a36Sopenharmony_ci * physical block mark won't (in general) appear on a byte boundary in 139062306a36Sopenharmony_ci * the data. 139162306a36Sopenharmony_ci */ 139262306a36Sopenharmony_ci from_data = (p[0] >> bit) | (p[1] << (8 - bit)); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci /* Get the byte from the OOB. */ 139562306a36Sopenharmony_ci from_oob = a[0]; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* Swap them. */ 139862306a36Sopenharmony_ci a[0] = from_data; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci mask = (0x1 << bit) - 1; 140162306a36Sopenharmony_ci p[0] = (p[0] & mask) | (from_oob << bit); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci mask = ~0 << bit; 140462306a36Sopenharmony_ci p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cistatic int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, 140862306a36Sopenharmony_ci int last, int meta) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 141162306a36Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 141262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 141362306a36Sopenharmony_ci int i; 141462306a36Sopenharmony_ci unsigned char *status; 141562306a36Sopenharmony_ci unsigned int max_bitflips = 0; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci /* Loop over status bytes, accumulating ECC status. */ 141862306a36Sopenharmony_ci status = this->auxiliary_virt + ALIGN(meta, 4); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci for (i = first; i < last; i++, status++) { 142162306a36Sopenharmony_ci if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) 142262306a36Sopenharmony_ci continue; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci if (*status == STATUS_UNCORRECTABLE) { 142562306a36Sopenharmony_ci int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; 142662306a36Sopenharmony_ci u8 *eccbuf = this->raw_buffer; 142762306a36Sopenharmony_ci int offset, bitoffset; 142862306a36Sopenharmony_ci int eccbytes; 142962306a36Sopenharmony_ci int flips; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci /* Read ECC bytes into our internal raw_buffer */ 143262306a36Sopenharmony_ci offset = nfc_geo->metadata_size * 8; 143362306a36Sopenharmony_ci offset += ((8 * nfc_geo->eccn_chunk_size) + eccbits) * (i + 1); 143462306a36Sopenharmony_ci offset -= eccbits; 143562306a36Sopenharmony_ci bitoffset = offset % 8; 143662306a36Sopenharmony_ci eccbytes = DIV_ROUND_UP(offset + eccbits, 8); 143762306a36Sopenharmony_ci offset /= 8; 143862306a36Sopenharmony_ci eccbytes -= offset; 143962306a36Sopenharmony_ci nand_change_read_column_op(chip, offset, eccbuf, 144062306a36Sopenharmony_ci eccbytes, false); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* 144362306a36Sopenharmony_ci * ECC data are not byte aligned and we may have 144462306a36Sopenharmony_ci * in-band data in the first and last byte of 144562306a36Sopenharmony_ci * eccbuf. Set non-eccbits to one so that 144662306a36Sopenharmony_ci * nand_check_erased_ecc_chunk() does not count them 144762306a36Sopenharmony_ci * as bitflips. 144862306a36Sopenharmony_ci */ 144962306a36Sopenharmony_ci if (bitoffset) 145062306a36Sopenharmony_ci eccbuf[0] |= GENMASK(bitoffset - 1, 0); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci bitoffset = (bitoffset + eccbits) % 8; 145362306a36Sopenharmony_ci if (bitoffset) 145462306a36Sopenharmony_ci eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* 145762306a36Sopenharmony_ci * The ECC hardware has an uncorrectable ECC status 145862306a36Sopenharmony_ci * code in case we have bitflips in an erased page. As 145962306a36Sopenharmony_ci * nothing was written into this subpage the ECC is 146062306a36Sopenharmony_ci * obviously wrong and we can not trust it. We assume 146162306a36Sopenharmony_ci * at this point that we are reading an erased page and 146262306a36Sopenharmony_ci * try to correct the bitflips in buffer up to 146362306a36Sopenharmony_ci * ecc_strength bitflips. If this is a page with random 146462306a36Sopenharmony_ci * data, we exceed this number of bitflips and have a 146562306a36Sopenharmony_ci * ECC failure. Otherwise we use the corrected buffer. 146662306a36Sopenharmony_ci */ 146762306a36Sopenharmony_ci if (i == 0) { 146862306a36Sopenharmony_ci /* The first block includes metadata */ 146962306a36Sopenharmony_ci flips = nand_check_erased_ecc_chunk( 147062306a36Sopenharmony_ci buf + i * nfc_geo->eccn_chunk_size, 147162306a36Sopenharmony_ci nfc_geo->eccn_chunk_size, 147262306a36Sopenharmony_ci eccbuf, eccbytes, 147362306a36Sopenharmony_ci this->auxiliary_virt, 147462306a36Sopenharmony_ci nfc_geo->metadata_size, 147562306a36Sopenharmony_ci nfc_geo->ecc_strength); 147662306a36Sopenharmony_ci } else { 147762306a36Sopenharmony_ci flips = nand_check_erased_ecc_chunk( 147862306a36Sopenharmony_ci buf + i * nfc_geo->eccn_chunk_size, 147962306a36Sopenharmony_ci nfc_geo->eccn_chunk_size, 148062306a36Sopenharmony_ci eccbuf, eccbytes, 148162306a36Sopenharmony_ci NULL, 0, 148262306a36Sopenharmony_ci nfc_geo->ecc_strength); 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (flips > 0) { 148662306a36Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, 148762306a36Sopenharmony_ci flips); 148862306a36Sopenharmony_ci mtd->ecc_stats.corrected += flips; 148962306a36Sopenharmony_ci continue; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci mtd->ecc_stats.failed++; 149362306a36Sopenharmony_ci continue; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci mtd->ecc_stats.corrected += *status; 149762306a36Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, *status); 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci return max_bitflips; 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic void gpmi_bch_layout_std(struct gpmi_nand_data *this) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 150662306a36Sopenharmony_ci unsigned int ecc_strength = geo->ecc_strength >> 1; 150762306a36Sopenharmony_ci unsigned int gf_len = geo->gf_len; 150862306a36Sopenharmony_ci unsigned int block0_size = geo->ecc0_chunk_size; 150962306a36Sopenharmony_ci unsigned int blockn_size = geo->eccn_chunk_size; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci this->bch_flashlayout0 = 151262306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) | 151362306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) | 151462306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | 151562306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) | 151662306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size, this); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci this->bch_flashlayout1 = 151962306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) | 152062306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | 152162306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) | 152262306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size, this); 152362306a36Sopenharmony_ci} 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_cistatic int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf, 152662306a36Sopenharmony_ci int oob_required, int page) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 152962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 153062306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 153162306a36Sopenharmony_ci unsigned int max_bitflips; 153262306a36Sopenharmony_ci int ret; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci gpmi_bch_layout_std(this); 153562306a36Sopenharmony_ci this->bch = true; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, buf, geo->page_size); 153862306a36Sopenharmony_ci if (ret) 153962306a36Sopenharmony_ci return ret; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci max_bitflips = gpmi_count_bitflips(chip, buf, 0, 154262306a36Sopenharmony_ci geo->ecc_chunk_count, 154362306a36Sopenharmony_ci geo->auxiliary_status_offset); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* handle the block mark swapping */ 154662306a36Sopenharmony_ci block_mark_swapping(this, buf, this->auxiliary_virt); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (oob_required) { 154962306a36Sopenharmony_ci /* 155062306a36Sopenharmony_ci * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() 155162306a36Sopenharmony_ci * for details about our policy for delivering the OOB. 155262306a36Sopenharmony_ci * 155362306a36Sopenharmony_ci * We fill the caller's buffer with set bits, and then copy the 155462306a36Sopenharmony_ci * block mark to th caller's buffer. Note that, if block mark 155562306a36Sopenharmony_ci * swapping was necessary, it has already been done, so we can 155662306a36Sopenharmony_ci * rely on the first byte of the auxiliary buffer to contain 155762306a36Sopenharmony_ci * the block mark. 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci memset(chip->oob_poi, ~0, mtd->oobsize); 156062306a36Sopenharmony_ci chip->oob_poi[0] = ((uint8_t *)this->auxiliary_virt)[0]; 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci return max_bitflips; 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci/* Fake a virtual small page for the subpage read */ 156762306a36Sopenharmony_cistatic int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, 156862306a36Sopenharmony_ci uint32_t len, uint8_t *buf, int page) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 157162306a36Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 157262306a36Sopenharmony_ci int size = chip->ecc.size; /* ECC chunk size */ 157362306a36Sopenharmony_ci int meta, n, page_size; 157462306a36Sopenharmony_ci unsigned int max_bitflips; 157562306a36Sopenharmony_ci unsigned int ecc_strength; 157662306a36Sopenharmony_ci int first, last, marker_pos; 157762306a36Sopenharmony_ci int ecc_parity_size; 157862306a36Sopenharmony_ci int col = 0; 157962306a36Sopenharmony_ci int ret; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci /* The size of ECC parity */ 158262306a36Sopenharmony_ci ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci /* Align it with the chunk size */ 158562306a36Sopenharmony_ci first = offs / size; 158662306a36Sopenharmony_ci last = (offs + len - 1) / size; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci if (this->swap_block_mark) { 158962306a36Sopenharmony_ci /* 159062306a36Sopenharmony_ci * Find the chunk which contains the Block Marker. 159162306a36Sopenharmony_ci * If this chunk is in the range of [first, last], 159262306a36Sopenharmony_ci * we have to read out the whole page. 159362306a36Sopenharmony_ci * Why? since we had swapped the data at the position of Block 159462306a36Sopenharmony_ci * Marker to the metadata which is bound with the chunk 0. 159562306a36Sopenharmony_ci */ 159662306a36Sopenharmony_ci marker_pos = geo->block_mark_byte_offset / size; 159762306a36Sopenharmony_ci if (last >= marker_pos && first <= marker_pos) { 159862306a36Sopenharmony_ci dev_dbg(this->dev, 159962306a36Sopenharmony_ci "page:%d, first:%d, last:%d, marker at:%d\n", 160062306a36Sopenharmony_ci page, first, last, marker_pos); 160162306a36Sopenharmony_ci return gpmi_ecc_read_page(chip, buf, 0, page); 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci /* 160662306a36Sopenharmony_ci * if there is an ECC dedicate for meta: 160762306a36Sopenharmony_ci * - need to add an extra ECC size when calculating col and page_size, 160862306a36Sopenharmony_ci * if the meta size is NOT zero. 160962306a36Sopenharmony_ci * - ecc0_chunk size need to set to the same size as other chunks, 161062306a36Sopenharmony_ci * if the meta size is zero. 161162306a36Sopenharmony_ci */ 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci meta = geo->metadata_size; 161462306a36Sopenharmony_ci if (first) { 161562306a36Sopenharmony_ci if (geo->ecc_for_meta) 161662306a36Sopenharmony_ci col = meta + ecc_parity_size 161762306a36Sopenharmony_ci + (size + ecc_parity_size) * first; 161862306a36Sopenharmony_ci else 161962306a36Sopenharmony_ci col = meta + (size + ecc_parity_size) * first; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci meta = 0; 162262306a36Sopenharmony_ci buf = buf + first * size; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; 162662306a36Sopenharmony_ci n = last - first + 1; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci if (geo->ecc_for_meta && meta) 162962306a36Sopenharmony_ci page_size = meta + ecc_parity_size 163062306a36Sopenharmony_ci + (size + ecc_parity_size) * n; 163162306a36Sopenharmony_ci else 163262306a36Sopenharmony_ci page_size = meta + (size + ecc_parity_size) * n; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci ecc_strength = geo->ecc_strength >> 1; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS( 163762306a36Sopenharmony_ci (geo->ecc_for_meta ? n : n - 1)) | 163862306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) | 163962306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | 164062306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) | 164162306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_DATA0_SIZE((geo->ecc_for_meta ? 164262306a36Sopenharmony_ci 0 : geo->ecc0_chunk_size), this); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | 164562306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | 164662306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) | 164762306a36Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->eccn_chunk_size, this); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci this->bch = true; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, col, buf, page_size); 165262306a36Sopenharmony_ci if (ret) 165362306a36Sopenharmony_ci return ret; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", 165662306a36Sopenharmony_ci page, offs, len, col, first, n, page_size); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci max_bitflips = gpmi_count_bitflips(chip, buf, first, last, meta); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci return max_bitflips; 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf, 166462306a36Sopenharmony_ci int oob_required, int page) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 166762306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 166862306a36Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci dev_dbg(this->dev, "ecc write page.\n"); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci gpmi_bch_layout_std(this); 167362306a36Sopenharmony_ci this->bch = true; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci memcpy(this->auxiliary_virt, chip->oob_poi, nfc_geo->auxiliary_size); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (this->swap_block_mark) { 167862306a36Sopenharmony_ci /* 167962306a36Sopenharmony_ci * When doing bad block marker swapping we must always copy the 168062306a36Sopenharmony_ci * input buffer as we can't modify the const buffer. 168162306a36Sopenharmony_ci */ 168262306a36Sopenharmony_ci memcpy(this->data_buffer_dma, buf, mtd->writesize); 168362306a36Sopenharmony_ci buf = this->data_buffer_dma; 168462306a36Sopenharmony_ci block_mark_swapping(this, this->data_buffer_dma, 168562306a36Sopenharmony_ci this->auxiliary_virt); 168662306a36Sopenharmony_ci } 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci return nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size); 168962306a36Sopenharmony_ci} 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci/* 169262306a36Sopenharmony_ci * There are several places in this driver where we have to handle the OOB and 169362306a36Sopenharmony_ci * block marks. This is the function where things are the most complicated, so 169462306a36Sopenharmony_ci * this is where we try to explain it all. All the other places refer back to 169562306a36Sopenharmony_ci * here. 169662306a36Sopenharmony_ci * 169762306a36Sopenharmony_ci * These are the rules, in order of decreasing importance: 169862306a36Sopenharmony_ci * 169962306a36Sopenharmony_ci * 1) Nothing the caller does can be allowed to imperil the block mark. 170062306a36Sopenharmony_ci * 170162306a36Sopenharmony_ci * 2) In read operations, the first byte of the OOB we return must reflect the 170262306a36Sopenharmony_ci * true state of the block mark, no matter where that block mark appears in 170362306a36Sopenharmony_ci * the physical page. 170462306a36Sopenharmony_ci * 170562306a36Sopenharmony_ci * 3) ECC-based read operations return an OOB full of set bits (since we never 170662306a36Sopenharmony_ci * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads 170762306a36Sopenharmony_ci * return). 170862306a36Sopenharmony_ci * 170962306a36Sopenharmony_ci * 4) "Raw" read operations return a direct view of the physical bytes in the 171062306a36Sopenharmony_ci * page, using the conventional definition of which bytes are data and which 171162306a36Sopenharmony_ci * are OOB. This gives the caller a way to see the actual, physical bytes 171262306a36Sopenharmony_ci * in the page, without the distortions applied by our ECC engine. 171362306a36Sopenharmony_ci * 171462306a36Sopenharmony_ci * 171562306a36Sopenharmony_ci * What we do for this specific read operation depends on two questions: 171662306a36Sopenharmony_ci * 171762306a36Sopenharmony_ci * 1) Are we doing a "raw" read, or an ECC-based read? 171862306a36Sopenharmony_ci * 171962306a36Sopenharmony_ci * 2) Are we using block mark swapping or transcription? 172062306a36Sopenharmony_ci * 172162306a36Sopenharmony_ci * There are four cases, illustrated by the following Karnaugh map: 172262306a36Sopenharmony_ci * 172362306a36Sopenharmony_ci * | Raw | ECC-based | 172462306a36Sopenharmony_ci * -------------+-------------------------+-------------------------+ 172562306a36Sopenharmony_ci * | Read the conventional | | 172662306a36Sopenharmony_ci * | OOB at the end of the | | 172762306a36Sopenharmony_ci * Swapping | page and return it. It | | 172862306a36Sopenharmony_ci * | contains exactly what | | 172962306a36Sopenharmony_ci * | we want. | Read the block mark and | 173062306a36Sopenharmony_ci * -------------+-------------------------+ return it in a buffer | 173162306a36Sopenharmony_ci * | Read the conventional | full of set bits. | 173262306a36Sopenharmony_ci * | OOB at the end of the | | 173362306a36Sopenharmony_ci * | page and also the block | | 173462306a36Sopenharmony_ci * Transcribing | mark in the metadata. | | 173562306a36Sopenharmony_ci * | Copy the block mark | | 173662306a36Sopenharmony_ci * | into the first byte of | | 173762306a36Sopenharmony_ci * | the OOB. | | 173862306a36Sopenharmony_ci * -------------+-------------------------+-------------------------+ 173962306a36Sopenharmony_ci * 174062306a36Sopenharmony_ci * Note that we break rule #4 in the Transcribing/Raw case because we're not 174162306a36Sopenharmony_ci * giving an accurate view of the actual, physical bytes in the page (we're 174262306a36Sopenharmony_ci * overwriting the block mark). That's OK because it's more important to follow 174362306a36Sopenharmony_ci * rule #2. 174462306a36Sopenharmony_ci * 174562306a36Sopenharmony_ci * It turns out that knowing whether we want an "ECC-based" or "raw" read is not 174662306a36Sopenharmony_ci * easy. When reading a page, for example, the NAND Flash MTD code calls our 174762306a36Sopenharmony_ci * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an 174862306a36Sopenharmony_ci * ECC-based or raw view of the page is implicit in which function it calls 174962306a36Sopenharmony_ci * (there is a similar pair of ECC-based/raw functions for writing). 175062306a36Sopenharmony_ci */ 175162306a36Sopenharmony_cistatic int gpmi_ecc_read_oob(struct nand_chip *chip, int page) 175262306a36Sopenharmony_ci{ 175362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 175462306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 175562306a36Sopenharmony_ci int ret; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* clear the OOB buffer */ 175862306a36Sopenharmony_ci memset(chip->oob_poi, ~0, mtd->oobsize); 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci /* Read out the conventional OOB. */ 176162306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, mtd->writesize, chip->oob_poi, 176262306a36Sopenharmony_ci mtd->oobsize); 176362306a36Sopenharmony_ci if (ret) 176462306a36Sopenharmony_ci return ret; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci /* 176762306a36Sopenharmony_ci * Now, we want to make sure the block mark is correct. In the 176862306a36Sopenharmony_ci * non-transcribing case (!GPMI_IS_MX23()), we already have it. 176962306a36Sopenharmony_ci * Otherwise, we need to explicitly read it. 177062306a36Sopenharmony_ci */ 177162306a36Sopenharmony_ci if (GPMI_IS_MX23(this)) { 177262306a36Sopenharmony_ci /* Read the block mark into the first byte of the OOB buffer. */ 177362306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, chip->oob_poi, 1); 177462306a36Sopenharmony_ci if (ret) 177562306a36Sopenharmony_ci return ret; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci return 0; 177962306a36Sopenharmony_ci} 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_cistatic int gpmi_ecc_write_oob(struct nand_chip *chip, int page) 178262306a36Sopenharmony_ci{ 178362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 178462306a36Sopenharmony_ci struct mtd_oob_region of = { }; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci /* Do we have available oob area? */ 178762306a36Sopenharmony_ci mtd_ooblayout_free(mtd, 0, &of); 178862306a36Sopenharmony_ci if (!of.length) 178962306a36Sopenharmony_ci return -EPERM; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci if (!nand_is_slc(chip)) 179262306a36Sopenharmony_ci return -EPERM; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci return nand_prog_page_op(chip, page, mtd->writesize + of.offset, 179562306a36Sopenharmony_ci chip->oob_poi + of.offset, of.length); 179662306a36Sopenharmony_ci} 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci/* 179962306a36Sopenharmony_ci * This function reads a NAND page without involving the ECC engine (no HW 180062306a36Sopenharmony_ci * ECC correction). 180162306a36Sopenharmony_ci * The tricky part in the GPMI/BCH controller is that it stores ECC bits 180262306a36Sopenharmony_ci * inline (interleaved with payload DATA), and do not align data chunk on 180362306a36Sopenharmony_ci * byte boundaries. 180462306a36Sopenharmony_ci * We thus need to take care moving the payload data and ECC bits stored in the 180562306a36Sopenharmony_ci * page into the provided buffers, which is why we're using nand_extract_bits(). 180662306a36Sopenharmony_ci * 180762306a36Sopenharmony_ci * See set_geometry_by_ecc_info inline comments to have a full description 180862306a36Sopenharmony_ci * of the layout used by the GPMI controller. 180962306a36Sopenharmony_ci */ 181062306a36Sopenharmony_cistatic int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf, 181162306a36Sopenharmony_ci int oob_required, int page) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 181462306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 181562306a36Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 181662306a36Sopenharmony_ci int eccsize = nfc_geo->eccn_chunk_size; 181762306a36Sopenharmony_ci int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; 181862306a36Sopenharmony_ci u8 *tmp_buf = this->raw_buffer; 181962306a36Sopenharmony_ci size_t src_bit_off; 182062306a36Sopenharmony_ci size_t oob_bit_off; 182162306a36Sopenharmony_ci size_t oob_byte_off; 182262306a36Sopenharmony_ci uint8_t *oob = chip->oob_poi; 182362306a36Sopenharmony_ci int step; 182462306a36Sopenharmony_ci int ret; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, tmp_buf, 182762306a36Sopenharmony_ci mtd->writesize + mtd->oobsize); 182862306a36Sopenharmony_ci if (ret) 182962306a36Sopenharmony_ci return ret; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci /* 183262306a36Sopenharmony_ci * If required, swap the bad block marker and the data stored in the 183362306a36Sopenharmony_ci * metadata section, so that we don't wrongly consider a block as bad. 183462306a36Sopenharmony_ci * 183562306a36Sopenharmony_ci * See the layout description for a detailed explanation on why this 183662306a36Sopenharmony_ci * is needed. 183762306a36Sopenharmony_ci */ 183862306a36Sopenharmony_ci if (this->swap_block_mark) 183962306a36Sopenharmony_ci swap(tmp_buf[0], tmp_buf[mtd->writesize]); 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* 184262306a36Sopenharmony_ci * Copy the metadata section into the oob buffer (this section is 184362306a36Sopenharmony_ci * guaranteed to be aligned on a byte boundary). 184462306a36Sopenharmony_ci */ 184562306a36Sopenharmony_ci if (oob_required) 184662306a36Sopenharmony_ci memcpy(oob, tmp_buf, nfc_geo->metadata_size); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci oob_bit_off = nfc_geo->metadata_size * 8; 184962306a36Sopenharmony_ci src_bit_off = oob_bit_off; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* Extract interleaved payload data and ECC bits */ 185262306a36Sopenharmony_ci for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { 185362306a36Sopenharmony_ci if (buf) 185462306a36Sopenharmony_ci nand_extract_bits(buf, step * eccsize * 8, tmp_buf, 185562306a36Sopenharmony_ci src_bit_off, eccsize * 8); 185662306a36Sopenharmony_ci src_bit_off += eccsize * 8; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci /* Align last ECC block to align a byte boundary */ 185962306a36Sopenharmony_ci if (step == nfc_geo->ecc_chunk_count - 1 && 186062306a36Sopenharmony_ci (oob_bit_off + eccbits) % 8) 186162306a36Sopenharmony_ci eccbits += 8 - ((oob_bit_off + eccbits) % 8); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci if (oob_required) 186462306a36Sopenharmony_ci nand_extract_bits(oob, oob_bit_off, tmp_buf, 186562306a36Sopenharmony_ci src_bit_off, eccbits); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci src_bit_off += eccbits; 186862306a36Sopenharmony_ci oob_bit_off += eccbits; 186962306a36Sopenharmony_ci } 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci if (oob_required) { 187262306a36Sopenharmony_ci oob_byte_off = oob_bit_off / 8; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci if (oob_byte_off < mtd->oobsize) 187562306a36Sopenharmony_ci memcpy(oob + oob_byte_off, 187662306a36Sopenharmony_ci tmp_buf + mtd->writesize + oob_byte_off, 187762306a36Sopenharmony_ci mtd->oobsize - oob_byte_off); 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci return 0; 188162306a36Sopenharmony_ci} 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci/* 188462306a36Sopenharmony_ci * This function writes a NAND page without involving the ECC engine (no HW 188562306a36Sopenharmony_ci * ECC generation). 188662306a36Sopenharmony_ci * The tricky part in the GPMI/BCH controller is that it stores ECC bits 188762306a36Sopenharmony_ci * inline (interleaved with payload DATA), and do not align data chunk on 188862306a36Sopenharmony_ci * byte boundaries. 188962306a36Sopenharmony_ci * We thus need to take care moving the OOB area at the right place in the 189062306a36Sopenharmony_ci * final page, which is why we're using nand_extract_bits(). 189162306a36Sopenharmony_ci * 189262306a36Sopenharmony_ci * See set_geometry_by_ecc_info inline comments to have a full description 189362306a36Sopenharmony_ci * of the layout used by the GPMI controller. 189462306a36Sopenharmony_ci */ 189562306a36Sopenharmony_cistatic int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf, 189662306a36Sopenharmony_ci int oob_required, int page) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 189962306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 190062306a36Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 190162306a36Sopenharmony_ci int eccsize = nfc_geo->eccn_chunk_size; 190262306a36Sopenharmony_ci int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; 190362306a36Sopenharmony_ci u8 *tmp_buf = this->raw_buffer; 190462306a36Sopenharmony_ci uint8_t *oob = chip->oob_poi; 190562306a36Sopenharmony_ci size_t dst_bit_off; 190662306a36Sopenharmony_ci size_t oob_bit_off; 190762306a36Sopenharmony_ci size_t oob_byte_off; 190862306a36Sopenharmony_ci int step; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci /* 191162306a36Sopenharmony_ci * Initialize all bits to 1 in case we don't have a buffer for the 191262306a36Sopenharmony_ci * payload or oob data in order to leave unspecified bits of data 191362306a36Sopenharmony_ci * to their initial state. 191462306a36Sopenharmony_ci */ 191562306a36Sopenharmony_ci if (!buf || !oob_required) 191662306a36Sopenharmony_ci memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci /* 191962306a36Sopenharmony_ci * First copy the metadata section (stored in oob buffer) at the 192062306a36Sopenharmony_ci * beginning of the page, as imposed by the GPMI layout. 192162306a36Sopenharmony_ci */ 192262306a36Sopenharmony_ci memcpy(tmp_buf, oob, nfc_geo->metadata_size); 192362306a36Sopenharmony_ci oob_bit_off = nfc_geo->metadata_size * 8; 192462306a36Sopenharmony_ci dst_bit_off = oob_bit_off; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Interleave payload data and ECC bits */ 192762306a36Sopenharmony_ci for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { 192862306a36Sopenharmony_ci if (buf) 192962306a36Sopenharmony_ci nand_extract_bits(tmp_buf, dst_bit_off, buf, 193062306a36Sopenharmony_ci step * eccsize * 8, eccsize * 8); 193162306a36Sopenharmony_ci dst_bit_off += eccsize * 8; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci /* Align last ECC block to align a byte boundary */ 193462306a36Sopenharmony_ci if (step == nfc_geo->ecc_chunk_count - 1 && 193562306a36Sopenharmony_ci (oob_bit_off + eccbits) % 8) 193662306a36Sopenharmony_ci eccbits += 8 - ((oob_bit_off + eccbits) % 8); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci if (oob_required) 193962306a36Sopenharmony_ci nand_extract_bits(tmp_buf, dst_bit_off, oob, 194062306a36Sopenharmony_ci oob_bit_off, eccbits); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci dst_bit_off += eccbits; 194362306a36Sopenharmony_ci oob_bit_off += eccbits; 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci oob_byte_off = oob_bit_off / 8; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci if (oob_required && oob_byte_off < mtd->oobsize) 194962306a36Sopenharmony_ci memcpy(tmp_buf + mtd->writesize + oob_byte_off, 195062306a36Sopenharmony_ci oob + oob_byte_off, mtd->oobsize - oob_byte_off); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci /* 195362306a36Sopenharmony_ci * If required, swap the bad block marker and the first byte of the 195462306a36Sopenharmony_ci * metadata section, so that we don't modify the bad block marker. 195562306a36Sopenharmony_ci * 195662306a36Sopenharmony_ci * See the layout description for a detailed explanation on why this 195762306a36Sopenharmony_ci * is needed. 195862306a36Sopenharmony_ci */ 195962306a36Sopenharmony_ci if (this->swap_block_mark) 196062306a36Sopenharmony_ci swap(tmp_buf[0], tmp_buf[mtd->writesize]); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci return nand_prog_page_op(chip, page, 0, tmp_buf, 196362306a36Sopenharmony_ci mtd->writesize + mtd->oobsize); 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_cistatic int gpmi_ecc_read_oob_raw(struct nand_chip *chip, int page) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci return gpmi_ecc_read_page_raw(chip, NULL, 1, page); 196962306a36Sopenharmony_ci} 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_cistatic int gpmi_ecc_write_oob_raw(struct nand_chip *chip, int page) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci return gpmi_ecc_write_page_raw(chip, NULL, 1, page); 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_cistatic int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs) 197762306a36Sopenharmony_ci{ 197862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 197962306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 198062306a36Sopenharmony_ci int ret = 0; 198162306a36Sopenharmony_ci uint8_t *block_mark; 198262306a36Sopenharmony_ci int column, page, chipnr; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci chipnr = (int)(ofs >> chip->chip_shift); 198562306a36Sopenharmony_ci nand_select_target(chip, chipnr); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci /* Write the block mark. */ 199062306a36Sopenharmony_ci block_mark = this->data_buffer_dma; 199162306a36Sopenharmony_ci block_mark[0] = 0; /* bad block marker */ 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci /* Shift to get page */ 199462306a36Sopenharmony_ci page = (int)(ofs >> chip->page_shift); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci ret = nand_prog_page_op(chip, page, column, block_mark, 1); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci nand_deselect_target(chip); 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci return ret; 200162306a36Sopenharmony_ci} 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic int nand_boot_set_geometry(struct gpmi_nand_data *this) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci struct boot_rom_geometry *geometry = &this->rom_geometry; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci /* 200862306a36Sopenharmony_ci * Set the boot block stride size. 200962306a36Sopenharmony_ci * 201062306a36Sopenharmony_ci * In principle, we should be reading this from the OTP bits, since 201162306a36Sopenharmony_ci * that's where the ROM is going to get it. In fact, we don't have any 201262306a36Sopenharmony_ci * way to read the OTP bits, so we go with the default and hope for the 201362306a36Sopenharmony_ci * best. 201462306a36Sopenharmony_ci */ 201562306a36Sopenharmony_ci geometry->stride_size_in_pages = 64; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* 201862306a36Sopenharmony_ci * Set the search area stride exponent. 201962306a36Sopenharmony_ci * 202062306a36Sopenharmony_ci * In principle, we should be reading this from the OTP bits, since 202162306a36Sopenharmony_ci * that's where the ROM is going to get it. In fact, we don't have any 202262306a36Sopenharmony_ci * way to read the OTP bits, so we go with the default and hope for the 202362306a36Sopenharmony_ci * best. 202462306a36Sopenharmony_ci */ 202562306a36Sopenharmony_ci geometry->search_area_stride_exponent = 2; 202662306a36Sopenharmony_ci return 0; 202762306a36Sopenharmony_ci} 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_cistatic const char *fingerprint = "STMP"; 203062306a36Sopenharmony_cistatic int mx23_check_transcription_stamp(struct gpmi_nand_data *this) 203162306a36Sopenharmony_ci{ 203262306a36Sopenharmony_ci struct boot_rom_geometry *rom_geo = &this->rom_geometry; 203362306a36Sopenharmony_ci struct device *dev = this->dev; 203462306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 203562306a36Sopenharmony_ci unsigned int search_area_size_in_strides; 203662306a36Sopenharmony_ci unsigned int stride; 203762306a36Sopenharmony_ci unsigned int page; 203862306a36Sopenharmony_ci u8 *buffer = nand_get_data_buf(chip); 203962306a36Sopenharmony_ci int found_an_ncb_fingerprint = false; 204062306a36Sopenharmony_ci int ret; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci /* Compute the number of strides in a search area. */ 204362306a36Sopenharmony_ci search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci nand_select_target(chip, 0); 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci /* 204862306a36Sopenharmony_ci * Loop through the first search area, looking for the NCB fingerprint. 204962306a36Sopenharmony_ci */ 205062306a36Sopenharmony_ci dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci for (stride = 0; stride < search_area_size_in_strides; stride++) { 205362306a36Sopenharmony_ci /* Compute the page addresses. */ 205462306a36Sopenharmony_ci page = stride * rom_geo->stride_size_in_pages; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* 205962306a36Sopenharmony_ci * Read the NCB fingerprint. The fingerprint is four bytes long 206062306a36Sopenharmony_ci * and starts in the 12th byte of the page. 206162306a36Sopenharmony_ci */ 206262306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, 12, buffer, 206362306a36Sopenharmony_ci strlen(fingerprint)); 206462306a36Sopenharmony_ci if (ret) 206562306a36Sopenharmony_ci continue; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci /* Look for the fingerprint. */ 206862306a36Sopenharmony_ci if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { 206962306a36Sopenharmony_ci found_an_ncb_fingerprint = true; 207062306a36Sopenharmony_ci break; 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci nand_deselect_target(chip); 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci if (found_an_ncb_fingerprint) 207862306a36Sopenharmony_ci dev_dbg(dev, "\tFound a fingerprint\n"); 207962306a36Sopenharmony_ci else 208062306a36Sopenharmony_ci dev_dbg(dev, "\tNo fingerprint found\n"); 208162306a36Sopenharmony_ci return found_an_ncb_fingerprint; 208262306a36Sopenharmony_ci} 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci/* Writes a transcription stamp. */ 208562306a36Sopenharmony_cistatic int mx23_write_transcription_stamp(struct gpmi_nand_data *this) 208662306a36Sopenharmony_ci{ 208762306a36Sopenharmony_ci struct device *dev = this->dev; 208862306a36Sopenharmony_ci struct boot_rom_geometry *rom_geo = &this->rom_geometry; 208962306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 209062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 209162306a36Sopenharmony_ci unsigned int block_size_in_pages; 209262306a36Sopenharmony_ci unsigned int search_area_size_in_strides; 209362306a36Sopenharmony_ci unsigned int search_area_size_in_pages; 209462306a36Sopenharmony_ci unsigned int search_area_size_in_blocks; 209562306a36Sopenharmony_ci unsigned int block; 209662306a36Sopenharmony_ci unsigned int stride; 209762306a36Sopenharmony_ci unsigned int page; 209862306a36Sopenharmony_ci u8 *buffer = nand_get_data_buf(chip); 209962306a36Sopenharmony_ci int status; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci /* Compute the search area geometry. */ 210262306a36Sopenharmony_ci block_size_in_pages = mtd->erasesize / mtd->writesize; 210362306a36Sopenharmony_ci search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; 210462306a36Sopenharmony_ci search_area_size_in_pages = search_area_size_in_strides * 210562306a36Sopenharmony_ci rom_geo->stride_size_in_pages; 210662306a36Sopenharmony_ci search_area_size_in_blocks = 210762306a36Sopenharmony_ci (search_area_size_in_pages + (block_size_in_pages - 1)) / 210862306a36Sopenharmony_ci block_size_in_pages; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci dev_dbg(dev, "Search Area Geometry :\n"); 211162306a36Sopenharmony_ci dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks); 211262306a36Sopenharmony_ci dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); 211362306a36Sopenharmony_ci dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci nand_select_target(chip, 0); 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci /* Loop over blocks in the first search area, erasing them. */ 211862306a36Sopenharmony_ci dev_dbg(dev, "Erasing the search area...\n"); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci for (block = 0; block < search_area_size_in_blocks; block++) { 212162306a36Sopenharmony_ci /* Erase this block. */ 212262306a36Sopenharmony_ci dev_dbg(dev, "\tErasing block 0x%x\n", block); 212362306a36Sopenharmony_ci status = nand_erase_op(chip, block); 212462306a36Sopenharmony_ci if (status) 212562306a36Sopenharmony_ci dev_err(dev, "[%s] Erase failed.\n", __func__); 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci /* Write the NCB fingerprint into the page buffer. */ 212962306a36Sopenharmony_ci memset(buffer, ~0, mtd->writesize); 213062306a36Sopenharmony_ci memcpy(buffer + 12, fingerprint, strlen(fingerprint)); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci /* Loop through the first search area, writing NCB fingerprints. */ 213362306a36Sopenharmony_ci dev_dbg(dev, "Writing NCB fingerprints...\n"); 213462306a36Sopenharmony_ci for (stride = 0; stride < search_area_size_in_strides; stride++) { 213562306a36Sopenharmony_ci /* Compute the page addresses. */ 213662306a36Sopenharmony_ci page = stride * rom_geo->stride_size_in_pages; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci /* Write the first page of the current stride. */ 213962306a36Sopenharmony_ci dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci status = chip->ecc.write_page_raw(chip, buffer, 0, page); 214262306a36Sopenharmony_ci if (status) 214362306a36Sopenharmony_ci dev_err(dev, "[%s] Write failed.\n", __func__); 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci nand_deselect_target(chip); 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci return 0; 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_cistatic int mx23_boot_init(struct gpmi_nand_data *this) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci struct device *dev = this->dev; 215462306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 215562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 215662306a36Sopenharmony_ci unsigned int block_count; 215762306a36Sopenharmony_ci unsigned int block; 215862306a36Sopenharmony_ci int chipnr; 215962306a36Sopenharmony_ci int page; 216062306a36Sopenharmony_ci loff_t byte; 216162306a36Sopenharmony_ci uint8_t block_mark; 216262306a36Sopenharmony_ci int ret = 0; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* 216562306a36Sopenharmony_ci * If control arrives here, we can't use block mark swapping, which 216662306a36Sopenharmony_ci * means we're forced to use transcription. First, scan for the 216762306a36Sopenharmony_ci * transcription stamp. If we find it, then we don't have to do 216862306a36Sopenharmony_ci * anything -- the block marks are already transcribed. 216962306a36Sopenharmony_ci */ 217062306a36Sopenharmony_ci if (mx23_check_transcription_stamp(this)) 217162306a36Sopenharmony_ci return 0; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci /* 217462306a36Sopenharmony_ci * If control arrives here, we couldn't find a transcription stamp, so 217562306a36Sopenharmony_ci * so we presume the block marks are in the conventional location. 217662306a36Sopenharmony_ci */ 217762306a36Sopenharmony_ci dev_dbg(dev, "Transcribing bad block marks...\n"); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci /* Compute the number of blocks in the entire medium. */ 218062306a36Sopenharmony_ci block_count = nanddev_eraseblocks_per_target(&chip->base); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci /* 218362306a36Sopenharmony_ci * Loop over all the blocks in the medium, transcribing block marks as 218462306a36Sopenharmony_ci * we go. 218562306a36Sopenharmony_ci */ 218662306a36Sopenharmony_ci for (block = 0; block < block_count; block++) { 218762306a36Sopenharmony_ci /* 218862306a36Sopenharmony_ci * Compute the chip, page and byte addresses for this block's 218962306a36Sopenharmony_ci * conventional mark. 219062306a36Sopenharmony_ci */ 219162306a36Sopenharmony_ci chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); 219262306a36Sopenharmony_ci page = block << (chip->phys_erase_shift - chip->page_shift); 219362306a36Sopenharmony_ci byte = block << chip->phys_erase_shift; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci /* Send the command to read the conventional block mark. */ 219662306a36Sopenharmony_ci nand_select_target(chip, chipnr); 219762306a36Sopenharmony_ci ret = nand_read_page_op(chip, page, mtd->writesize, &block_mark, 219862306a36Sopenharmony_ci 1); 219962306a36Sopenharmony_ci nand_deselect_target(chip); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci if (ret) 220262306a36Sopenharmony_ci continue; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* 220562306a36Sopenharmony_ci * Check if the block is marked bad. If so, we need to mark it 220662306a36Sopenharmony_ci * again, but this time the result will be a mark in the 220762306a36Sopenharmony_ci * location where we transcribe block marks. 220862306a36Sopenharmony_ci */ 220962306a36Sopenharmony_ci if (block_mark != 0xff) { 221062306a36Sopenharmony_ci dev_dbg(dev, "Transcribing mark in block %u\n", block); 221162306a36Sopenharmony_ci ret = chip->legacy.block_markbad(chip, byte); 221262306a36Sopenharmony_ci if (ret) 221362306a36Sopenharmony_ci dev_err(dev, 221462306a36Sopenharmony_ci "Failed to mark block bad with ret %d\n", 221562306a36Sopenharmony_ci ret); 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci /* Write the stamp that indicates we've transcribed the block marks. */ 222062306a36Sopenharmony_ci mx23_write_transcription_stamp(this); 222162306a36Sopenharmony_ci return 0; 222262306a36Sopenharmony_ci} 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_cistatic int nand_boot_init(struct gpmi_nand_data *this) 222562306a36Sopenharmony_ci{ 222662306a36Sopenharmony_ci nand_boot_set_geometry(this); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci /* This is ROM arch-specific initilization before the BBT scanning. */ 222962306a36Sopenharmony_ci if (GPMI_IS_MX23(this)) 223062306a36Sopenharmony_ci return mx23_boot_init(this); 223162306a36Sopenharmony_ci return 0; 223262306a36Sopenharmony_ci} 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cistatic int gpmi_set_geometry(struct gpmi_nand_data *this) 223562306a36Sopenharmony_ci{ 223662306a36Sopenharmony_ci int ret; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci /* Free the temporary DMA memory for reading ID. */ 223962306a36Sopenharmony_ci gpmi_free_dma_buffer(this); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci /* Set up the NFC geometry which is used by BCH. */ 224262306a36Sopenharmony_ci ret = bch_set_geometry(this); 224362306a36Sopenharmony_ci if (ret) { 224462306a36Sopenharmony_ci dev_err(this->dev, "Error setting BCH geometry : %d\n", ret); 224562306a36Sopenharmony_ci return ret; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci /* Alloc the new DMA buffers according to the pagesize and oobsize */ 224962306a36Sopenharmony_ci return gpmi_alloc_dma_buffer(this); 225062306a36Sopenharmony_ci} 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_cistatic int gpmi_init_last(struct gpmi_nand_data *this) 225362306a36Sopenharmony_ci{ 225462306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 225562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 225662306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 225762306a36Sopenharmony_ci struct bch_geometry *bch_geo = &this->bch_geometry; 225862306a36Sopenharmony_ci int ret; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci /* Set up the medium geometry */ 226162306a36Sopenharmony_ci ret = gpmi_set_geometry(this); 226262306a36Sopenharmony_ci if (ret) 226362306a36Sopenharmony_ci return ret; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci /* Init the nand_ecc_ctrl{} */ 226662306a36Sopenharmony_ci ecc->read_page = gpmi_ecc_read_page; 226762306a36Sopenharmony_ci ecc->write_page = gpmi_ecc_write_page; 226862306a36Sopenharmony_ci ecc->read_oob = gpmi_ecc_read_oob; 226962306a36Sopenharmony_ci ecc->write_oob = gpmi_ecc_write_oob; 227062306a36Sopenharmony_ci ecc->read_page_raw = gpmi_ecc_read_page_raw; 227162306a36Sopenharmony_ci ecc->write_page_raw = gpmi_ecc_write_page_raw; 227262306a36Sopenharmony_ci ecc->read_oob_raw = gpmi_ecc_read_oob_raw; 227362306a36Sopenharmony_ci ecc->write_oob_raw = gpmi_ecc_write_oob_raw; 227462306a36Sopenharmony_ci ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 227562306a36Sopenharmony_ci ecc->size = bch_geo->eccn_chunk_size; 227662306a36Sopenharmony_ci ecc->strength = bch_geo->ecc_strength; 227762306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci /* 228062306a36Sopenharmony_ci * We only enable the subpage read when: 228162306a36Sopenharmony_ci * (1) the chip is imx6, and 228262306a36Sopenharmony_ci * (2) the size of the ECC parity is byte aligned. 228362306a36Sopenharmony_ci */ 228462306a36Sopenharmony_ci if (GPMI_IS_MX6(this) && 228562306a36Sopenharmony_ci ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { 228662306a36Sopenharmony_ci ecc->read_subpage = gpmi_ecc_read_subpage; 228762306a36Sopenharmony_ci chip->options |= NAND_SUBPAGE_READ; 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci return 0; 229162306a36Sopenharmony_ci} 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic int gpmi_nand_attach_chip(struct nand_chip *chip) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 229662306a36Sopenharmony_ci int ret; 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci if (chip->bbt_options & NAND_BBT_USE_FLASH) { 229962306a36Sopenharmony_ci chip->bbt_options |= NAND_BBT_NO_OOB; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci if (of_property_read_bool(this->dev->of_node, 230262306a36Sopenharmony_ci "fsl,no-blockmark-swap")) 230362306a36Sopenharmony_ci this->swap_block_mark = false; 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci dev_dbg(this->dev, "Blockmark swapping %sabled\n", 230662306a36Sopenharmony_ci this->swap_block_mark ? "en" : "dis"); 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci ret = gpmi_init_last(this); 230962306a36Sopenharmony_ci if (ret) 231062306a36Sopenharmony_ci return ret; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci chip->options |= NAND_SKIP_BBTSCAN; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci return 0; 231562306a36Sopenharmony_ci} 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_cistatic struct gpmi_transfer *get_next_transfer(struct gpmi_nand_data *this) 231862306a36Sopenharmony_ci{ 231962306a36Sopenharmony_ci struct gpmi_transfer *transfer = &this->transfers[this->ntransfers]; 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci this->ntransfers++; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci if (this->ntransfers == GPMI_MAX_TRANSFERS) 232462306a36Sopenharmony_ci return NULL; 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci return transfer; 232762306a36Sopenharmony_ci} 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_command( 233062306a36Sopenharmony_ci struct gpmi_nand_data *this, u8 cmd, const u8 *addr, int naddr) 233162306a36Sopenharmony_ci{ 233262306a36Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 233362306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 233462306a36Sopenharmony_ci struct gpmi_transfer *transfer; 233562306a36Sopenharmony_ci int chip = this->nand.cur_cs; 233662306a36Sopenharmony_ci u32 pio[3]; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci /* [1] send out the PIO words */ 233962306a36Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) 234062306a36Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 234162306a36Sopenharmony_ci | BF_GPMI_CTRL0_CS(chip, this) 234262306a36Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 234362306a36Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) 234462306a36Sopenharmony_ci | BM_GPMI_CTRL0_ADDRESS_INCREMENT 234562306a36Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(naddr + 1); 234662306a36Sopenharmony_ci pio[1] = 0; 234762306a36Sopenharmony_ci pio[2] = 0; 234862306a36Sopenharmony_ci desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), 234962306a36Sopenharmony_ci DMA_TRANS_NONE, 0); 235062306a36Sopenharmony_ci if (!desc) 235162306a36Sopenharmony_ci return NULL; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci transfer = get_next_transfer(this); 235462306a36Sopenharmony_ci if (!transfer) 235562306a36Sopenharmony_ci return NULL; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci transfer->cmdbuf[0] = cmd; 235862306a36Sopenharmony_ci if (naddr) 235962306a36Sopenharmony_ci memcpy(&transfer->cmdbuf[1], addr, naddr); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci sg_init_one(&transfer->sgl, transfer->cmdbuf, naddr + 1); 236262306a36Sopenharmony_ci dma_map_sg(this->dev, &transfer->sgl, 1, DMA_TO_DEVICE); 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci transfer->direction = DMA_TO_DEVICE; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, DMA_MEM_TO_DEV, 236762306a36Sopenharmony_ci MXS_DMA_CTRL_WAIT4END); 236862306a36Sopenharmony_ci return desc; 236962306a36Sopenharmony_ci} 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_wait_ready( 237262306a36Sopenharmony_ci struct gpmi_nand_data *this) 237362306a36Sopenharmony_ci{ 237462306a36Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 237562306a36Sopenharmony_ci u32 pio[2]; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) 237862306a36Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 237962306a36Sopenharmony_ci | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) 238062306a36Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 238162306a36Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) 238262306a36Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(0); 238362306a36Sopenharmony_ci pio[1] = 0; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci return mxs_dmaengine_prep_pio(channel, pio, 2, DMA_TRANS_NONE, 238662306a36Sopenharmony_ci MXS_DMA_CTRL_WAIT4END | MXS_DMA_CTRL_WAIT4RDY); 238762306a36Sopenharmony_ci} 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_data_read( 239062306a36Sopenharmony_ci struct gpmi_nand_data *this, void *buf, int raw_len, bool *direct) 239162306a36Sopenharmony_ci{ 239262306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 239362306a36Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 239462306a36Sopenharmony_ci struct gpmi_transfer *transfer; 239562306a36Sopenharmony_ci u32 pio[6] = {}; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci transfer = get_next_transfer(this); 239862306a36Sopenharmony_ci if (!transfer) 239962306a36Sopenharmony_ci return NULL; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci transfer->direction = DMA_FROM_DEVICE; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci *direct = prepare_data_dma(this, buf, raw_len, &transfer->sgl, 240462306a36Sopenharmony_ci DMA_FROM_DEVICE); 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) 240762306a36Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 240862306a36Sopenharmony_ci | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) 240962306a36Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 241062306a36Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) 241162306a36Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(raw_len); 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci if (this->bch) { 241462306a36Sopenharmony_ci pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC 241562306a36Sopenharmony_ci | BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE) 241662306a36Sopenharmony_ci | BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 241762306a36Sopenharmony_ci | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY); 241862306a36Sopenharmony_ci pio[3] = raw_len; 241962306a36Sopenharmony_ci pio[4] = transfer->sgl.dma_address; 242062306a36Sopenharmony_ci pio[5] = this->auxiliary_phys; 242162306a36Sopenharmony_ci } 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), 242462306a36Sopenharmony_ci DMA_TRANS_NONE, 0); 242562306a36Sopenharmony_ci if (!desc) 242662306a36Sopenharmony_ci return NULL; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci if (!this->bch) 242962306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, 243062306a36Sopenharmony_ci DMA_DEV_TO_MEM, 243162306a36Sopenharmony_ci MXS_DMA_CTRL_WAIT4END); 243262306a36Sopenharmony_ci 243362306a36Sopenharmony_ci return desc; 243462306a36Sopenharmony_ci} 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_data_write( 243762306a36Sopenharmony_ci struct gpmi_nand_data *this, const void *buf, int raw_len) 243862306a36Sopenharmony_ci{ 243962306a36Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 244062306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 244162306a36Sopenharmony_ci struct gpmi_transfer *transfer; 244262306a36Sopenharmony_ci u32 pio[6] = {}; 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci transfer = get_next_transfer(this); 244562306a36Sopenharmony_ci if (!transfer) 244662306a36Sopenharmony_ci return NULL; 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci transfer->direction = DMA_TO_DEVICE; 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci prepare_data_dma(this, buf, raw_len, &transfer->sgl, DMA_TO_DEVICE); 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) 245362306a36Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 245462306a36Sopenharmony_ci | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) 245562306a36Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 245662306a36Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) 245762306a36Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(raw_len); 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci if (this->bch) { 246062306a36Sopenharmony_ci pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC 246162306a36Sopenharmony_ci | BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE) 246262306a36Sopenharmony_ci | BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | 246362306a36Sopenharmony_ci BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY); 246462306a36Sopenharmony_ci pio[3] = raw_len; 246562306a36Sopenharmony_ci pio[4] = transfer->sgl.dma_address; 246662306a36Sopenharmony_ci pio[5] = this->auxiliary_phys; 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), 247062306a36Sopenharmony_ci DMA_TRANS_NONE, 247162306a36Sopenharmony_ci (this->bch ? MXS_DMA_CTRL_WAIT4END : 0)); 247262306a36Sopenharmony_ci if (!desc) 247362306a36Sopenharmony_ci return NULL; 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci if (!this->bch) 247662306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, 247762306a36Sopenharmony_ci DMA_MEM_TO_DEV, 247862306a36Sopenharmony_ci MXS_DMA_CTRL_WAIT4END); 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci return desc; 248162306a36Sopenharmony_ci} 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_cistatic int gpmi_nfc_exec_op(struct nand_chip *chip, 248462306a36Sopenharmony_ci const struct nand_operation *op, 248562306a36Sopenharmony_ci bool check_only) 248662306a36Sopenharmony_ci{ 248762306a36Sopenharmony_ci const struct nand_op_instr *instr; 248862306a36Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 248962306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc = NULL; 249062306a36Sopenharmony_ci int i, ret, buf_len = 0, nbufs = 0; 249162306a36Sopenharmony_ci u8 cmd = 0; 249262306a36Sopenharmony_ci void *buf_read = NULL; 249362306a36Sopenharmony_ci const void *buf_write = NULL; 249462306a36Sopenharmony_ci bool direct = false; 249562306a36Sopenharmony_ci struct completion *dma_completion, *bch_completion; 249662306a36Sopenharmony_ci unsigned long to; 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci if (check_only) 249962306a36Sopenharmony_ci return 0; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci this->ntransfers = 0; 250262306a36Sopenharmony_ci for (i = 0; i < GPMI_MAX_TRANSFERS; i++) 250362306a36Sopenharmony_ci this->transfers[i].direction = DMA_NONE; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(this->dev); 250662306a36Sopenharmony_ci if (ret < 0) 250762306a36Sopenharmony_ci return ret; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci /* 251062306a36Sopenharmony_ci * This driver currently supports only one NAND chip. Plus, dies share 251162306a36Sopenharmony_ci * the same configuration. So once timings have been applied on the 251262306a36Sopenharmony_ci * controller side, they will not change anymore. When the time will 251362306a36Sopenharmony_ci * come, the check on must_apply_timings will have to be dropped. 251462306a36Sopenharmony_ci */ 251562306a36Sopenharmony_ci if (this->hw.must_apply_timings) { 251662306a36Sopenharmony_ci this->hw.must_apply_timings = false; 251762306a36Sopenharmony_ci ret = gpmi_nfc_apply_timings(this); 251862306a36Sopenharmony_ci if (ret) 251962306a36Sopenharmony_ci goto out_pm; 252062306a36Sopenharmony_ci } 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs); 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 252562306a36Sopenharmony_ci instr = &op->instrs[i]; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci nand_op_trace(" ", instr); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci switch (instr->type) { 253062306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 253162306a36Sopenharmony_ci desc = gpmi_chain_wait_ready(this); 253262306a36Sopenharmony_ci break; 253362306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 253462306a36Sopenharmony_ci cmd = instr->ctx.cmd.opcode; 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci /* 253762306a36Sopenharmony_ci * When this command has an address cycle chain it 253862306a36Sopenharmony_ci * together with the address cycle 253962306a36Sopenharmony_ci */ 254062306a36Sopenharmony_ci if (i + 1 != op->ninstrs && 254162306a36Sopenharmony_ci op->instrs[i + 1].type == NAND_OP_ADDR_INSTR) 254262306a36Sopenharmony_ci continue; 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci desc = gpmi_chain_command(this, cmd, NULL, 0); 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci break; 254762306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 254862306a36Sopenharmony_ci desc = gpmi_chain_command(this, cmd, instr->ctx.addr.addrs, 254962306a36Sopenharmony_ci instr->ctx.addr.naddrs); 255062306a36Sopenharmony_ci break; 255162306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 255262306a36Sopenharmony_ci buf_write = instr->ctx.data.buf.out; 255362306a36Sopenharmony_ci buf_len = instr->ctx.data.len; 255462306a36Sopenharmony_ci nbufs++; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci desc = gpmi_chain_data_write(this, buf_write, buf_len); 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci break; 255962306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 256062306a36Sopenharmony_ci if (!instr->ctx.data.len) 256162306a36Sopenharmony_ci break; 256262306a36Sopenharmony_ci buf_read = instr->ctx.data.buf.in; 256362306a36Sopenharmony_ci buf_len = instr->ctx.data.len; 256462306a36Sopenharmony_ci nbufs++; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci desc = gpmi_chain_data_read(this, buf_read, buf_len, 256762306a36Sopenharmony_ci &direct); 256862306a36Sopenharmony_ci break; 256962306a36Sopenharmony_ci } 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci if (!desc) { 257262306a36Sopenharmony_ci ret = -ENXIO; 257362306a36Sopenharmony_ci goto unmap; 257462306a36Sopenharmony_ci } 257562306a36Sopenharmony_ci } 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci dev_dbg(this->dev, "%s setup done\n", __func__); 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_ci if (nbufs > 1) { 258062306a36Sopenharmony_ci dev_err(this->dev, "Multiple data instructions not supported\n"); 258162306a36Sopenharmony_ci ret = -EINVAL; 258262306a36Sopenharmony_ci goto unmap; 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci if (this->bch) { 258662306a36Sopenharmony_ci writel(this->bch_flashlayout0, 258762306a36Sopenharmony_ci this->resources.bch_regs + HW_BCH_FLASH0LAYOUT0); 258862306a36Sopenharmony_ci writel(this->bch_flashlayout1, 258962306a36Sopenharmony_ci this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1); 259062306a36Sopenharmony_ci } 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci desc->callback = dma_irq_callback; 259362306a36Sopenharmony_ci desc->callback_param = this; 259462306a36Sopenharmony_ci dma_completion = &this->dma_done; 259562306a36Sopenharmony_ci bch_completion = NULL; 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci init_completion(dma_completion); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci if (this->bch && buf_read) { 260062306a36Sopenharmony_ci writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, 260162306a36Sopenharmony_ci this->resources.bch_regs + HW_BCH_CTRL_SET); 260262306a36Sopenharmony_ci bch_completion = &this->bch_done; 260362306a36Sopenharmony_ci init_completion(bch_completion); 260462306a36Sopenharmony_ci } 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_ci dmaengine_submit(desc); 260762306a36Sopenharmony_ci dma_async_issue_pending(get_dma_chan(this)); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000)); 261062306a36Sopenharmony_ci if (!to) { 261162306a36Sopenharmony_ci dev_err(this->dev, "DMA timeout, last DMA\n"); 261262306a36Sopenharmony_ci gpmi_dump_info(this); 261362306a36Sopenharmony_ci ret = -ETIMEDOUT; 261462306a36Sopenharmony_ci goto unmap; 261562306a36Sopenharmony_ci } 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci if (this->bch && buf_read) { 261862306a36Sopenharmony_ci to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000)); 261962306a36Sopenharmony_ci if (!to) { 262062306a36Sopenharmony_ci dev_err(this->dev, "BCH timeout, last DMA\n"); 262162306a36Sopenharmony_ci gpmi_dump_info(this); 262262306a36Sopenharmony_ci ret = -ETIMEDOUT; 262362306a36Sopenharmony_ci goto unmap; 262462306a36Sopenharmony_ci } 262562306a36Sopenharmony_ci } 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, 262862306a36Sopenharmony_ci this->resources.bch_regs + HW_BCH_CTRL_CLR); 262962306a36Sopenharmony_ci gpmi_clear_bch(this); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci ret = 0; 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ciunmap: 263462306a36Sopenharmony_ci for (i = 0; i < this->ntransfers; i++) { 263562306a36Sopenharmony_ci struct gpmi_transfer *transfer = &this->transfers[i]; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci if (transfer->direction != DMA_NONE) 263862306a36Sopenharmony_ci dma_unmap_sg(this->dev, &transfer->sgl, 1, 263962306a36Sopenharmony_ci transfer->direction); 264062306a36Sopenharmony_ci } 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci if (!ret && buf_read && !direct) 264362306a36Sopenharmony_ci memcpy(buf_read, this->data_buffer_dma, 264462306a36Sopenharmony_ci gpmi_raw_len_to_len(this, buf_len)); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci this->bch = false; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ciout_pm: 264962306a36Sopenharmony_ci pm_runtime_mark_last_busy(this->dev); 265062306a36Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 265162306a36Sopenharmony_ci 265262306a36Sopenharmony_ci return ret; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_cistatic const struct nand_controller_ops gpmi_nand_controller_ops = { 265662306a36Sopenharmony_ci .attach_chip = gpmi_nand_attach_chip, 265762306a36Sopenharmony_ci .setup_interface = gpmi_setup_interface, 265862306a36Sopenharmony_ci .exec_op = gpmi_nfc_exec_op, 265962306a36Sopenharmony_ci}; 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_cistatic int gpmi_nand_init(struct gpmi_nand_data *this) 266262306a36Sopenharmony_ci{ 266362306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 266462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 266562306a36Sopenharmony_ci int ret; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci /* init the MTD data structures */ 266862306a36Sopenharmony_ci mtd->name = "gpmi-nand"; 266962306a36Sopenharmony_ci mtd->dev.parent = this->dev; 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ 267262306a36Sopenharmony_ci nand_set_controller_data(chip, this); 267362306a36Sopenharmony_ci nand_set_flash_node(chip, this->pdev->dev.of_node); 267462306a36Sopenharmony_ci chip->legacy.block_markbad = gpmi_block_markbad; 267562306a36Sopenharmony_ci chip->badblock_pattern = &gpmi_bbt_descr; 267662306a36Sopenharmony_ci chip->options |= NAND_NO_SUBPAGE_WRITE; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ 267962306a36Sopenharmony_ci this->swap_block_mark = !GPMI_IS_MX23(this); 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci /* 268262306a36Sopenharmony_ci * Allocate a temporary DMA buffer for reading ID in the 268362306a36Sopenharmony_ci * nand_scan_ident(). 268462306a36Sopenharmony_ci */ 268562306a36Sopenharmony_ci this->bch_geometry.payload_size = 1024; 268662306a36Sopenharmony_ci this->bch_geometry.auxiliary_size = 128; 268762306a36Sopenharmony_ci ret = gpmi_alloc_dma_buffer(this); 268862306a36Sopenharmony_ci if (ret) 268962306a36Sopenharmony_ci return ret; 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci nand_controller_init(&this->base); 269262306a36Sopenharmony_ci this->base.ops = &gpmi_nand_controller_ops; 269362306a36Sopenharmony_ci chip->controller = &this->base; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1); 269662306a36Sopenharmony_ci if (ret) 269762306a36Sopenharmony_ci goto err_out; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci ret = nand_boot_init(this); 270062306a36Sopenharmony_ci if (ret) 270162306a36Sopenharmony_ci goto err_nand_cleanup; 270262306a36Sopenharmony_ci ret = nand_create_bbt(chip); 270362306a36Sopenharmony_ci if (ret) 270462306a36Sopenharmony_ci goto err_nand_cleanup; 270562306a36Sopenharmony_ci 270662306a36Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 270762306a36Sopenharmony_ci if (ret) 270862306a36Sopenharmony_ci goto err_nand_cleanup; 270962306a36Sopenharmony_ci return 0; 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_cierr_nand_cleanup: 271262306a36Sopenharmony_ci nand_cleanup(chip); 271362306a36Sopenharmony_cierr_out: 271462306a36Sopenharmony_ci gpmi_free_dma_buffer(this); 271562306a36Sopenharmony_ci return ret; 271662306a36Sopenharmony_ci} 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_cistatic const struct of_device_id gpmi_nand_id_table[] = { 271962306a36Sopenharmony_ci { .compatible = "fsl,imx23-gpmi-nand", .data = &gpmi_devdata_imx23, }, 272062306a36Sopenharmony_ci { .compatible = "fsl,imx28-gpmi-nand", .data = &gpmi_devdata_imx28, }, 272162306a36Sopenharmony_ci { .compatible = "fsl,imx6q-gpmi-nand", .data = &gpmi_devdata_imx6q, }, 272262306a36Sopenharmony_ci { .compatible = "fsl,imx6sx-gpmi-nand", .data = &gpmi_devdata_imx6sx, }, 272362306a36Sopenharmony_ci { .compatible = "fsl,imx7d-gpmi-nand", .data = &gpmi_devdata_imx7d,}, 272462306a36Sopenharmony_ci {} 272562306a36Sopenharmony_ci}; 272662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpmi_nand_id_table); 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_cistatic int gpmi_nand_probe(struct platform_device *pdev) 272962306a36Sopenharmony_ci{ 273062306a36Sopenharmony_ci struct gpmi_nand_data *this; 273162306a36Sopenharmony_ci int ret; 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_ci this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); 273462306a36Sopenharmony_ci if (!this) 273562306a36Sopenharmony_ci return -ENOMEM; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci this->devdata = of_device_get_match_data(&pdev->dev); 273862306a36Sopenharmony_ci platform_set_drvdata(pdev, this); 273962306a36Sopenharmony_ci this->pdev = pdev; 274062306a36Sopenharmony_ci this->dev = &pdev->dev; 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci ret = acquire_resources(this); 274362306a36Sopenharmony_ci if (ret) 274462306a36Sopenharmony_ci goto exit_acquire_resources; 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci ret = __gpmi_enable_clk(this, true); 274762306a36Sopenharmony_ci if (ret) 274862306a36Sopenharmony_ci goto exit_acquire_resources; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 500); 275162306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 275262306a36Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 275362306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 275462306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci ret = gpmi_init(this); 275762306a36Sopenharmony_ci if (ret) 275862306a36Sopenharmony_ci goto exit_nfc_init; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci ret = gpmi_nand_init(this); 276162306a36Sopenharmony_ci if (ret) 276262306a36Sopenharmony_ci goto exit_nfc_init; 276362306a36Sopenharmony_ci 276462306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 276562306a36Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci dev_info(this->dev, "driver registered.\n"); 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci return 0; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ciexit_nfc_init: 277262306a36Sopenharmony_ci pm_runtime_put(&pdev->dev); 277362306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 277462306a36Sopenharmony_ci release_resources(this); 277562306a36Sopenharmony_ciexit_acquire_resources: 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci return ret; 277862306a36Sopenharmony_ci} 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_cistatic void gpmi_nand_remove(struct platform_device *pdev) 278162306a36Sopenharmony_ci{ 278262306a36Sopenharmony_ci struct gpmi_nand_data *this = platform_get_drvdata(pdev); 278362306a36Sopenharmony_ci struct nand_chip *chip = &this->nand; 278462306a36Sopenharmony_ci int ret; 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 278762306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 279062306a36Sopenharmony_ci WARN_ON(ret); 279162306a36Sopenharmony_ci nand_cleanup(chip); 279262306a36Sopenharmony_ci gpmi_free_dma_buffer(this); 279362306a36Sopenharmony_ci release_resources(this); 279462306a36Sopenharmony_ci} 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 279762306a36Sopenharmony_cistatic int gpmi_pm_suspend(struct device *dev) 279862306a36Sopenharmony_ci{ 279962306a36Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci release_dma_channels(this); 280262306a36Sopenharmony_ci return 0; 280362306a36Sopenharmony_ci} 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_cistatic int gpmi_pm_resume(struct device *dev) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 280862306a36Sopenharmony_ci int ret; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci ret = acquire_dma_channels(this); 281162306a36Sopenharmony_ci if (ret < 0) 281262306a36Sopenharmony_ci return ret; 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci /* re-init the GPMI registers */ 281562306a36Sopenharmony_ci ret = gpmi_init(this); 281662306a36Sopenharmony_ci if (ret) { 281762306a36Sopenharmony_ci dev_err(this->dev, "Error setting GPMI : %d\n", ret); 281862306a36Sopenharmony_ci return ret; 281962306a36Sopenharmony_ci } 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci /* Set flag to get timing setup restored for next exec_op */ 282262306a36Sopenharmony_ci if (this->hw.clk_rate) 282362306a36Sopenharmony_ci this->hw.must_apply_timings = true; 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci /* re-init the BCH registers */ 282662306a36Sopenharmony_ci ret = bch_set_geometry(this); 282762306a36Sopenharmony_ci if (ret) { 282862306a36Sopenharmony_ci dev_err(this->dev, "Error setting BCH : %d\n", ret); 282962306a36Sopenharmony_ci return ret; 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci return 0; 283362306a36Sopenharmony_ci} 283462306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_cistatic int __maybe_unused gpmi_runtime_suspend(struct device *dev) 283762306a36Sopenharmony_ci{ 283862306a36Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci return __gpmi_enable_clk(this, false); 284162306a36Sopenharmony_ci} 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_cistatic int __maybe_unused gpmi_runtime_resume(struct device *dev) 284462306a36Sopenharmony_ci{ 284562306a36Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci return __gpmi_enable_clk(this, true); 284862306a36Sopenharmony_ci} 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_cistatic const struct dev_pm_ops gpmi_pm_ops = { 285162306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) 285262306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(gpmi_runtime_suspend, gpmi_runtime_resume, NULL) 285362306a36Sopenharmony_ci}; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_cistatic struct platform_driver gpmi_nand_driver = { 285662306a36Sopenharmony_ci .driver = { 285762306a36Sopenharmony_ci .name = "gpmi-nand", 285862306a36Sopenharmony_ci .pm = &gpmi_pm_ops, 285962306a36Sopenharmony_ci .of_match_table = gpmi_nand_id_table, 286062306a36Sopenharmony_ci }, 286162306a36Sopenharmony_ci .probe = gpmi_nand_probe, 286262306a36Sopenharmony_ci .remove_new = gpmi_nand_remove, 286362306a36Sopenharmony_ci}; 286462306a36Sopenharmony_cimodule_platform_driver(gpmi_nand_driver); 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 286762306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); 286862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2869