18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Freescale GPMI NAND Flash Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Embedded Alley Solutions, Inc. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/dma/mxs-dma.h> 198c2ecf20Sopenharmony_ci#include "gpmi-nand.h" 208c2ecf20Sopenharmony_ci#include "gpmi-regs.h" 218c2ecf20Sopenharmony_ci#include "bch-regs.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Resource names for the GPMI NAND driver. */ 248c2ecf20Sopenharmony_ci#define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" 258c2ecf20Sopenharmony_ci#define GPMI_NAND_BCH_REGS_ADDR_RES_NAME "bch" 268c2ecf20Sopenharmony_ci#define GPMI_NAND_BCH_INTERRUPT_RES_NAME "bch" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Converts time to clock cycles */ 298c2ecf20Sopenharmony_ci#define TO_CYCLES(duration, period) DIV_ROUND_UP_ULL(duration, period) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define MXS_SET_ADDR 0x4 328c2ecf20Sopenharmony_ci#define MXS_CLR_ADDR 0x8 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * Clear the bit and poll it cleared. This is usually called with 358c2ecf20Sopenharmony_ci * a reset address and mask being either SFTRST(bit 31) or CLKGATE 368c2ecf20Sopenharmony_ci * (bit 30). 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic int clear_poll_bit(void __iomem *addr, u32 mask) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int timeout = 0x400; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* clear the bit */ 438c2ecf20Sopenharmony_ci writel(mask, addr + MXS_CLR_ADDR); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * SFTRST needs 3 GPMI clocks to settle, the reference manual 478c2ecf20Sopenharmony_ci * recommends to wait 1us. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci udelay(1); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* poll the bit becoming clear */ 528c2ecf20Sopenharmony_ci while ((readl(addr) & mask) && --timeout) 538c2ecf20Sopenharmony_ci /* nothing */; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return !timeout; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define MODULE_CLKGATE (1 << 30) 598c2ecf20Sopenharmony_ci#define MODULE_SFTRST (1 << 31) 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * The current mxs_reset_block() will do two things: 628c2ecf20Sopenharmony_ci * [1] enable the module. 638c2ecf20Sopenharmony_ci * [2] reset the module. 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * In most of the cases, it's ok. 668c2ecf20Sopenharmony_ci * But in MX23, there is a hardware bug in the BCH block (see erratum #2847). 678c2ecf20Sopenharmony_ci * If you try to soft reset the BCH block, it becomes unusable until 688c2ecf20Sopenharmony_ci * the next hard reset. This case occurs in the NAND boot mode. When the board 698c2ecf20Sopenharmony_ci * boots by NAND, the ROM of the chip will initialize the BCH blocks itself. 708c2ecf20Sopenharmony_ci * So If the driver tries to reset the BCH again, the BCH will not work anymore. 718c2ecf20Sopenharmony_ci * You will see a DMA timeout in this case. The bug has been fixed 728c2ecf20Sopenharmony_ci * in the following chips, such as MX28. 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * To avoid this bug, just add a new parameter `just_enable` for 758c2ecf20Sopenharmony_ci * the mxs_reset_block(), and rewrite it here. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistatic int gpmi_reset_block(void __iomem *reset_addr, bool just_enable) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int ret; 808c2ecf20Sopenharmony_ci int timeout = 0x400; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* clear and poll SFTRST */ 838c2ecf20Sopenharmony_ci ret = clear_poll_bit(reset_addr, MODULE_SFTRST); 848c2ecf20Sopenharmony_ci if (unlikely(ret)) 858c2ecf20Sopenharmony_ci goto error; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* clear CLKGATE */ 888c2ecf20Sopenharmony_ci writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!just_enable) { 918c2ecf20Sopenharmony_ci /* set SFTRST to reset the block */ 928c2ecf20Sopenharmony_ci writel(MODULE_SFTRST, reset_addr + MXS_SET_ADDR); 938c2ecf20Sopenharmony_ci udelay(1); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* poll CLKGATE becoming set */ 968c2ecf20Sopenharmony_ci while ((!(readl(reset_addr) & MODULE_CLKGATE)) && --timeout) 978c2ecf20Sopenharmony_ci /* nothing */; 988c2ecf20Sopenharmony_ci if (unlikely(!timeout)) 998c2ecf20Sopenharmony_ci goto error; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* clear and poll SFTRST */ 1038c2ecf20Sopenharmony_ci ret = clear_poll_bit(reset_addr, MODULE_SFTRST); 1048c2ecf20Sopenharmony_ci if (unlikely(ret)) 1058c2ecf20Sopenharmony_ci goto error; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* clear and poll CLKGATE */ 1088c2ecf20Sopenharmony_ci ret = clear_poll_bit(reset_addr, MODULE_CLKGATE); 1098c2ecf20Sopenharmony_ci if (unlikely(ret)) 1108c2ecf20Sopenharmony_ci goto error; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cierror: 1158c2ecf20Sopenharmony_ci pr_err("%s(%p): module reset timeout\n", __func__, reset_addr); 1168c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int __gpmi_enable_clk(struct gpmi_nand_data *this, bool v) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct clk *clk; 1228c2ecf20Sopenharmony_ci int ret; 1238c2ecf20Sopenharmony_ci int i; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (i = 0; i < GPMI_CLK_MAX; i++) { 1268c2ecf20Sopenharmony_ci clk = this->resources.clock[i]; 1278c2ecf20Sopenharmony_ci if (!clk) 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (v) { 1318c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 1328c2ecf20Sopenharmony_ci if (ret) 1338c2ecf20Sopenharmony_ci goto err_clk; 1348c2ecf20Sopenharmony_ci } else { 1358c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cierr_clk: 1418c2ecf20Sopenharmony_ci for (; i > 0; i--) 1428c2ecf20Sopenharmony_ci clk_disable_unprepare(this->resources.clock[i - 1]); 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int gpmi_init(struct gpmi_nand_data *this) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 1498c2ecf20Sopenharmony_ci int ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(this->dev); 1528c2ecf20Sopenharmony_ci if (ret < 0) { 1538c2ecf20Sopenharmony_ci pm_runtime_put_noidle(this->dev); 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = gpmi_reset_block(r->gpmi_regs, false); 1588c2ecf20Sopenharmony_ci if (ret) 1598c2ecf20Sopenharmony_ci goto err_out; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * Reset BCH here, too. We got failures otherwise :( 1638c2ecf20Sopenharmony_ci * See later BCH reset for explanation of MX23 and MX28 handling 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci goto err_out; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* Choose NAND mode. */ 1708c2ecf20Sopenharmony_ci writel(BM_GPMI_CTRL1_GPMI_MODE, r->gpmi_regs + HW_GPMI_CTRL1_CLR); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Set the IRQ polarity. */ 1738c2ecf20Sopenharmony_ci writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, 1748c2ecf20Sopenharmony_ci r->gpmi_regs + HW_GPMI_CTRL1_SET); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Disable Write-Protection. */ 1778c2ecf20Sopenharmony_ci writel(BM_GPMI_CTRL1_DEV_RESET, r->gpmi_regs + HW_GPMI_CTRL1_SET); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Select BCH ECC. */ 1808c2ecf20Sopenharmony_ci writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Decouple the chip select from dma channel. We use dma0 for all 1848c2ecf20Sopenharmony_ci * the chips. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cierr_out: 1898c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(this->dev); 1908c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* This function is very useful. It is called only when the bug occur. */ 1958c2ecf20Sopenharmony_cistatic void gpmi_dump_info(struct gpmi_nand_data *this) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 1988c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 1998c2ecf20Sopenharmony_ci u32 reg; 2008c2ecf20Sopenharmony_ci int i; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci dev_err(this->dev, "Show GPMI registers :\n"); 2038c2ecf20Sopenharmony_ci for (i = 0; i <= HW_GPMI_DEBUG / 0x10 + 1; i++) { 2048c2ecf20Sopenharmony_ci reg = readl(r->gpmi_regs + i * 0x10); 2058c2ecf20Sopenharmony_ci dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* start to print out the BCH info */ 2098c2ecf20Sopenharmony_ci dev_err(this->dev, "Show BCH registers :\n"); 2108c2ecf20Sopenharmony_ci for (i = 0; i <= HW_BCH_VERSION / 0x10 + 1; i++) { 2118c2ecf20Sopenharmony_ci reg = readl(r->bch_regs + i * 0x10); 2128c2ecf20Sopenharmony_ci dev_err(this->dev, "offset 0x%.3x : 0x%.8x\n", i * 0x10, reg); 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci dev_err(this->dev, "BCH Geometry :\n" 2158c2ecf20Sopenharmony_ci "GF length : %u\n" 2168c2ecf20Sopenharmony_ci "ECC Strength : %u\n" 2178c2ecf20Sopenharmony_ci "Page Size in Bytes : %u\n" 2188c2ecf20Sopenharmony_ci "Metadata Size in Bytes : %u\n" 2198c2ecf20Sopenharmony_ci "ECC Chunk Size in Bytes: %u\n" 2208c2ecf20Sopenharmony_ci "ECC Chunk Count : %u\n" 2218c2ecf20Sopenharmony_ci "Payload Size in Bytes : %u\n" 2228c2ecf20Sopenharmony_ci "Auxiliary Size in Bytes: %u\n" 2238c2ecf20Sopenharmony_ci "Auxiliary Status Offset: %u\n" 2248c2ecf20Sopenharmony_ci "Block Mark Byte Offset : %u\n" 2258c2ecf20Sopenharmony_ci "Block Mark Bit Offset : %u\n", 2268c2ecf20Sopenharmony_ci geo->gf_len, 2278c2ecf20Sopenharmony_ci geo->ecc_strength, 2288c2ecf20Sopenharmony_ci geo->page_size, 2298c2ecf20Sopenharmony_ci geo->metadata_size, 2308c2ecf20Sopenharmony_ci geo->ecc_chunk_size, 2318c2ecf20Sopenharmony_ci geo->ecc_chunk_count, 2328c2ecf20Sopenharmony_ci geo->payload_size, 2338c2ecf20Sopenharmony_ci geo->auxiliary_size, 2348c2ecf20Sopenharmony_ci geo->auxiliary_status_offset, 2358c2ecf20Sopenharmony_ci geo->block_mark_byte_offset, 2368c2ecf20Sopenharmony_ci geo->block_mark_bit_offset); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic inline bool gpmi_check_ecc(struct gpmi_nand_data *this) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Do the sanity check. */ 2448c2ecf20Sopenharmony_ci if (GPMI_IS_MXS(this)) { 2458c2ecf20Sopenharmony_ci /* The mx23/mx28 only support the GF13. */ 2468c2ecf20Sopenharmony_ci if (geo->gf_len == 14) 2478c2ecf20Sopenharmony_ci return false; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci return geo->ecc_strength <= this->devdata->bch_max_ecc_strength; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* 2538c2ecf20Sopenharmony_ci * If we can get the ECC information from the nand chip, we do not 2548c2ecf20Sopenharmony_ci * need to calculate them ourselves. 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * We may have available oob space in this case. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_cistatic int set_geometry_by_ecc_info(struct gpmi_nand_data *this, 2598c2ecf20Sopenharmony_ci unsigned int ecc_strength, 2608c2ecf20Sopenharmony_ci unsigned int ecc_step) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 2638c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 2648c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 2658c2ecf20Sopenharmony_ci unsigned int block_mark_bit_offset; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci switch (ecc_step) { 2688c2ecf20Sopenharmony_ci case SZ_512: 2698c2ecf20Sopenharmony_ci geo->gf_len = 13; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case SZ_1K: 2728c2ecf20Sopenharmony_ci geo->gf_len = 14; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci default: 2758c2ecf20Sopenharmony_ci dev_err(this->dev, 2768c2ecf20Sopenharmony_ci "unsupported nand chip. ecc bits : %d, ecc size : %d\n", 2778c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base)->strength, 2788c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base)->step_size); 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci geo->ecc_chunk_size = ecc_step; 2828c2ecf20Sopenharmony_ci geo->ecc_strength = round_up(ecc_strength, 2); 2838c2ecf20Sopenharmony_ci if (!gpmi_check_ecc(this)) 2848c2ecf20Sopenharmony_ci return -EINVAL; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Keep the C >= O */ 2878c2ecf20Sopenharmony_ci if (geo->ecc_chunk_size < mtd->oobsize) { 2888c2ecf20Sopenharmony_ci dev_err(this->dev, 2898c2ecf20Sopenharmony_ci "unsupported nand chip. ecc size: %d, oob size : %d\n", 2908c2ecf20Sopenharmony_ci ecc_step, mtd->oobsize); 2918c2ecf20Sopenharmony_ci return -EINVAL; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* The default value, see comment in the legacy_set_geometry(). */ 2958c2ecf20Sopenharmony_ci geo->metadata_size = 10; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* 3008c2ecf20Sopenharmony_ci * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * | P | 3038c2ecf20Sopenharmony_ci * |<----------------------------------------------------->| 3048c2ecf20Sopenharmony_ci * | | 3058c2ecf20Sopenharmony_ci * | (Block Mark) | 3068c2ecf20Sopenharmony_ci * | P' | | | | 3078c2ecf20Sopenharmony_ci * |<-------------------------------------------->| D | | O' | 3088c2ecf20Sopenharmony_ci * | |<---->| |<--->| 3098c2ecf20Sopenharmony_ci * V V V V V 3108c2ecf20Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+-----+ 3118c2ecf20Sopenharmony_ci * | M | data |E| data |E| data |E| data |E| | 3128c2ecf20Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+-----+ 3138c2ecf20Sopenharmony_ci * ^ ^ 3148c2ecf20Sopenharmony_ci * | O | 3158c2ecf20Sopenharmony_ci * |<------------>| 3168c2ecf20Sopenharmony_ci * | | 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * P : the page size for BCH module. 3198c2ecf20Sopenharmony_ci * E : The ECC strength. 3208c2ecf20Sopenharmony_ci * G : the length of Galois Field. 3218c2ecf20Sopenharmony_ci * N : The chunk count of per page. 3228c2ecf20Sopenharmony_ci * M : the metasize of per page. 3238c2ecf20Sopenharmony_ci * C : the ecc chunk size, aka the "data" above. 3248c2ecf20Sopenharmony_ci * P': the nand chip's page size. 3258c2ecf20Sopenharmony_ci * O : the nand chip's oob size. 3268c2ecf20Sopenharmony_ci * O': the free oob. 3278c2ecf20Sopenharmony_ci * 3288c2ecf20Sopenharmony_ci * The formula for P is : 3298c2ecf20Sopenharmony_ci * 3308c2ecf20Sopenharmony_ci * E * G * N 3318c2ecf20Sopenharmony_ci * P = ------------ + P' + M 3328c2ecf20Sopenharmony_ci * 8 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * The position of block mark moves forward in the ECC-based view 3358c2ecf20Sopenharmony_ci * of page, and the delta is: 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * E * G * (N - 1) 3388c2ecf20Sopenharmony_ci * D = (---------------- + M) 3398c2ecf20Sopenharmony_ci * 8 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * Please see the comment in legacy_set_geometry(). 3428c2ecf20Sopenharmony_ci * With the condition C >= O , we still can get same result. 3438c2ecf20Sopenharmony_ci * So the bit position of the physical block mark within the ECC-based 3448c2ecf20Sopenharmony_ci * view of the page is : 3458c2ecf20Sopenharmony_ci * (P' - D) * 8 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci geo->page_size = mtd->writesize + geo->metadata_size + 3488c2ecf20Sopenharmony_ci (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci geo->payload_size = mtd->writesize; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); 3538c2ecf20Sopenharmony_ci geo->auxiliary_size = ALIGN(geo->metadata_size, 4) 3548c2ecf20Sopenharmony_ci + ALIGN(geo->ecc_chunk_count, 4); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!this->swap_block_mark) 3578c2ecf20Sopenharmony_ci return 0; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* For bit swap. */ 3608c2ecf20Sopenharmony_ci block_mark_bit_offset = mtd->writesize * 8 - 3618c2ecf20Sopenharmony_ci (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) 3628c2ecf20Sopenharmony_ci + geo->metadata_size * 8); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci geo->block_mark_byte_offset = block_mark_bit_offset / 8; 3658c2ecf20Sopenharmony_ci geo->block_mark_bit_offset = block_mark_bit_offset % 8; 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * Calculate the ECC strength by hand: 3718c2ecf20Sopenharmony_ci * E : The ECC strength. 3728c2ecf20Sopenharmony_ci * G : the length of Galois Field. 3738c2ecf20Sopenharmony_ci * N : The chunk count of per page. 3748c2ecf20Sopenharmony_ci * O : the oobsize of the NAND chip. 3758c2ecf20Sopenharmony_ci * M : the metasize of per page. 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * The formula is : 3788c2ecf20Sopenharmony_ci * E * G * N 3798c2ecf20Sopenharmony_ci * ------------ <= (O - M) 3808c2ecf20Sopenharmony_ci * 8 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * So, we get E by: 3838c2ecf20Sopenharmony_ci * (O - M) * 8 3848c2ecf20Sopenharmony_ci * E <= ------------- 3858c2ecf20Sopenharmony_ci * G * N 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_cistatic inline int get_ecc_strength(struct gpmi_nand_data *this) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 3908c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 3918c2ecf20Sopenharmony_ci int ecc_strength; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci ecc_strength = ((mtd->oobsize - geo->metadata_size) * 8) 3948c2ecf20Sopenharmony_ci / (geo->gf_len * geo->ecc_chunk_count); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* We need the minor even number. */ 3978c2ecf20Sopenharmony_ci return round_down(ecc_strength, 2); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int legacy_set_geometry(struct gpmi_nand_data *this) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 4038c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 4048c2ecf20Sopenharmony_ci unsigned int metadata_size; 4058c2ecf20Sopenharmony_ci unsigned int status_size; 4068c2ecf20Sopenharmony_ci unsigned int block_mark_bit_offset; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * The size of the metadata can be changed, though we set it to 10 4108c2ecf20Sopenharmony_ci * bytes now. But it can't be too large, because we have to save 4118c2ecf20Sopenharmony_ci * enough space for BCH. 4128c2ecf20Sopenharmony_ci */ 4138c2ecf20Sopenharmony_ci geo->metadata_size = 10; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* The default for the length of Galois Field. */ 4168c2ecf20Sopenharmony_ci geo->gf_len = 13; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* The default for chunk size. */ 4198c2ecf20Sopenharmony_ci geo->ecc_chunk_size = 512; 4208c2ecf20Sopenharmony_ci while (geo->ecc_chunk_size < mtd->oobsize) { 4218c2ecf20Sopenharmony_ci geo->ecc_chunk_size *= 2; /* keep C >= O */ 4228c2ecf20Sopenharmony_ci geo->gf_len = 14; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* We use the same ECC strength for all chunks. */ 4288c2ecf20Sopenharmony_ci geo->ecc_strength = get_ecc_strength(this); 4298c2ecf20Sopenharmony_ci if (!gpmi_check_ecc(this)) { 4308c2ecf20Sopenharmony_ci dev_err(this->dev, 4318c2ecf20Sopenharmony_ci "ecc strength: %d cannot be supported by the controller (%d)\n" 4328c2ecf20Sopenharmony_ci "try to use minimum ecc strength that NAND chip required\n", 4338c2ecf20Sopenharmony_ci geo->ecc_strength, 4348c2ecf20Sopenharmony_ci this->devdata->bch_max_ecc_strength); 4358c2ecf20Sopenharmony_ci return -EINVAL; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci geo->page_size = mtd->writesize + geo->metadata_size + 4398c2ecf20Sopenharmony_ci (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; 4408c2ecf20Sopenharmony_ci geo->payload_size = mtd->writesize; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * The auxiliary buffer contains the metadata and the ECC status. The 4448c2ecf20Sopenharmony_ci * metadata is padded to the nearest 32-bit boundary. The ECC status 4458c2ecf20Sopenharmony_ci * contains one byte for every ECC chunk, and is also padded to the 4468c2ecf20Sopenharmony_ci * nearest 32-bit boundary. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci metadata_size = ALIGN(geo->metadata_size, 4); 4498c2ecf20Sopenharmony_ci status_size = ALIGN(geo->ecc_chunk_count, 4); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci geo->auxiliary_size = metadata_size + status_size; 4528c2ecf20Sopenharmony_ci geo->auxiliary_status_offset = metadata_size; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (!this->swap_block_mark) 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * We need to compute the byte and bit offsets of 4598c2ecf20Sopenharmony_ci * the physical block mark within the ECC-based view of the page. 4608c2ecf20Sopenharmony_ci * 4618c2ecf20Sopenharmony_ci * NAND chip with 2K page shows below: 4628c2ecf20Sopenharmony_ci * (Block Mark) 4638c2ecf20Sopenharmony_ci * | | 4648c2ecf20Sopenharmony_ci * | D | 4658c2ecf20Sopenharmony_ci * |<---->| 4668c2ecf20Sopenharmony_ci * V V 4678c2ecf20Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+ 4688c2ecf20Sopenharmony_ci * | M | data |E| data |E| data |E| data |E| 4698c2ecf20Sopenharmony_ci * +---+----------+-+----------+-+----------+-+----------+-+ 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * The position of block mark moves forward in the ECC-based view 4728c2ecf20Sopenharmony_ci * of page, and the delta is: 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * E * G * (N - 1) 4758c2ecf20Sopenharmony_ci * D = (---------------- + M) 4768c2ecf20Sopenharmony_ci * 8 4778c2ecf20Sopenharmony_ci * 4788c2ecf20Sopenharmony_ci * With the formula to compute the ECC strength, and the condition 4798c2ecf20Sopenharmony_ci * : C >= O (C is the ecc chunk size) 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * It's easy to deduce to the following result: 4828c2ecf20Sopenharmony_ci * 4838c2ecf20Sopenharmony_ci * E * G (O - M) C - M C - M 4848c2ecf20Sopenharmony_ci * ----------- <= ------- <= -------- < --------- 4858c2ecf20Sopenharmony_ci * 8 N N (N - 1) 4868c2ecf20Sopenharmony_ci * 4878c2ecf20Sopenharmony_ci * So, we get: 4888c2ecf20Sopenharmony_ci * 4898c2ecf20Sopenharmony_ci * E * G * (N - 1) 4908c2ecf20Sopenharmony_ci * D = (---------------- + M) < C 4918c2ecf20Sopenharmony_ci * 8 4928c2ecf20Sopenharmony_ci * 4938c2ecf20Sopenharmony_ci * The above inequality means the position of block mark 4948c2ecf20Sopenharmony_ci * within the ECC-based view of the page is still in the data chunk, 4958c2ecf20Sopenharmony_ci * and it's NOT in the ECC bits of the chunk. 4968c2ecf20Sopenharmony_ci * 4978c2ecf20Sopenharmony_ci * Use the following to compute the bit position of the 4988c2ecf20Sopenharmony_ci * physical block mark within the ECC-based view of the page: 4998c2ecf20Sopenharmony_ci * (page_size - D) * 8 5008c2ecf20Sopenharmony_ci * 5018c2ecf20Sopenharmony_ci * --Huang Shijie 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci block_mark_bit_offset = mtd->writesize * 8 - 5048c2ecf20Sopenharmony_ci (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) 5058c2ecf20Sopenharmony_ci + geo->metadata_size * 8); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci geo->block_mark_byte_offset = block_mark_bit_offset / 8; 5088c2ecf20Sopenharmony_ci geo->block_mark_bit_offset = block_mark_bit_offset % 8; 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int common_nfc_set_geometry(struct gpmi_nand_data *this) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 5158c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 5168c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (chip->ecc.strength > 0 && chip->ecc.size > 0) 5198c2ecf20Sopenharmony_ci return set_geometry_by_ecc_info(this, chip->ecc.strength, 5208c2ecf20Sopenharmony_ci chip->ecc.size); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if ((of_property_read_bool(this->dev->of_node, "fsl,use-minimum-ecc")) 5238c2ecf20Sopenharmony_ci || legacy_set_geometry(this)) { 5248c2ecf20Sopenharmony_ci if (!(requirements->strength > 0 && requirements->step_size > 0)) 5258c2ecf20Sopenharmony_ci return -EINVAL; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return set_geometry_by_ecc_info(this, 5288c2ecf20Sopenharmony_ci requirements->strength, 5298c2ecf20Sopenharmony_ci requirements->step_size); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return 0; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/* Configures the geometry for BCH. */ 5368c2ecf20Sopenharmony_cistatic int bch_set_geometry(struct gpmi_nand_data *this) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci ret = common_nfc_set_geometry(this); 5428c2ecf20Sopenharmony_ci if (ret) 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(this->dev); 5468c2ecf20Sopenharmony_ci if (ret < 0) { 5478c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 5488c2ecf20Sopenharmony_ci return ret; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* 5528c2ecf20Sopenharmony_ci * Due to erratum #2847 of the MX23, the BCH cannot be soft reset on this 5538c2ecf20Sopenharmony_ci * chip, otherwise it will lock up. So we skip resetting BCH on the MX23. 5548c2ecf20Sopenharmony_ci * and MX28. 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci ret = gpmi_reset_block(r->bch_regs, GPMI_IS_MXS(this)); 5578c2ecf20Sopenharmony_ci if (ret) 5588c2ecf20Sopenharmony_ci goto err_out; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Set *all* chip selects to use layout 0. */ 5618c2ecf20Sopenharmony_ci writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci ret = 0; 5648c2ecf20Sopenharmony_cierr_out: 5658c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(this->dev); 5668c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return ret; 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci/* 5728c2ecf20Sopenharmony_ci * <1> Firstly, we should know what's the GPMI-clock means. 5738c2ecf20Sopenharmony_ci * The GPMI-clock is the internal clock in the gpmi nand controller. 5748c2ecf20Sopenharmony_ci * If you set 100MHz to gpmi nand controller, the GPMI-clock's period 5758c2ecf20Sopenharmony_ci * is 10ns. Mark the GPMI-clock's period as GPMI-clock-period. 5768c2ecf20Sopenharmony_ci * 5778c2ecf20Sopenharmony_ci * <2> Secondly, we should know what's the frequency on the nand chip pins. 5788c2ecf20Sopenharmony_ci * The frequency on the nand chip pins is derived from the GPMI-clock. 5798c2ecf20Sopenharmony_ci * We can get it from the following equation: 5808c2ecf20Sopenharmony_ci * 5818c2ecf20Sopenharmony_ci * F = G / (DS + DH) 5828c2ecf20Sopenharmony_ci * 5838c2ecf20Sopenharmony_ci * F : the frequency on the nand chip pins. 5848c2ecf20Sopenharmony_ci * G : the GPMI clock, such as 100MHz. 5858c2ecf20Sopenharmony_ci * DS : GPMI_HW_GPMI_TIMING0:DATA_SETUP 5868c2ecf20Sopenharmony_ci * DH : GPMI_HW_GPMI_TIMING0:DATA_HOLD 5878c2ecf20Sopenharmony_ci * 5888c2ecf20Sopenharmony_ci * <3> Thirdly, when the frequency on the nand chip pins is above 33MHz, 5898c2ecf20Sopenharmony_ci * the nand EDO(extended Data Out) timing could be applied. 5908c2ecf20Sopenharmony_ci * The GPMI implements a feedback read strobe to sample the read data. 5918c2ecf20Sopenharmony_ci * The feedback read strobe can be delayed to support the nand EDO timing 5928c2ecf20Sopenharmony_ci * where the read strobe may deasserts before the read data is valid, and 5938c2ecf20Sopenharmony_ci * read data is valid for some time after read strobe. 5948c2ecf20Sopenharmony_ci * 5958c2ecf20Sopenharmony_ci * The following figure illustrates some aspects of a NAND Flash read: 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * |<---tREA---->| 5988c2ecf20Sopenharmony_ci * | | 5998c2ecf20Sopenharmony_ci * | | | 6008c2ecf20Sopenharmony_ci * |<--tRP-->| | 6018c2ecf20Sopenharmony_ci * | | | 6028c2ecf20Sopenharmony_ci * __ ___|__________________________________ 6038c2ecf20Sopenharmony_ci * RDN \________/ | 6048c2ecf20Sopenharmony_ci * | 6058c2ecf20Sopenharmony_ci * /---------\ 6068c2ecf20Sopenharmony_ci * Read Data --------------< >--------- 6078c2ecf20Sopenharmony_ci * \---------/ 6088c2ecf20Sopenharmony_ci * | | 6098c2ecf20Sopenharmony_ci * |<-D->| 6108c2ecf20Sopenharmony_ci * FeedbackRDN ________ ____________ 6118c2ecf20Sopenharmony_ci * \___________/ 6128c2ecf20Sopenharmony_ci * 6138c2ecf20Sopenharmony_ci * D stands for delay, set in the HW_GPMI_CTRL1:RDN_DELAY. 6148c2ecf20Sopenharmony_ci * 6158c2ecf20Sopenharmony_ci * 6168c2ecf20Sopenharmony_ci * <4> Now, we begin to describe how to compute the right RDN_DELAY. 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * 4.1) From the aspect of the nand chip pins: 6198c2ecf20Sopenharmony_ci * Delay = (tREA + C - tRP) {1} 6208c2ecf20Sopenharmony_ci * 6218c2ecf20Sopenharmony_ci * tREA : the maximum read access time. 6228c2ecf20Sopenharmony_ci * C : a constant to adjust the delay. default is 4000ps. 6238c2ecf20Sopenharmony_ci * tRP : the read pulse width, which is exactly: 6248c2ecf20Sopenharmony_ci * tRP = (GPMI-clock-period) * DATA_SETUP 6258c2ecf20Sopenharmony_ci * 6268c2ecf20Sopenharmony_ci * 4.2) From the aspect of the GPMI nand controller: 6278c2ecf20Sopenharmony_ci * Delay = RDN_DELAY * 0.125 * RP {2} 6288c2ecf20Sopenharmony_ci * 6298c2ecf20Sopenharmony_ci * RP : the DLL reference period. 6308c2ecf20Sopenharmony_ci * if (GPMI-clock-period > DLL_THRETHOLD) 6318c2ecf20Sopenharmony_ci * RP = GPMI-clock-period / 2; 6328c2ecf20Sopenharmony_ci * else 6338c2ecf20Sopenharmony_ci * RP = GPMI-clock-period; 6348c2ecf20Sopenharmony_ci * 6358c2ecf20Sopenharmony_ci * Set the HW_GPMI_CTRL1:HALF_PERIOD if GPMI-clock-period 6368c2ecf20Sopenharmony_ci * is greater DLL_THRETHOLD. In other SOCs, the DLL_THRETHOLD 6378c2ecf20Sopenharmony_ci * is 16000ps, but in mx6q, we use 12000ps. 6388c2ecf20Sopenharmony_ci * 6398c2ecf20Sopenharmony_ci * 4.3) since {1} equals {2}, we get: 6408c2ecf20Sopenharmony_ci * 6418c2ecf20Sopenharmony_ci * (tREA + 4000 - tRP) * 8 6428c2ecf20Sopenharmony_ci * RDN_DELAY = ----------------------- {3} 6438c2ecf20Sopenharmony_ci * RP 6448c2ecf20Sopenharmony_ci */ 6458c2ecf20Sopenharmony_cistatic void gpmi_nfc_compute_timings(struct gpmi_nand_data *this, 6468c2ecf20Sopenharmony_ci const struct nand_sdr_timings *sdr) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct gpmi_nfc_hardware_timing *hw = &this->hw; 6498c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 6508c2ecf20Sopenharmony_ci unsigned int dll_threshold_ps = this->devdata->max_chain_delay; 6518c2ecf20Sopenharmony_ci unsigned int period_ps, reference_period_ps; 6528c2ecf20Sopenharmony_ci unsigned int data_setup_cycles, data_hold_cycles, addr_setup_cycles; 6538c2ecf20Sopenharmony_ci unsigned int tRP_ps; 6548c2ecf20Sopenharmony_ci bool use_half_period; 6558c2ecf20Sopenharmony_ci int sample_delay_ps, sample_delay_factor; 6568c2ecf20Sopenharmony_ci unsigned int busy_timeout_cycles; 6578c2ecf20Sopenharmony_ci u8 wrn_dly_sel; 6588c2ecf20Sopenharmony_ci u64 busy_timeout_ps; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (sdr->tRC_min >= 30000) { 6618c2ecf20Sopenharmony_ci /* ONFI non-EDO modes [0-3] */ 6628c2ecf20Sopenharmony_ci hw->clk_rate = 22000000; 6638c2ecf20Sopenharmony_ci wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS; 6648c2ecf20Sopenharmony_ci } else if (sdr->tRC_min >= 25000) { 6658c2ecf20Sopenharmony_ci /* ONFI EDO mode 4 */ 6668c2ecf20Sopenharmony_ci hw->clk_rate = 80000000; 6678c2ecf20Sopenharmony_ci wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci /* ONFI EDO mode 5 */ 6708c2ecf20Sopenharmony_ci hw->clk_rate = 100000000; 6718c2ecf20Sopenharmony_ci wrn_dly_sel = BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci hw->clk_rate = clk_round_rate(r->clock[0], hw->clk_rate); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* SDR core timings are given in picoseconds */ 6778c2ecf20Sopenharmony_ci period_ps = div_u64((u64)NSEC_PER_SEC * 1000, hw->clk_rate); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci addr_setup_cycles = TO_CYCLES(sdr->tALS_min, period_ps); 6808c2ecf20Sopenharmony_ci data_setup_cycles = TO_CYCLES(sdr->tDS_min, period_ps); 6818c2ecf20Sopenharmony_ci data_hold_cycles = TO_CYCLES(sdr->tDH_min, period_ps); 6828c2ecf20Sopenharmony_ci busy_timeout_ps = max(sdr->tBERS_max, sdr->tPROG_max); 6838c2ecf20Sopenharmony_ci busy_timeout_cycles = TO_CYCLES(busy_timeout_ps, period_ps); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci hw->timing0 = BF_GPMI_TIMING0_ADDRESS_SETUP(addr_setup_cycles) | 6868c2ecf20Sopenharmony_ci BF_GPMI_TIMING0_DATA_HOLD(data_hold_cycles) | 6878c2ecf20Sopenharmony_ci BF_GPMI_TIMING0_DATA_SETUP(data_setup_cycles); 6888c2ecf20Sopenharmony_ci hw->timing1 = BF_GPMI_TIMING1_BUSY_TIMEOUT(busy_timeout_cycles * 4096); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * Derive NFC ideal delay from {3}: 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * (tREA + 4000 - tRP) * 8 6948c2ecf20Sopenharmony_ci * RDN_DELAY = ----------------------- 6958c2ecf20Sopenharmony_ci * RP 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ci if (period_ps > dll_threshold_ps) { 6988c2ecf20Sopenharmony_ci use_half_period = true; 6998c2ecf20Sopenharmony_ci reference_period_ps = period_ps / 2; 7008c2ecf20Sopenharmony_ci } else { 7018c2ecf20Sopenharmony_ci use_half_period = false; 7028c2ecf20Sopenharmony_ci reference_period_ps = period_ps; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci tRP_ps = data_setup_cycles * period_ps; 7068c2ecf20Sopenharmony_ci sample_delay_ps = (sdr->tREA_max + 4000 - tRP_ps) * 8; 7078c2ecf20Sopenharmony_ci if (sample_delay_ps > 0) 7088c2ecf20Sopenharmony_ci sample_delay_factor = sample_delay_ps / reference_period_ps; 7098c2ecf20Sopenharmony_ci else 7108c2ecf20Sopenharmony_ci sample_delay_factor = 0; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci hw->ctrl1n = BF_GPMI_CTRL1_WRN_DLY_SEL(wrn_dly_sel); 7138c2ecf20Sopenharmony_ci if (sample_delay_factor) 7148c2ecf20Sopenharmony_ci hw->ctrl1n |= BF_GPMI_CTRL1_RDN_DELAY(sample_delay_factor) | 7158c2ecf20Sopenharmony_ci BM_GPMI_CTRL1_DLL_ENABLE | 7168c2ecf20Sopenharmony_ci (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0); 7178c2ecf20Sopenharmony_ci} 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_cistatic int gpmi_nfc_apply_timings(struct gpmi_nand_data *this) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci struct gpmi_nfc_hardware_timing *hw = &this->hw; 7228c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 7238c2ecf20Sopenharmony_ci void __iomem *gpmi_regs = r->gpmi_regs; 7248c2ecf20Sopenharmony_ci unsigned int dll_wait_time_us; 7258c2ecf20Sopenharmony_ci int ret; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Clock dividers do NOT guarantee a clean clock signal on its output 7288c2ecf20Sopenharmony_ci * during the change of the divide factor on i.MX6Q/UL/SX. On i.MX7/8, 7298c2ecf20Sopenharmony_ci * all clock dividers provide these guarantee. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_ci if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) 7328c2ecf20Sopenharmony_ci clk_disable_unprepare(r->clock[0]); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ret = clk_set_rate(r->clock[0], hw->clk_rate); 7358c2ecf20Sopenharmony_ci if (ret) { 7368c2ecf20Sopenharmony_ci dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret); 7378c2ecf20Sopenharmony_ci return ret; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) { 7418c2ecf20Sopenharmony_ci ret = clk_prepare_enable(r->clock[0]); 7428c2ecf20Sopenharmony_ci if (ret) 7438c2ecf20Sopenharmony_ci return ret; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0); 7478c2ecf20Sopenharmony_ci writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* 7508c2ecf20Sopenharmony_ci * Clear several CTRL1 fields, DLL must be disabled when setting 7518c2ecf20Sopenharmony_ci * RDN_DELAY or HALF_PERIOD. 7528c2ecf20Sopenharmony_ci */ 7538c2ecf20Sopenharmony_ci writel(BM_GPMI_CTRL1_CLEAR_MASK, gpmi_regs + HW_GPMI_CTRL1_CLR); 7548c2ecf20Sopenharmony_ci writel(hw->ctrl1n, gpmi_regs + HW_GPMI_CTRL1_SET); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Wait 64 clock cycles before using the GPMI after enabling the DLL */ 7578c2ecf20Sopenharmony_ci dll_wait_time_us = USEC_PER_SEC / hw->clk_rate * 64; 7588c2ecf20Sopenharmony_ci if (!dll_wait_time_us) 7598c2ecf20Sopenharmony_ci dll_wait_time_us = 1; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* Wait for the DLL to settle. */ 7628c2ecf20Sopenharmony_ci udelay(dll_wait_time_us); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int gpmi_setup_interface(struct nand_chip *chip, int chipnr, 7688c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 7718c2ecf20Sopenharmony_ci const struct nand_sdr_timings *sdr; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* Retrieve required NAND timings */ 7748c2ecf20Sopenharmony_ci sdr = nand_get_sdr_timings(conf); 7758c2ecf20Sopenharmony_ci if (IS_ERR(sdr)) 7768c2ecf20Sopenharmony_ci return PTR_ERR(sdr); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Only MX6 GPMI controller can reach EDO timings */ 7798c2ecf20Sopenharmony_ci if (sdr->tRC_min <= 25000 && !GPMI_IS_MX6(this)) 7808c2ecf20Sopenharmony_ci return -ENOTSUPP; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Stop here if this call was just a check */ 7838c2ecf20Sopenharmony_ci if (chipnr < 0) 7848c2ecf20Sopenharmony_ci return 0; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* Do the actual derivation of the controller timings */ 7878c2ecf20Sopenharmony_ci gpmi_nfc_compute_timings(this, sdr); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci this->hw.must_apply_timings = true; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/* Clears a BCH interrupt. */ 7958c2ecf20Sopenharmony_cistatic void gpmi_clear_bch(struct gpmi_nand_data *this) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 7988c2ecf20Sopenharmony_ci writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); 7998c2ecf20Sopenharmony_ci} 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci /* We use the DMA channel 0 to access all the nand chips. */ 8048c2ecf20Sopenharmony_ci return this->dma_chans[0]; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/* This will be called after the DMA operation is finished. */ 8088c2ecf20Sopenharmony_cistatic void dma_irq_callback(void *param) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = param; 8118c2ecf20Sopenharmony_ci struct completion *dma_c = &this->dma_done; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci complete(dma_c); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic irqreturn_t bch_irq(int irq, void *cookie) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = cookie; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci gpmi_clear_bch(this); 8218c2ecf20Sopenharmony_ci complete(&this->bch_done); 8228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic int gpmi_raw_len_to_len(struct gpmi_nand_data *this, int raw_len) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * raw_len is the length to read/write including bch data which 8298c2ecf20Sopenharmony_ci * we are passed in exec_op. Calculate the data length from it. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci if (this->bch) 8328c2ecf20Sopenharmony_ci return ALIGN_DOWN(raw_len, this->bch_geometry.ecc_chunk_size); 8338c2ecf20Sopenharmony_ci else 8348c2ecf20Sopenharmony_ci return raw_len; 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/* Can we use the upper's buffer directly for DMA? */ 8388c2ecf20Sopenharmony_cistatic bool prepare_data_dma(struct gpmi_nand_data *this, const void *buf, 8398c2ecf20Sopenharmony_ci int raw_len, struct scatterlist *sgl, 8408c2ecf20Sopenharmony_ci enum dma_data_direction dr) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci int ret; 8438c2ecf20Sopenharmony_ci int len = gpmi_raw_len_to_len(this, raw_len); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* first try to map the upper buffer directly */ 8468c2ecf20Sopenharmony_ci if (virt_addr_valid(buf) && !object_is_on_stack(buf)) { 8478c2ecf20Sopenharmony_ci sg_init_one(sgl, buf, len); 8488c2ecf20Sopenharmony_ci ret = dma_map_sg(this->dev, sgl, 1, dr); 8498c2ecf20Sopenharmony_ci if (ret == 0) 8508c2ecf20Sopenharmony_ci goto map_fail; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci return true; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cimap_fail: 8568c2ecf20Sopenharmony_ci /* We have to use our own DMA buffer. */ 8578c2ecf20Sopenharmony_ci sg_init_one(sgl, this->data_buffer_dma, len); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci if (dr == DMA_TO_DEVICE && buf != this->data_buffer_dma) 8608c2ecf20Sopenharmony_ci memcpy(this->data_buffer_dma, buf, len); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci dma_map_sg(this->dev, sgl, 1, dr); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return false; 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci/* add our owner bbt descriptor */ 8688c2ecf20Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff }; 8698c2ecf20Sopenharmony_cistatic struct nand_bbt_descr gpmi_bbt_descr = { 8708c2ecf20Sopenharmony_ci .options = 0, 8718c2ecf20Sopenharmony_ci .offs = 0, 8728c2ecf20Sopenharmony_ci .len = 1, 8738c2ecf20Sopenharmony_ci .pattern = scan_ff_pattern 8748c2ecf20Sopenharmony_ci}; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci/* 8778c2ecf20Sopenharmony_ci * We may change the layout if we can get the ECC info from the datasheet, 8788c2ecf20Sopenharmony_ci * else we will use all the (page + OOB). 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_cistatic int gpmi_ooblayout_ecc(struct mtd_info *mtd, int section, 8818c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 8848c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 8858c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (section) 8888c2ecf20Sopenharmony_ci return -ERANGE; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci oobregion->offset = 0; 8918c2ecf20Sopenharmony_ci oobregion->length = geo->page_size - mtd->writesize; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci return 0; 8948c2ecf20Sopenharmony_ci} 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_cistatic int gpmi_ooblayout_free(struct mtd_info *mtd, int section, 8978c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 9008c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 9018c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (section) 9048c2ecf20Sopenharmony_ci return -ERANGE; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* The available oob size we have. */ 9078c2ecf20Sopenharmony_ci if (geo->page_size < mtd->writesize + mtd->oobsize) { 9088c2ecf20Sopenharmony_ci oobregion->offset = geo->page_size - mtd->writesize; 9098c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic const char * const gpmi_clks_for_mx2x[] = { 9168c2ecf20Sopenharmony_ci "gpmi_io", 9178c2ecf20Sopenharmony_ci}; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops gpmi_ooblayout_ops = { 9208c2ecf20Sopenharmony_ci .ecc = gpmi_ooblayout_ecc, 9218c2ecf20Sopenharmony_ci .free = gpmi_ooblayout_free, 9228c2ecf20Sopenharmony_ci}; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx23 = { 9258c2ecf20Sopenharmony_ci .type = IS_MX23, 9268c2ecf20Sopenharmony_ci .bch_max_ecc_strength = 20, 9278c2ecf20Sopenharmony_ci .max_chain_delay = 16000, 9288c2ecf20Sopenharmony_ci .clks = gpmi_clks_for_mx2x, 9298c2ecf20Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), 9308c2ecf20Sopenharmony_ci}; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx28 = { 9338c2ecf20Sopenharmony_ci .type = IS_MX28, 9348c2ecf20Sopenharmony_ci .bch_max_ecc_strength = 20, 9358c2ecf20Sopenharmony_ci .max_chain_delay = 16000, 9368c2ecf20Sopenharmony_ci .clks = gpmi_clks_for_mx2x, 9378c2ecf20Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx2x), 9388c2ecf20Sopenharmony_ci}; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_cistatic const char * const gpmi_clks_for_mx6[] = { 9418c2ecf20Sopenharmony_ci "gpmi_io", "gpmi_apb", "gpmi_bch", "gpmi_bch_apb", "per1_bch", 9428c2ecf20Sopenharmony_ci}; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx6q = { 9458c2ecf20Sopenharmony_ci .type = IS_MX6Q, 9468c2ecf20Sopenharmony_ci .bch_max_ecc_strength = 40, 9478c2ecf20Sopenharmony_ci .max_chain_delay = 12000, 9488c2ecf20Sopenharmony_ci .clks = gpmi_clks_for_mx6, 9498c2ecf20Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), 9508c2ecf20Sopenharmony_ci}; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx6sx = { 9538c2ecf20Sopenharmony_ci .type = IS_MX6SX, 9548c2ecf20Sopenharmony_ci .bch_max_ecc_strength = 62, 9558c2ecf20Sopenharmony_ci .max_chain_delay = 12000, 9568c2ecf20Sopenharmony_ci .clks = gpmi_clks_for_mx6, 9578c2ecf20Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx6), 9588c2ecf20Sopenharmony_ci}; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic const char * const gpmi_clks_for_mx7d[] = { 9618c2ecf20Sopenharmony_ci "gpmi_io", "gpmi_bch_apb", 9628c2ecf20Sopenharmony_ci}; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic const struct gpmi_devdata gpmi_devdata_imx7d = { 9658c2ecf20Sopenharmony_ci .type = IS_MX7D, 9668c2ecf20Sopenharmony_ci .bch_max_ecc_strength = 62, 9678c2ecf20Sopenharmony_ci .max_chain_delay = 12000, 9688c2ecf20Sopenharmony_ci .clks = gpmi_clks_for_mx7d, 9698c2ecf20Sopenharmony_ci .clks_count = ARRAY_SIZE(gpmi_clks_for_mx7d), 9708c2ecf20Sopenharmony_ci}; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic int acquire_register_block(struct gpmi_nand_data *this, 9738c2ecf20Sopenharmony_ci const char *res_name) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct platform_device *pdev = this->pdev; 9768c2ecf20Sopenharmony_ci struct resources *res = &this->resources; 9778c2ecf20Sopenharmony_ci struct resource *r; 9788c2ecf20Sopenharmony_ci void __iomem *p; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); 9818c2ecf20Sopenharmony_ci p = devm_ioremap_resource(&pdev->dev, r); 9828c2ecf20Sopenharmony_ci if (IS_ERR(p)) 9838c2ecf20Sopenharmony_ci return PTR_ERR(p); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (!strcmp(res_name, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME)) 9868c2ecf20Sopenharmony_ci res->gpmi_regs = p; 9878c2ecf20Sopenharmony_ci else if (!strcmp(res_name, GPMI_NAND_BCH_REGS_ADDR_RES_NAME)) 9888c2ecf20Sopenharmony_ci res->bch_regs = p; 9898c2ecf20Sopenharmony_ci else 9908c2ecf20Sopenharmony_ci dev_err(this->dev, "unknown resource name : %s\n", res_name); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) 9968c2ecf20Sopenharmony_ci{ 9978c2ecf20Sopenharmony_ci struct platform_device *pdev = this->pdev; 9988c2ecf20Sopenharmony_ci const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME; 9998c2ecf20Sopenharmony_ci struct resource *r; 10008c2ecf20Sopenharmony_ci int err; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); 10038c2ecf20Sopenharmony_ci if (!r) { 10048c2ecf20Sopenharmony_ci dev_err(this->dev, "Can't get resource for %s\n", res_name); 10058c2ecf20Sopenharmony_ci return -ENODEV; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this); 10098c2ecf20Sopenharmony_ci if (err) 10108c2ecf20Sopenharmony_ci dev_err(this->dev, "error requesting BCH IRQ\n"); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci return err; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic void release_dma_channels(struct gpmi_nand_data *this) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci unsigned int i; 10188c2ecf20Sopenharmony_ci for (i = 0; i < DMA_CHANS; i++) 10198c2ecf20Sopenharmony_ci if (this->dma_chans[i]) { 10208c2ecf20Sopenharmony_ci dma_release_channel(this->dma_chans[i]); 10218c2ecf20Sopenharmony_ci this->dma_chans[i] = NULL; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic int acquire_dma_channels(struct gpmi_nand_data *this) 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci struct platform_device *pdev = this->pdev; 10288c2ecf20Sopenharmony_ci struct dma_chan *dma_chan; 10298c2ecf20Sopenharmony_ci int ret = 0; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* request dma channel */ 10328c2ecf20Sopenharmony_ci dma_chan = dma_request_chan(&pdev->dev, "rx-tx"); 10338c2ecf20Sopenharmony_ci if (IS_ERR(dma_chan)) { 10348c2ecf20Sopenharmony_ci ret = dev_err_probe(this->dev, PTR_ERR(dma_chan), 10358c2ecf20Sopenharmony_ci "DMA channel request failed\n"); 10368c2ecf20Sopenharmony_ci release_dma_channels(this); 10378c2ecf20Sopenharmony_ci } else { 10388c2ecf20Sopenharmony_ci this->dma_chans[0] = dma_chan; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci return ret; 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_cistatic int gpmi_get_clks(struct gpmi_nand_data *this) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct resources *r = &this->resources; 10478c2ecf20Sopenharmony_ci struct clk *clk; 10488c2ecf20Sopenharmony_ci int err, i; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci for (i = 0; i < this->devdata->clks_count; i++) { 10518c2ecf20Sopenharmony_ci clk = devm_clk_get(this->dev, this->devdata->clks[i]); 10528c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 10538c2ecf20Sopenharmony_ci err = PTR_ERR(clk); 10548c2ecf20Sopenharmony_ci goto err_clock; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci r->clock[i] = clk; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return 0; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cierr_clock: 10638c2ecf20Sopenharmony_ci dev_dbg(this->dev, "failed in finding the clocks.\n"); 10648c2ecf20Sopenharmony_ci return err; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic int acquire_resources(struct gpmi_nand_data *this) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci int ret; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); 10728c2ecf20Sopenharmony_ci if (ret) 10738c2ecf20Sopenharmony_ci goto exit_regs; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ret = acquire_register_block(this, GPMI_NAND_BCH_REGS_ADDR_RES_NAME); 10768c2ecf20Sopenharmony_ci if (ret) 10778c2ecf20Sopenharmony_ci goto exit_regs; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci ret = acquire_bch_irq(this, bch_irq); 10808c2ecf20Sopenharmony_ci if (ret) 10818c2ecf20Sopenharmony_ci goto exit_regs; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci ret = acquire_dma_channels(this); 10848c2ecf20Sopenharmony_ci if (ret) 10858c2ecf20Sopenharmony_ci goto exit_regs; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci ret = gpmi_get_clks(this); 10888c2ecf20Sopenharmony_ci if (ret) 10898c2ecf20Sopenharmony_ci goto exit_clock; 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ciexit_clock: 10938c2ecf20Sopenharmony_ci release_dma_channels(this); 10948c2ecf20Sopenharmony_ciexit_regs: 10958c2ecf20Sopenharmony_ci return ret; 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic void release_resources(struct gpmi_nand_data *this) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci release_dma_channels(this); 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic void gpmi_free_dma_buffer(struct gpmi_nand_data *this) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct device *dev = this->dev; 11068c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci if (this->auxiliary_virt && virt_addr_valid(this->auxiliary_virt)) 11098c2ecf20Sopenharmony_ci dma_free_coherent(dev, geo->auxiliary_size, 11108c2ecf20Sopenharmony_ci this->auxiliary_virt, 11118c2ecf20Sopenharmony_ci this->auxiliary_phys); 11128c2ecf20Sopenharmony_ci kfree(this->data_buffer_dma); 11138c2ecf20Sopenharmony_ci kfree(this->raw_buffer); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci this->data_buffer_dma = NULL; 11168c2ecf20Sopenharmony_ci this->raw_buffer = NULL; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci/* Allocate the DMA buffers */ 11208c2ecf20Sopenharmony_cistatic int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 11238c2ecf20Sopenharmony_ci struct device *dev = this->dev; 11248c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&this->nand); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* 11278c2ecf20Sopenharmony_ci * [2] Allocate a read/write data buffer. 11288c2ecf20Sopenharmony_ci * The gpmi_alloc_dma_buffer can be called twice. 11298c2ecf20Sopenharmony_ci * We allocate a PAGE_SIZE length buffer if gpmi_alloc_dma_buffer 11308c2ecf20Sopenharmony_ci * is called before the NAND identification; and we allocate a 11318c2ecf20Sopenharmony_ci * buffer of the real NAND page size when the gpmi_alloc_dma_buffer 11328c2ecf20Sopenharmony_ci * is called after. 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_ci this->data_buffer_dma = kzalloc(mtd->writesize ?: PAGE_SIZE, 11358c2ecf20Sopenharmony_ci GFP_DMA | GFP_KERNEL); 11368c2ecf20Sopenharmony_ci if (this->data_buffer_dma == NULL) 11378c2ecf20Sopenharmony_ci goto error_alloc; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci this->auxiliary_virt = dma_alloc_coherent(dev, geo->auxiliary_size, 11408c2ecf20Sopenharmony_ci &this->auxiliary_phys, GFP_DMA); 11418c2ecf20Sopenharmony_ci if (!this->auxiliary_virt) 11428c2ecf20Sopenharmony_ci goto error_alloc; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci this->raw_buffer = kzalloc((mtd->writesize ?: PAGE_SIZE) + mtd->oobsize, GFP_KERNEL); 11458c2ecf20Sopenharmony_ci if (!this->raw_buffer) 11468c2ecf20Sopenharmony_ci goto error_alloc; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci return 0; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cierror_alloc: 11518c2ecf20Sopenharmony_ci gpmi_free_dma_buffer(this); 11528c2ecf20Sopenharmony_ci return -ENOMEM; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci/* 11568c2ecf20Sopenharmony_ci * Handles block mark swapping. 11578c2ecf20Sopenharmony_ci * It can be called in swapping the block mark, or swapping it back, 11588c2ecf20Sopenharmony_ci * because the the operations are the same. 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_cistatic void block_mark_swapping(struct gpmi_nand_data *this, 11618c2ecf20Sopenharmony_ci void *payload, void *auxiliary) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 11648c2ecf20Sopenharmony_ci unsigned char *p; 11658c2ecf20Sopenharmony_ci unsigned char *a; 11668c2ecf20Sopenharmony_ci unsigned int bit; 11678c2ecf20Sopenharmony_ci unsigned char mask; 11688c2ecf20Sopenharmony_ci unsigned char from_data; 11698c2ecf20Sopenharmony_ci unsigned char from_oob; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (!this->swap_block_mark) 11728c2ecf20Sopenharmony_ci return; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* 11758c2ecf20Sopenharmony_ci * If control arrives here, we're swapping. Make some convenience 11768c2ecf20Sopenharmony_ci * variables. 11778c2ecf20Sopenharmony_ci */ 11788c2ecf20Sopenharmony_ci bit = nfc_geo->block_mark_bit_offset; 11798c2ecf20Sopenharmony_ci p = payload + nfc_geo->block_mark_byte_offset; 11808c2ecf20Sopenharmony_ci a = auxiliary; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci /* 11838c2ecf20Sopenharmony_ci * Get the byte from the data area that overlays the block mark. Since 11848c2ecf20Sopenharmony_ci * the ECC engine applies its own view to the bits in the page, the 11858c2ecf20Sopenharmony_ci * physical block mark won't (in general) appear on a byte boundary in 11868c2ecf20Sopenharmony_ci * the data. 11878c2ecf20Sopenharmony_ci */ 11888c2ecf20Sopenharmony_ci from_data = (p[0] >> bit) | (p[1] << (8 - bit)); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci /* Get the byte from the OOB. */ 11918c2ecf20Sopenharmony_ci from_oob = a[0]; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Swap them. */ 11948c2ecf20Sopenharmony_ci a[0] = from_data; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci mask = (0x1 << bit) - 1; 11978c2ecf20Sopenharmony_ci p[0] = (p[0] & mask) | (from_oob << bit); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci mask = ~0 << bit; 12008c2ecf20Sopenharmony_ci p[1] = (p[1] & mask) | (from_oob >> (8 - bit)); 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_cistatic int gpmi_count_bitflips(struct nand_chip *chip, void *buf, int first, 12048c2ecf20Sopenharmony_ci int last, int meta) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 12078c2ecf20Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 12088c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 12098c2ecf20Sopenharmony_ci int i; 12108c2ecf20Sopenharmony_ci unsigned char *status; 12118c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Loop over status bytes, accumulating ECC status. */ 12148c2ecf20Sopenharmony_ci status = this->auxiliary_virt + ALIGN(meta, 4); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci for (i = first; i < last; i++, status++) { 12178c2ecf20Sopenharmony_ci if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED)) 12188c2ecf20Sopenharmony_ci continue; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (*status == STATUS_UNCORRECTABLE) { 12218c2ecf20Sopenharmony_ci int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; 12228c2ecf20Sopenharmony_ci u8 *eccbuf = this->raw_buffer; 12238c2ecf20Sopenharmony_ci int offset, bitoffset; 12248c2ecf20Sopenharmony_ci int eccbytes; 12258c2ecf20Sopenharmony_ci int flips; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci /* Read ECC bytes into our internal raw_buffer */ 12288c2ecf20Sopenharmony_ci offset = nfc_geo->metadata_size * 8; 12298c2ecf20Sopenharmony_ci offset += ((8 * nfc_geo->ecc_chunk_size) + eccbits) * (i + 1); 12308c2ecf20Sopenharmony_ci offset -= eccbits; 12318c2ecf20Sopenharmony_ci bitoffset = offset % 8; 12328c2ecf20Sopenharmony_ci eccbytes = DIV_ROUND_UP(offset + eccbits, 8); 12338c2ecf20Sopenharmony_ci offset /= 8; 12348c2ecf20Sopenharmony_ci eccbytes -= offset; 12358c2ecf20Sopenharmony_ci nand_change_read_column_op(chip, offset, eccbuf, 12368c2ecf20Sopenharmony_ci eccbytes, false); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* 12398c2ecf20Sopenharmony_ci * ECC data are not byte aligned and we may have 12408c2ecf20Sopenharmony_ci * in-band data in the first and last byte of 12418c2ecf20Sopenharmony_ci * eccbuf. Set non-eccbits to one so that 12428c2ecf20Sopenharmony_ci * nand_check_erased_ecc_chunk() does not count them 12438c2ecf20Sopenharmony_ci * as bitflips. 12448c2ecf20Sopenharmony_ci */ 12458c2ecf20Sopenharmony_ci if (bitoffset) 12468c2ecf20Sopenharmony_ci eccbuf[0] |= GENMASK(bitoffset - 1, 0); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci bitoffset = (bitoffset + eccbits) % 8; 12498c2ecf20Sopenharmony_ci if (bitoffset) 12508c2ecf20Sopenharmony_ci eccbuf[eccbytes - 1] |= GENMASK(7, bitoffset); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* 12538c2ecf20Sopenharmony_ci * The ECC hardware has an uncorrectable ECC status 12548c2ecf20Sopenharmony_ci * code in case we have bitflips in an erased page. As 12558c2ecf20Sopenharmony_ci * nothing was written into this subpage the ECC is 12568c2ecf20Sopenharmony_ci * obviously wrong and we can not trust it. We assume 12578c2ecf20Sopenharmony_ci * at this point that we are reading an erased page and 12588c2ecf20Sopenharmony_ci * try to correct the bitflips in buffer up to 12598c2ecf20Sopenharmony_ci * ecc_strength bitflips. If this is a page with random 12608c2ecf20Sopenharmony_ci * data, we exceed this number of bitflips and have a 12618c2ecf20Sopenharmony_ci * ECC failure. Otherwise we use the corrected buffer. 12628c2ecf20Sopenharmony_ci */ 12638c2ecf20Sopenharmony_ci if (i == 0) { 12648c2ecf20Sopenharmony_ci /* The first block includes metadata */ 12658c2ecf20Sopenharmony_ci flips = nand_check_erased_ecc_chunk( 12668c2ecf20Sopenharmony_ci buf + i * nfc_geo->ecc_chunk_size, 12678c2ecf20Sopenharmony_ci nfc_geo->ecc_chunk_size, 12688c2ecf20Sopenharmony_ci eccbuf, eccbytes, 12698c2ecf20Sopenharmony_ci this->auxiliary_virt, 12708c2ecf20Sopenharmony_ci nfc_geo->metadata_size, 12718c2ecf20Sopenharmony_ci nfc_geo->ecc_strength); 12728c2ecf20Sopenharmony_ci } else { 12738c2ecf20Sopenharmony_ci flips = nand_check_erased_ecc_chunk( 12748c2ecf20Sopenharmony_ci buf + i * nfc_geo->ecc_chunk_size, 12758c2ecf20Sopenharmony_ci nfc_geo->ecc_chunk_size, 12768c2ecf20Sopenharmony_ci eccbuf, eccbytes, 12778c2ecf20Sopenharmony_ci NULL, 0, 12788c2ecf20Sopenharmony_ci nfc_geo->ecc_strength); 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (flips > 0) { 12828c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, 12838c2ecf20Sopenharmony_ci flips); 12848c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += flips; 12858c2ecf20Sopenharmony_ci continue; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 12898c2ecf20Sopenharmony_ci continue; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += *status; 12938c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, *status); 12948c2ecf20Sopenharmony_ci } 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci return max_bitflips; 12978c2ecf20Sopenharmony_ci} 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic void gpmi_bch_layout_std(struct gpmi_nand_data *this) 13008c2ecf20Sopenharmony_ci{ 13018c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 13028c2ecf20Sopenharmony_ci unsigned int ecc_strength = geo->ecc_strength >> 1; 13038c2ecf20Sopenharmony_ci unsigned int gf_len = geo->gf_len; 13048c2ecf20Sopenharmony_ci unsigned int block_size = geo->ecc_chunk_size; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci this->bch_flashlayout0 = 13078c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_NBLOCKS(geo->ecc_chunk_count - 1) | 13088c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_META_SIZE(geo->metadata_size) | 13098c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | 13108c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_GF(gf_len, this) | 13118c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size, this); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci this->bch_flashlayout1 = 13148c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(geo->page_size) | 13158c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | 13168c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_GF(gf_len, this) | 13178c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this); 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_cistatic int gpmi_ecc_read_page(struct nand_chip *chip, uint8_t *buf, 13218c2ecf20Sopenharmony_ci int oob_required, int page) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 13248c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 13258c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 13268c2ecf20Sopenharmony_ci unsigned int max_bitflips; 13278c2ecf20Sopenharmony_ci int ret; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci gpmi_bch_layout_std(this); 13308c2ecf20Sopenharmony_ci this->bch = true; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, buf, geo->page_size); 13338c2ecf20Sopenharmony_ci if (ret) 13348c2ecf20Sopenharmony_ci return ret; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci max_bitflips = gpmi_count_bitflips(chip, buf, 0, 13378c2ecf20Sopenharmony_ci geo->ecc_chunk_count, 13388c2ecf20Sopenharmony_ci geo->auxiliary_status_offset); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci /* handle the block mark swapping */ 13418c2ecf20Sopenharmony_ci block_mark_swapping(this, buf, this->auxiliary_virt); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (oob_required) { 13448c2ecf20Sopenharmony_ci /* 13458c2ecf20Sopenharmony_ci * It's time to deliver the OOB bytes. See gpmi_ecc_read_oob() 13468c2ecf20Sopenharmony_ci * for details about our policy for delivering the OOB. 13478c2ecf20Sopenharmony_ci * 13488c2ecf20Sopenharmony_ci * We fill the caller's buffer with set bits, and then copy the 13498c2ecf20Sopenharmony_ci * block mark to th caller's buffer. Note that, if block mark 13508c2ecf20Sopenharmony_ci * swapping was necessary, it has already been done, so we can 13518c2ecf20Sopenharmony_ci * rely on the first byte of the auxiliary buffer to contain 13528c2ecf20Sopenharmony_ci * the block mark. 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_ci memset(chip->oob_poi, ~0, mtd->oobsize); 13558c2ecf20Sopenharmony_ci chip->oob_poi[0] = ((uint8_t *)this->auxiliary_virt)[0]; 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci return max_bitflips; 13598c2ecf20Sopenharmony_ci} 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci/* Fake a virtual small page for the subpage read */ 13628c2ecf20Sopenharmony_cistatic int gpmi_ecc_read_subpage(struct nand_chip *chip, uint32_t offs, 13638c2ecf20Sopenharmony_ci uint32_t len, uint8_t *buf, int page) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 13668c2ecf20Sopenharmony_ci struct bch_geometry *geo = &this->bch_geometry; 13678c2ecf20Sopenharmony_ci int size = chip->ecc.size; /* ECC chunk size */ 13688c2ecf20Sopenharmony_ci int meta, n, page_size; 13698c2ecf20Sopenharmony_ci unsigned int max_bitflips; 13708c2ecf20Sopenharmony_ci unsigned int ecc_strength; 13718c2ecf20Sopenharmony_ci int first, last, marker_pos; 13728c2ecf20Sopenharmony_ci int ecc_parity_size; 13738c2ecf20Sopenharmony_ci int col = 0; 13748c2ecf20Sopenharmony_ci int ret; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* The size of ECC parity */ 13778c2ecf20Sopenharmony_ci ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci /* Align it with the chunk size */ 13808c2ecf20Sopenharmony_ci first = offs / size; 13818c2ecf20Sopenharmony_ci last = (offs + len - 1) / size; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (this->swap_block_mark) { 13848c2ecf20Sopenharmony_ci /* 13858c2ecf20Sopenharmony_ci * Find the chunk which contains the Block Marker. 13868c2ecf20Sopenharmony_ci * If this chunk is in the range of [first, last], 13878c2ecf20Sopenharmony_ci * we have to read out the whole page. 13888c2ecf20Sopenharmony_ci * Why? since we had swapped the data at the position of Block 13898c2ecf20Sopenharmony_ci * Marker to the metadata which is bound with the chunk 0. 13908c2ecf20Sopenharmony_ci */ 13918c2ecf20Sopenharmony_ci marker_pos = geo->block_mark_byte_offset / size; 13928c2ecf20Sopenharmony_ci if (last >= marker_pos && first <= marker_pos) { 13938c2ecf20Sopenharmony_ci dev_dbg(this->dev, 13948c2ecf20Sopenharmony_ci "page:%d, first:%d, last:%d, marker at:%d\n", 13958c2ecf20Sopenharmony_ci page, first, last, marker_pos); 13968c2ecf20Sopenharmony_ci return gpmi_ecc_read_page(chip, buf, 0, page); 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci meta = geo->metadata_size; 14018c2ecf20Sopenharmony_ci if (first) { 14028c2ecf20Sopenharmony_ci col = meta + (size + ecc_parity_size) * first; 14038c2ecf20Sopenharmony_ci meta = 0; 14048c2ecf20Sopenharmony_ci buf = buf + first * size; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci n = last - first + 1; 14108c2ecf20Sopenharmony_ci page_size = meta + (size + ecc_parity_size) * n; 14118c2ecf20Sopenharmony_ci ecc_strength = geo->ecc_strength >> 1; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci this->bch_flashlayout0 = BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) | 14148c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_META_SIZE(meta) | 14158c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength, this) | 14168c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_GF(geo->gf_len, this) | 14178c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(geo->ecc_chunk_size, this); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci this->bch_flashlayout1 = BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) | 14208c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength, this) | 14218c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_GF(geo->gf_len, this) | 14228c2ecf20Sopenharmony_ci BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(geo->ecc_chunk_size, this); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci this->bch = true; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, col, buf, page_size); 14278c2ecf20Sopenharmony_ci if (ret) 14288c2ecf20Sopenharmony_ci return ret; 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", 14318c2ecf20Sopenharmony_ci page, offs, len, col, first, n, page_size); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci max_bitflips = gpmi_count_bitflips(chip, buf, first, last, meta); 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci return max_bitflips; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf, 14398c2ecf20Sopenharmony_ci int oob_required, int page) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 14428c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 14438c2ecf20Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 14448c2ecf20Sopenharmony_ci int ret; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci dev_dbg(this->dev, "ecc write page.\n"); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci gpmi_bch_layout_std(this); 14498c2ecf20Sopenharmony_ci this->bch = true; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci memcpy(this->auxiliary_virt, chip->oob_poi, nfc_geo->auxiliary_size); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (this->swap_block_mark) { 14548c2ecf20Sopenharmony_ci /* 14558c2ecf20Sopenharmony_ci * When doing bad block marker swapping we must always copy the 14568c2ecf20Sopenharmony_ci * input buffer as we can't modify the const buffer. 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_ci memcpy(this->data_buffer_dma, buf, mtd->writesize); 14598c2ecf20Sopenharmony_ci buf = this->data_buffer_dma; 14608c2ecf20Sopenharmony_ci block_mark_swapping(this, this->data_buffer_dma, 14618c2ecf20Sopenharmony_ci this->auxiliary_virt); 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci ret = nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return ret; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci/* 14708c2ecf20Sopenharmony_ci * There are several places in this driver where we have to handle the OOB and 14718c2ecf20Sopenharmony_ci * block marks. This is the function where things are the most complicated, so 14728c2ecf20Sopenharmony_ci * this is where we try to explain it all. All the other places refer back to 14738c2ecf20Sopenharmony_ci * here. 14748c2ecf20Sopenharmony_ci * 14758c2ecf20Sopenharmony_ci * These are the rules, in order of decreasing importance: 14768c2ecf20Sopenharmony_ci * 14778c2ecf20Sopenharmony_ci * 1) Nothing the caller does can be allowed to imperil the block mark. 14788c2ecf20Sopenharmony_ci * 14798c2ecf20Sopenharmony_ci * 2) In read operations, the first byte of the OOB we return must reflect the 14808c2ecf20Sopenharmony_ci * true state of the block mark, no matter where that block mark appears in 14818c2ecf20Sopenharmony_ci * the physical page. 14828c2ecf20Sopenharmony_ci * 14838c2ecf20Sopenharmony_ci * 3) ECC-based read operations return an OOB full of set bits (since we never 14848c2ecf20Sopenharmony_ci * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads 14858c2ecf20Sopenharmony_ci * return). 14868c2ecf20Sopenharmony_ci * 14878c2ecf20Sopenharmony_ci * 4) "Raw" read operations return a direct view of the physical bytes in the 14888c2ecf20Sopenharmony_ci * page, using the conventional definition of which bytes are data and which 14898c2ecf20Sopenharmony_ci * are OOB. This gives the caller a way to see the actual, physical bytes 14908c2ecf20Sopenharmony_ci * in the page, without the distortions applied by our ECC engine. 14918c2ecf20Sopenharmony_ci * 14928c2ecf20Sopenharmony_ci * 14938c2ecf20Sopenharmony_ci * What we do for this specific read operation depends on two questions: 14948c2ecf20Sopenharmony_ci * 14958c2ecf20Sopenharmony_ci * 1) Are we doing a "raw" read, or an ECC-based read? 14968c2ecf20Sopenharmony_ci * 14978c2ecf20Sopenharmony_ci * 2) Are we using block mark swapping or transcription? 14988c2ecf20Sopenharmony_ci * 14998c2ecf20Sopenharmony_ci * There are four cases, illustrated by the following Karnaugh map: 15008c2ecf20Sopenharmony_ci * 15018c2ecf20Sopenharmony_ci * | Raw | ECC-based | 15028c2ecf20Sopenharmony_ci * -------------+-------------------------+-------------------------+ 15038c2ecf20Sopenharmony_ci * | Read the conventional | | 15048c2ecf20Sopenharmony_ci * | OOB at the end of the | | 15058c2ecf20Sopenharmony_ci * Swapping | page and return it. It | | 15068c2ecf20Sopenharmony_ci * | contains exactly what | | 15078c2ecf20Sopenharmony_ci * | we want. | Read the block mark and | 15088c2ecf20Sopenharmony_ci * -------------+-------------------------+ return it in a buffer | 15098c2ecf20Sopenharmony_ci * | Read the conventional | full of set bits. | 15108c2ecf20Sopenharmony_ci * | OOB at the end of the | | 15118c2ecf20Sopenharmony_ci * | page and also the block | | 15128c2ecf20Sopenharmony_ci * Transcribing | mark in the metadata. | | 15138c2ecf20Sopenharmony_ci * | Copy the block mark | | 15148c2ecf20Sopenharmony_ci * | into the first byte of | | 15158c2ecf20Sopenharmony_ci * | the OOB. | | 15168c2ecf20Sopenharmony_ci * -------------+-------------------------+-------------------------+ 15178c2ecf20Sopenharmony_ci * 15188c2ecf20Sopenharmony_ci * Note that we break rule #4 in the Transcribing/Raw case because we're not 15198c2ecf20Sopenharmony_ci * giving an accurate view of the actual, physical bytes in the page (we're 15208c2ecf20Sopenharmony_ci * overwriting the block mark). That's OK because it's more important to follow 15218c2ecf20Sopenharmony_ci * rule #2. 15228c2ecf20Sopenharmony_ci * 15238c2ecf20Sopenharmony_ci * It turns out that knowing whether we want an "ECC-based" or "raw" read is not 15248c2ecf20Sopenharmony_ci * easy. When reading a page, for example, the NAND Flash MTD code calls our 15258c2ecf20Sopenharmony_ci * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an 15268c2ecf20Sopenharmony_ci * ECC-based or raw view of the page is implicit in which function it calls 15278c2ecf20Sopenharmony_ci * (there is a similar pair of ECC-based/raw functions for writing). 15288c2ecf20Sopenharmony_ci */ 15298c2ecf20Sopenharmony_cistatic int gpmi_ecc_read_oob(struct nand_chip *chip, int page) 15308c2ecf20Sopenharmony_ci{ 15318c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 15328c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 15338c2ecf20Sopenharmony_ci int ret; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci /* clear the OOB buffer */ 15368c2ecf20Sopenharmony_ci memset(chip->oob_poi, ~0, mtd->oobsize); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci /* Read out the conventional OOB. */ 15398c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, mtd->writesize, chip->oob_poi, 15408c2ecf20Sopenharmony_ci mtd->oobsize); 15418c2ecf20Sopenharmony_ci if (ret) 15428c2ecf20Sopenharmony_ci return ret; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* 15458c2ecf20Sopenharmony_ci * Now, we want to make sure the block mark is correct. In the 15468c2ecf20Sopenharmony_ci * non-transcribing case (!GPMI_IS_MX23()), we already have it. 15478c2ecf20Sopenharmony_ci * Otherwise, we need to explicitly read it. 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_ci if (GPMI_IS_MX23(this)) { 15508c2ecf20Sopenharmony_ci /* Read the block mark into the first byte of the OOB buffer. */ 15518c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, chip->oob_poi, 1); 15528c2ecf20Sopenharmony_ci if (ret) 15538c2ecf20Sopenharmony_ci return ret; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci return 0; 15578c2ecf20Sopenharmony_ci} 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic int gpmi_ecc_write_oob(struct nand_chip *chip, int page) 15608c2ecf20Sopenharmony_ci{ 15618c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 15628c2ecf20Sopenharmony_ci struct mtd_oob_region of = { }; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* Do we have available oob area? */ 15658c2ecf20Sopenharmony_ci mtd_ooblayout_free(mtd, 0, &of); 15668c2ecf20Sopenharmony_ci if (!of.length) 15678c2ecf20Sopenharmony_ci return -EPERM; 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci if (!nand_is_slc(chip)) 15708c2ecf20Sopenharmony_ci return -EPERM; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci return nand_prog_page_op(chip, page, mtd->writesize + of.offset, 15738c2ecf20Sopenharmony_ci chip->oob_poi + of.offset, of.length); 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci/* 15778c2ecf20Sopenharmony_ci * This function reads a NAND page without involving the ECC engine (no HW 15788c2ecf20Sopenharmony_ci * ECC correction). 15798c2ecf20Sopenharmony_ci * The tricky part in the GPMI/BCH controller is that it stores ECC bits 15808c2ecf20Sopenharmony_ci * inline (interleaved with payload DATA), and do not align data chunk on 15818c2ecf20Sopenharmony_ci * byte boundaries. 15828c2ecf20Sopenharmony_ci * We thus need to take care moving the payload data and ECC bits stored in the 15838c2ecf20Sopenharmony_ci * page into the provided buffers, which is why we're using nand_extract_bits(). 15848c2ecf20Sopenharmony_ci * 15858c2ecf20Sopenharmony_ci * See set_geometry_by_ecc_info inline comments to have a full description 15868c2ecf20Sopenharmony_ci * of the layout used by the GPMI controller. 15878c2ecf20Sopenharmony_ci */ 15888c2ecf20Sopenharmony_cistatic int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf, 15898c2ecf20Sopenharmony_ci int oob_required, int page) 15908c2ecf20Sopenharmony_ci{ 15918c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 15928c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 15938c2ecf20Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 15948c2ecf20Sopenharmony_ci int eccsize = nfc_geo->ecc_chunk_size; 15958c2ecf20Sopenharmony_ci int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; 15968c2ecf20Sopenharmony_ci u8 *tmp_buf = this->raw_buffer; 15978c2ecf20Sopenharmony_ci size_t src_bit_off; 15988c2ecf20Sopenharmony_ci size_t oob_bit_off; 15998c2ecf20Sopenharmony_ci size_t oob_byte_off; 16008c2ecf20Sopenharmony_ci uint8_t *oob = chip->oob_poi; 16018c2ecf20Sopenharmony_ci int step; 16028c2ecf20Sopenharmony_ci int ret; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, tmp_buf, 16058c2ecf20Sopenharmony_ci mtd->writesize + mtd->oobsize); 16068c2ecf20Sopenharmony_ci if (ret) 16078c2ecf20Sopenharmony_ci return ret; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* 16108c2ecf20Sopenharmony_ci * If required, swap the bad block marker and the data stored in the 16118c2ecf20Sopenharmony_ci * metadata section, so that we don't wrongly consider a block as bad. 16128c2ecf20Sopenharmony_ci * 16138c2ecf20Sopenharmony_ci * See the layout description for a detailed explanation on why this 16148c2ecf20Sopenharmony_ci * is needed. 16158c2ecf20Sopenharmony_ci */ 16168c2ecf20Sopenharmony_ci if (this->swap_block_mark) 16178c2ecf20Sopenharmony_ci swap(tmp_buf[0], tmp_buf[mtd->writesize]); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* 16208c2ecf20Sopenharmony_ci * Copy the metadata section into the oob buffer (this section is 16218c2ecf20Sopenharmony_ci * guaranteed to be aligned on a byte boundary). 16228c2ecf20Sopenharmony_ci */ 16238c2ecf20Sopenharmony_ci if (oob_required) 16248c2ecf20Sopenharmony_ci memcpy(oob, tmp_buf, nfc_geo->metadata_size); 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci oob_bit_off = nfc_geo->metadata_size * 8; 16278c2ecf20Sopenharmony_ci src_bit_off = oob_bit_off; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* Extract interleaved payload data and ECC bits */ 16308c2ecf20Sopenharmony_ci for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { 16318c2ecf20Sopenharmony_ci if (buf) 16328c2ecf20Sopenharmony_ci nand_extract_bits(buf, step * eccsize * 8, tmp_buf, 16338c2ecf20Sopenharmony_ci src_bit_off, eccsize * 8); 16348c2ecf20Sopenharmony_ci src_bit_off += eccsize * 8; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci /* Align last ECC block to align a byte boundary */ 16378c2ecf20Sopenharmony_ci if (step == nfc_geo->ecc_chunk_count - 1 && 16388c2ecf20Sopenharmony_ci (oob_bit_off + eccbits) % 8) 16398c2ecf20Sopenharmony_ci eccbits += 8 - ((oob_bit_off + eccbits) % 8); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (oob_required) 16428c2ecf20Sopenharmony_ci nand_extract_bits(oob, oob_bit_off, tmp_buf, 16438c2ecf20Sopenharmony_ci src_bit_off, eccbits); 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci src_bit_off += eccbits; 16468c2ecf20Sopenharmony_ci oob_bit_off += eccbits; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (oob_required) { 16508c2ecf20Sopenharmony_ci oob_byte_off = oob_bit_off / 8; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci if (oob_byte_off < mtd->oobsize) 16538c2ecf20Sopenharmony_ci memcpy(oob + oob_byte_off, 16548c2ecf20Sopenharmony_ci tmp_buf + mtd->writesize + oob_byte_off, 16558c2ecf20Sopenharmony_ci mtd->oobsize - oob_byte_off); 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci return 0; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci/* 16628c2ecf20Sopenharmony_ci * This function writes a NAND page without involving the ECC engine (no HW 16638c2ecf20Sopenharmony_ci * ECC generation). 16648c2ecf20Sopenharmony_ci * The tricky part in the GPMI/BCH controller is that it stores ECC bits 16658c2ecf20Sopenharmony_ci * inline (interleaved with payload DATA), and do not align data chunk on 16668c2ecf20Sopenharmony_ci * byte boundaries. 16678c2ecf20Sopenharmony_ci * We thus need to take care moving the OOB area at the right place in the 16688c2ecf20Sopenharmony_ci * final page, which is why we're using nand_extract_bits(). 16698c2ecf20Sopenharmony_ci * 16708c2ecf20Sopenharmony_ci * See set_geometry_by_ecc_info inline comments to have a full description 16718c2ecf20Sopenharmony_ci * of the layout used by the GPMI controller. 16728c2ecf20Sopenharmony_ci */ 16738c2ecf20Sopenharmony_cistatic int gpmi_ecc_write_page_raw(struct nand_chip *chip, const uint8_t *buf, 16748c2ecf20Sopenharmony_ci int oob_required, int page) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 16778c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 16788c2ecf20Sopenharmony_ci struct bch_geometry *nfc_geo = &this->bch_geometry; 16798c2ecf20Sopenharmony_ci int eccsize = nfc_geo->ecc_chunk_size; 16808c2ecf20Sopenharmony_ci int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len; 16818c2ecf20Sopenharmony_ci u8 *tmp_buf = this->raw_buffer; 16828c2ecf20Sopenharmony_ci uint8_t *oob = chip->oob_poi; 16838c2ecf20Sopenharmony_ci size_t dst_bit_off; 16848c2ecf20Sopenharmony_ci size_t oob_bit_off; 16858c2ecf20Sopenharmony_ci size_t oob_byte_off; 16868c2ecf20Sopenharmony_ci int step; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* 16898c2ecf20Sopenharmony_ci * Initialize all bits to 1 in case we don't have a buffer for the 16908c2ecf20Sopenharmony_ci * payload or oob data in order to leave unspecified bits of data 16918c2ecf20Sopenharmony_ci * to their initial state. 16928c2ecf20Sopenharmony_ci */ 16938c2ecf20Sopenharmony_ci if (!buf || !oob_required) 16948c2ecf20Sopenharmony_ci memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci /* 16978c2ecf20Sopenharmony_ci * First copy the metadata section (stored in oob buffer) at the 16988c2ecf20Sopenharmony_ci * beginning of the page, as imposed by the GPMI layout. 16998c2ecf20Sopenharmony_ci */ 17008c2ecf20Sopenharmony_ci memcpy(tmp_buf, oob, nfc_geo->metadata_size); 17018c2ecf20Sopenharmony_ci oob_bit_off = nfc_geo->metadata_size * 8; 17028c2ecf20Sopenharmony_ci dst_bit_off = oob_bit_off; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci /* Interleave payload data and ECC bits */ 17058c2ecf20Sopenharmony_ci for (step = 0; step < nfc_geo->ecc_chunk_count; step++) { 17068c2ecf20Sopenharmony_ci if (buf) 17078c2ecf20Sopenharmony_ci nand_extract_bits(tmp_buf, dst_bit_off, buf, 17088c2ecf20Sopenharmony_ci step * eccsize * 8, eccsize * 8); 17098c2ecf20Sopenharmony_ci dst_bit_off += eccsize * 8; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* Align last ECC block to align a byte boundary */ 17128c2ecf20Sopenharmony_ci if (step == nfc_geo->ecc_chunk_count - 1 && 17138c2ecf20Sopenharmony_ci (oob_bit_off + eccbits) % 8) 17148c2ecf20Sopenharmony_ci eccbits += 8 - ((oob_bit_off + eccbits) % 8); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci if (oob_required) 17178c2ecf20Sopenharmony_ci nand_extract_bits(tmp_buf, dst_bit_off, oob, 17188c2ecf20Sopenharmony_ci oob_bit_off, eccbits); 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci dst_bit_off += eccbits; 17218c2ecf20Sopenharmony_ci oob_bit_off += eccbits; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci oob_byte_off = oob_bit_off / 8; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (oob_required && oob_byte_off < mtd->oobsize) 17278c2ecf20Sopenharmony_ci memcpy(tmp_buf + mtd->writesize + oob_byte_off, 17288c2ecf20Sopenharmony_ci oob + oob_byte_off, mtd->oobsize - oob_byte_off); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci /* 17318c2ecf20Sopenharmony_ci * If required, swap the bad block marker and the first byte of the 17328c2ecf20Sopenharmony_ci * metadata section, so that we don't modify the bad block marker. 17338c2ecf20Sopenharmony_ci * 17348c2ecf20Sopenharmony_ci * See the layout description for a detailed explanation on why this 17358c2ecf20Sopenharmony_ci * is needed. 17368c2ecf20Sopenharmony_ci */ 17378c2ecf20Sopenharmony_ci if (this->swap_block_mark) 17388c2ecf20Sopenharmony_ci swap(tmp_buf[0], tmp_buf[mtd->writesize]); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci return nand_prog_page_op(chip, page, 0, tmp_buf, 17418c2ecf20Sopenharmony_ci mtd->writesize + mtd->oobsize); 17428c2ecf20Sopenharmony_ci} 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic int gpmi_ecc_read_oob_raw(struct nand_chip *chip, int page) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci return gpmi_ecc_read_page_raw(chip, NULL, 1, page); 17478c2ecf20Sopenharmony_ci} 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_cistatic int gpmi_ecc_write_oob_raw(struct nand_chip *chip, int page) 17508c2ecf20Sopenharmony_ci{ 17518c2ecf20Sopenharmony_ci return gpmi_ecc_write_page_raw(chip, NULL, 1, page); 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_cistatic int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs) 17558c2ecf20Sopenharmony_ci{ 17568c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 17578c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 17588c2ecf20Sopenharmony_ci int ret = 0; 17598c2ecf20Sopenharmony_ci uint8_t *block_mark; 17608c2ecf20Sopenharmony_ci int column, page, chipnr; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci chipnr = (int)(ofs >> chip->chip_shift); 17638c2ecf20Sopenharmony_ci nand_select_target(chip, chipnr); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci column = !GPMI_IS_MX23(this) ? mtd->writesize : 0; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci /* Write the block mark. */ 17688c2ecf20Sopenharmony_ci block_mark = this->data_buffer_dma; 17698c2ecf20Sopenharmony_ci block_mark[0] = 0; /* bad block marker */ 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci /* Shift to get page */ 17728c2ecf20Sopenharmony_ci page = (int)(ofs >> chip->page_shift); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci ret = nand_prog_page_op(chip, page, column, block_mark, 1); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci nand_deselect_target(chip); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci return ret; 17798c2ecf20Sopenharmony_ci} 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_cistatic int nand_boot_set_geometry(struct gpmi_nand_data *this) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci struct boot_rom_geometry *geometry = &this->rom_geometry; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci /* 17868c2ecf20Sopenharmony_ci * Set the boot block stride size. 17878c2ecf20Sopenharmony_ci * 17888c2ecf20Sopenharmony_ci * In principle, we should be reading this from the OTP bits, since 17898c2ecf20Sopenharmony_ci * that's where the ROM is going to get it. In fact, we don't have any 17908c2ecf20Sopenharmony_ci * way to read the OTP bits, so we go with the default and hope for the 17918c2ecf20Sopenharmony_ci * best. 17928c2ecf20Sopenharmony_ci */ 17938c2ecf20Sopenharmony_ci geometry->stride_size_in_pages = 64; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci /* 17968c2ecf20Sopenharmony_ci * Set the search area stride exponent. 17978c2ecf20Sopenharmony_ci * 17988c2ecf20Sopenharmony_ci * In principle, we should be reading this from the OTP bits, since 17998c2ecf20Sopenharmony_ci * that's where the ROM is going to get it. In fact, we don't have any 18008c2ecf20Sopenharmony_ci * way to read the OTP bits, so we go with the default and hope for the 18018c2ecf20Sopenharmony_ci * best. 18028c2ecf20Sopenharmony_ci */ 18038c2ecf20Sopenharmony_ci geometry->search_area_stride_exponent = 2; 18048c2ecf20Sopenharmony_ci return 0; 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_cistatic const char *fingerprint = "STMP"; 18088c2ecf20Sopenharmony_cistatic int mx23_check_transcription_stamp(struct gpmi_nand_data *this) 18098c2ecf20Sopenharmony_ci{ 18108c2ecf20Sopenharmony_ci struct boot_rom_geometry *rom_geo = &this->rom_geometry; 18118c2ecf20Sopenharmony_ci struct device *dev = this->dev; 18128c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 18138c2ecf20Sopenharmony_ci unsigned int search_area_size_in_strides; 18148c2ecf20Sopenharmony_ci unsigned int stride; 18158c2ecf20Sopenharmony_ci unsigned int page; 18168c2ecf20Sopenharmony_ci u8 *buffer = nand_get_data_buf(chip); 18178c2ecf20Sopenharmony_ci int found_an_ncb_fingerprint = false; 18188c2ecf20Sopenharmony_ci int ret; 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci /* Compute the number of strides in a search area. */ 18218c2ecf20Sopenharmony_ci search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci nand_select_target(chip, 0); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci /* 18268c2ecf20Sopenharmony_ci * Loop through the first search area, looking for the NCB fingerprint. 18278c2ecf20Sopenharmony_ci */ 18288c2ecf20Sopenharmony_ci dev_dbg(dev, "Scanning for an NCB fingerprint...\n"); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci for (stride = 0; stride < search_area_size_in_strides; stride++) { 18318c2ecf20Sopenharmony_ci /* Compute the page addresses. */ 18328c2ecf20Sopenharmony_ci page = stride * rom_geo->stride_size_in_pages; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci dev_dbg(dev, "Looking for a fingerprint in page 0x%x\n", page); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci /* 18378c2ecf20Sopenharmony_ci * Read the NCB fingerprint. The fingerprint is four bytes long 18388c2ecf20Sopenharmony_ci * and starts in the 12th byte of the page. 18398c2ecf20Sopenharmony_ci */ 18408c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 12, buffer, 18418c2ecf20Sopenharmony_ci strlen(fingerprint)); 18428c2ecf20Sopenharmony_ci if (ret) 18438c2ecf20Sopenharmony_ci continue; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* Look for the fingerprint. */ 18468c2ecf20Sopenharmony_ci if (!memcmp(buffer, fingerprint, strlen(fingerprint))) { 18478c2ecf20Sopenharmony_ci found_an_ncb_fingerprint = true; 18488c2ecf20Sopenharmony_ci break; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci nand_deselect_target(chip); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci if (found_an_ncb_fingerprint) 18568c2ecf20Sopenharmony_ci dev_dbg(dev, "\tFound a fingerprint\n"); 18578c2ecf20Sopenharmony_ci else 18588c2ecf20Sopenharmony_ci dev_dbg(dev, "\tNo fingerprint found\n"); 18598c2ecf20Sopenharmony_ci return found_an_ncb_fingerprint; 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci/* Writes a transcription stamp. */ 18638c2ecf20Sopenharmony_cistatic int mx23_write_transcription_stamp(struct gpmi_nand_data *this) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci struct device *dev = this->dev; 18668c2ecf20Sopenharmony_ci struct boot_rom_geometry *rom_geo = &this->rom_geometry; 18678c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 18688c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 18698c2ecf20Sopenharmony_ci unsigned int block_size_in_pages; 18708c2ecf20Sopenharmony_ci unsigned int search_area_size_in_strides; 18718c2ecf20Sopenharmony_ci unsigned int search_area_size_in_pages; 18728c2ecf20Sopenharmony_ci unsigned int search_area_size_in_blocks; 18738c2ecf20Sopenharmony_ci unsigned int block; 18748c2ecf20Sopenharmony_ci unsigned int stride; 18758c2ecf20Sopenharmony_ci unsigned int page; 18768c2ecf20Sopenharmony_ci u8 *buffer = nand_get_data_buf(chip); 18778c2ecf20Sopenharmony_ci int status; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci /* Compute the search area geometry. */ 18808c2ecf20Sopenharmony_ci block_size_in_pages = mtd->erasesize / mtd->writesize; 18818c2ecf20Sopenharmony_ci search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent; 18828c2ecf20Sopenharmony_ci search_area_size_in_pages = search_area_size_in_strides * 18838c2ecf20Sopenharmony_ci rom_geo->stride_size_in_pages; 18848c2ecf20Sopenharmony_ci search_area_size_in_blocks = 18858c2ecf20Sopenharmony_ci (search_area_size_in_pages + (block_size_in_pages - 1)) / 18868c2ecf20Sopenharmony_ci block_size_in_pages; 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci dev_dbg(dev, "Search Area Geometry :\n"); 18898c2ecf20Sopenharmony_ci dev_dbg(dev, "\tin Blocks : %u\n", search_area_size_in_blocks); 18908c2ecf20Sopenharmony_ci dev_dbg(dev, "\tin Strides: %u\n", search_area_size_in_strides); 18918c2ecf20Sopenharmony_ci dev_dbg(dev, "\tin Pages : %u\n", search_area_size_in_pages); 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci nand_select_target(chip, 0); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* Loop over blocks in the first search area, erasing them. */ 18968c2ecf20Sopenharmony_ci dev_dbg(dev, "Erasing the search area...\n"); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci for (block = 0; block < search_area_size_in_blocks; block++) { 18998c2ecf20Sopenharmony_ci /* Erase this block. */ 19008c2ecf20Sopenharmony_ci dev_dbg(dev, "\tErasing block 0x%x\n", block); 19018c2ecf20Sopenharmony_ci status = nand_erase_op(chip, block); 19028c2ecf20Sopenharmony_ci if (status) 19038c2ecf20Sopenharmony_ci dev_err(dev, "[%s] Erase failed.\n", __func__); 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci /* Write the NCB fingerprint into the page buffer. */ 19078c2ecf20Sopenharmony_ci memset(buffer, ~0, mtd->writesize); 19088c2ecf20Sopenharmony_ci memcpy(buffer + 12, fingerprint, strlen(fingerprint)); 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci /* Loop through the first search area, writing NCB fingerprints. */ 19118c2ecf20Sopenharmony_ci dev_dbg(dev, "Writing NCB fingerprints...\n"); 19128c2ecf20Sopenharmony_ci for (stride = 0; stride < search_area_size_in_strides; stride++) { 19138c2ecf20Sopenharmony_ci /* Compute the page addresses. */ 19148c2ecf20Sopenharmony_ci page = stride * rom_geo->stride_size_in_pages; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci /* Write the first page of the current stride. */ 19178c2ecf20Sopenharmony_ci dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci status = chip->ecc.write_page_raw(chip, buffer, 0, page); 19208c2ecf20Sopenharmony_ci if (status) 19218c2ecf20Sopenharmony_ci dev_err(dev, "[%s] Write failed.\n", __func__); 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci nand_deselect_target(chip); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci return 0; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_cistatic int mx23_boot_init(struct gpmi_nand_data *this) 19308c2ecf20Sopenharmony_ci{ 19318c2ecf20Sopenharmony_ci struct device *dev = this->dev; 19328c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 19338c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 19348c2ecf20Sopenharmony_ci unsigned int block_count; 19358c2ecf20Sopenharmony_ci unsigned int block; 19368c2ecf20Sopenharmony_ci int chipnr; 19378c2ecf20Sopenharmony_ci int page; 19388c2ecf20Sopenharmony_ci loff_t byte; 19398c2ecf20Sopenharmony_ci uint8_t block_mark; 19408c2ecf20Sopenharmony_ci int ret = 0; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci /* 19438c2ecf20Sopenharmony_ci * If control arrives here, we can't use block mark swapping, which 19448c2ecf20Sopenharmony_ci * means we're forced to use transcription. First, scan for the 19458c2ecf20Sopenharmony_ci * transcription stamp. If we find it, then we don't have to do 19468c2ecf20Sopenharmony_ci * anything -- the block marks are already transcribed. 19478c2ecf20Sopenharmony_ci */ 19488c2ecf20Sopenharmony_ci if (mx23_check_transcription_stamp(this)) 19498c2ecf20Sopenharmony_ci return 0; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci /* 19528c2ecf20Sopenharmony_ci * If control arrives here, we couldn't find a transcription stamp, so 19538c2ecf20Sopenharmony_ci * so we presume the block marks are in the conventional location. 19548c2ecf20Sopenharmony_ci */ 19558c2ecf20Sopenharmony_ci dev_dbg(dev, "Transcribing bad block marks...\n"); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci /* Compute the number of blocks in the entire medium. */ 19588c2ecf20Sopenharmony_ci block_count = nanddev_eraseblocks_per_target(&chip->base); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* 19618c2ecf20Sopenharmony_ci * Loop over all the blocks in the medium, transcribing block marks as 19628c2ecf20Sopenharmony_ci * we go. 19638c2ecf20Sopenharmony_ci */ 19648c2ecf20Sopenharmony_ci for (block = 0; block < block_count; block++) { 19658c2ecf20Sopenharmony_ci /* 19668c2ecf20Sopenharmony_ci * Compute the chip, page and byte addresses for this block's 19678c2ecf20Sopenharmony_ci * conventional mark. 19688c2ecf20Sopenharmony_ci */ 19698c2ecf20Sopenharmony_ci chipnr = block >> (chip->chip_shift - chip->phys_erase_shift); 19708c2ecf20Sopenharmony_ci page = block << (chip->phys_erase_shift - chip->page_shift); 19718c2ecf20Sopenharmony_ci byte = block << chip->phys_erase_shift; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci /* Send the command to read the conventional block mark. */ 19748c2ecf20Sopenharmony_ci nand_select_target(chip, chipnr); 19758c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, mtd->writesize, &block_mark, 19768c2ecf20Sopenharmony_ci 1); 19778c2ecf20Sopenharmony_ci nand_deselect_target(chip); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci if (ret) 19808c2ecf20Sopenharmony_ci continue; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* 19838c2ecf20Sopenharmony_ci * Check if the block is marked bad. If so, we need to mark it 19848c2ecf20Sopenharmony_ci * again, but this time the result will be a mark in the 19858c2ecf20Sopenharmony_ci * location where we transcribe block marks. 19868c2ecf20Sopenharmony_ci */ 19878c2ecf20Sopenharmony_ci if (block_mark != 0xff) { 19888c2ecf20Sopenharmony_ci dev_dbg(dev, "Transcribing mark in block %u\n", block); 19898c2ecf20Sopenharmony_ci ret = chip->legacy.block_markbad(chip, byte); 19908c2ecf20Sopenharmony_ci if (ret) 19918c2ecf20Sopenharmony_ci dev_err(dev, 19928c2ecf20Sopenharmony_ci "Failed to mark block bad with ret %d\n", 19938c2ecf20Sopenharmony_ci ret); 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* Write the stamp that indicates we've transcribed the block marks. */ 19988c2ecf20Sopenharmony_ci mx23_write_transcription_stamp(this); 19998c2ecf20Sopenharmony_ci return 0; 20008c2ecf20Sopenharmony_ci} 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_cistatic int nand_boot_init(struct gpmi_nand_data *this) 20038c2ecf20Sopenharmony_ci{ 20048c2ecf20Sopenharmony_ci nand_boot_set_geometry(this); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci /* This is ROM arch-specific initilization before the BBT scanning. */ 20078c2ecf20Sopenharmony_ci if (GPMI_IS_MX23(this)) 20088c2ecf20Sopenharmony_ci return mx23_boot_init(this); 20098c2ecf20Sopenharmony_ci return 0; 20108c2ecf20Sopenharmony_ci} 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_cistatic int gpmi_set_geometry(struct gpmi_nand_data *this) 20138c2ecf20Sopenharmony_ci{ 20148c2ecf20Sopenharmony_ci int ret; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* Free the temporary DMA memory for reading ID. */ 20178c2ecf20Sopenharmony_ci gpmi_free_dma_buffer(this); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci /* Set up the NFC geometry which is used by BCH. */ 20208c2ecf20Sopenharmony_ci ret = bch_set_geometry(this); 20218c2ecf20Sopenharmony_ci if (ret) { 20228c2ecf20Sopenharmony_ci dev_err(this->dev, "Error setting BCH geometry : %d\n", ret); 20238c2ecf20Sopenharmony_ci return ret; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci /* Alloc the new DMA buffers according to the pagesize and oobsize */ 20278c2ecf20Sopenharmony_ci return gpmi_alloc_dma_buffer(this); 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_cistatic int gpmi_init_last(struct gpmi_nand_data *this) 20318c2ecf20Sopenharmony_ci{ 20328c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 20338c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 20348c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 20358c2ecf20Sopenharmony_ci struct bch_geometry *bch_geo = &this->bch_geometry; 20368c2ecf20Sopenharmony_ci int ret; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci /* Set up the medium geometry */ 20398c2ecf20Sopenharmony_ci ret = gpmi_set_geometry(this); 20408c2ecf20Sopenharmony_ci if (ret) 20418c2ecf20Sopenharmony_ci return ret; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci /* Init the nand_ecc_ctrl{} */ 20448c2ecf20Sopenharmony_ci ecc->read_page = gpmi_ecc_read_page; 20458c2ecf20Sopenharmony_ci ecc->write_page = gpmi_ecc_write_page; 20468c2ecf20Sopenharmony_ci ecc->read_oob = gpmi_ecc_read_oob; 20478c2ecf20Sopenharmony_ci ecc->write_oob = gpmi_ecc_write_oob; 20488c2ecf20Sopenharmony_ci ecc->read_page_raw = gpmi_ecc_read_page_raw; 20498c2ecf20Sopenharmony_ci ecc->write_page_raw = gpmi_ecc_write_page_raw; 20508c2ecf20Sopenharmony_ci ecc->read_oob_raw = gpmi_ecc_read_oob_raw; 20518c2ecf20Sopenharmony_ci ecc->write_oob_raw = gpmi_ecc_write_oob_raw; 20528c2ecf20Sopenharmony_ci ecc->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 20538c2ecf20Sopenharmony_ci ecc->size = bch_geo->ecc_chunk_size; 20548c2ecf20Sopenharmony_ci ecc->strength = bch_geo->ecc_strength; 20558c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &gpmi_ooblayout_ops); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci /* 20588c2ecf20Sopenharmony_ci * We only enable the subpage read when: 20598c2ecf20Sopenharmony_ci * (1) the chip is imx6, and 20608c2ecf20Sopenharmony_ci * (2) the size of the ECC parity is byte aligned. 20618c2ecf20Sopenharmony_ci */ 20628c2ecf20Sopenharmony_ci if (GPMI_IS_MX6(this) && 20638c2ecf20Sopenharmony_ci ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { 20648c2ecf20Sopenharmony_ci ecc->read_subpage = gpmi_ecc_read_subpage; 20658c2ecf20Sopenharmony_ci chip->options |= NAND_SUBPAGE_READ; 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci return 0; 20698c2ecf20Sopenharmony_ci} 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_cistatic int gpmi_nand_attach_chip(struct nand_chip *chip) 20728c2ecf20Sopenharmony_ci{ 20738c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 20748c2ecf20Sopenharmony_ci int ret; 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci if (chip->bbt_options & NAND_BBT_USE_FLASH) { 20778c2ecf20Sopenharmony_ci chip->bbt_options |= NAND_BBT_NO_OOB; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci if (of_property_read_bool(this->dev->of_node, 20808c2ecf20Sopenharmony_ci "fsl,no-blockmark-swap")) 20818c2ecf20Sopenharmony_ci this->swap_block_mark = false; 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci dev_dbg(this->dev, "Blockmark swapping %sabled\n", 20848c2ecf20Sopenharmony_ci this->swap_block_mark ? "en" : "dis"); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci ret = gpmi_init_last(this); 20878c2ecf20Sopenharmony_ci if (ret) 20888c2ecf20Sopenharmony_ci return ret; 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci chip->options |= NAND_SKIP_BBTSCAN; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci return 0; 20938c2ecf20Sopenharmony_ci} 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_cistatic struct gpmi_transfer *get_next_transfer(struct gpmi_nand_data *this) 20968c2ecf20Sopenharmony_ci{ 20978c2ecf20Sopenharmony_ci struct gpmi_transfer *transfer = &this->transfers[this->ntransfers]; 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci this->ntransfers++; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci if (this->ntransfers == GPMI_MAX_TRANSFERS) 21028c2ecf20Sopenharmony_ci return NULL; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci return transfer; 21058c2ecf20Sopenharmony_ci} 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_command( 21088c2ecf20Sopenharmony_ci struct gpmi_nand_data *this, u8 cmd, const u8 *addr, int naddr) 21098c2ecf20Sopenharmony_ci{ 21108c2ecf20Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 21118c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 21128c2ecf20Sopenharmony_ci struct gpmi_transfer *transfer; 21138c2ecf20Sopenharmony_ci int chip = this->nand.cur_cs; 21148c2ecf20Sopenharmony_ci u32 pio[3]; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci /* [1] send out the PIO words */ 21178c2ecf20Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) 21188c2ecf20Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 21198c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_CS(chip, this) 21208c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 21218c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) 21228c2ecf20Sopenharmony_ci | BM_GPMI_CTRL0_ADDRESS_INCREMENT 21238c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(naddr + 1); 21248c2ecf20Sopenharmony_ci pio[1] = 0; 21258c2ecf20Sopenharmony_ci pio[2] = 0; 21268c2ecf20Sopenharmony_ci desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), 21278c2ecf20Sopenharmony_ci DMA_TRANS_NONE, 0); 21288c2ecf20Sopenharmony_ci if (!desc) 21298c2ecf20Sopenharmony_ci return NULL; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci transfer = get_next_transfer(this); 21328c2ecf20Sopenharmony_ci if (!transfer) 21338c2ecf20Sopenharmony_ci return NULL; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci transfer->cmdbuf[0] = cmd; 21368c2ecf20Sopenharmony_ci if (naddr) 21378c2ecf20Sopenharmony_ci memcpy(&transfer->cmdbuf[1], addr, naddr); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci sg_init_one(&transfer->sgl, transfer->cmdbuf, naddr + 1); 21408c2ecf20Sopenharmony_ci dma_map_sg(this->dev, &transfer->sgl, 1, DMA_TO_DEVICE); 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci transfer->direction = DMA_TO_DEVICE; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, DMA_MEM_TO_DEV, 21458c2ecf20Sopenharmony_ci MXS_DMA_CTRL_WAIT4END); 21468c2ecf20Sopenharmony_ci return desc; 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_wait_ready( 21508c2ecf20Sopenharmony_ci struct gpmi_nand_data *this) 21518c2ecf20Sopenharmony_ci{ 21528c2ecf20Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 21538c2ecf20Sopenharmony_ci u32 pio[2]; 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY) 21568c2ecf20Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 21578c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) 21588c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 21598c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) 21608c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(0); 21618c2ecf20Sopenharmony_ci pio[1] = 0; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci return mxs_dmaengine_prep_pio(channel, pio, 2, DMA_TRANS_NONE, 21648c2ecf20Sopenharmony_ci MXS_DMA_CTRL_WAIT4END | MXS_DMA_CTRL_WAIT4RDY); 21658c2ecf20Sopenharmony_ci} 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_data_read( 21688c2ecf20Sopenharmony_ci struct gpmi_nand_data *this, void *buf, int raw_len, bool *direct) 21698c2ecf20Sopenharmony_ci{ 21708c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 21718c2ecf20Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 21728c2ecf20Sopenharmony_ci struct gpmi_transfer *transfer; 21738c2ecf20Sopenharmony_ci u32 pio[6] = {}; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci transfer = get_next_transfer(this); 21768c2ecf20Sopenharmony_ci if (!transfer) 21778c2ecf20Sopenharmony_ci return NULL; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci transfer->direction = DMA_FROM_DEVICE; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci *direct = prepare_data_dma(this, buf, raw_len, &transfer->sgl, 21828c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) 21858c2ecf20Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 21868c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) 21878c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 21888c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) 21898c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(raw_len); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (this->bch) { 21928c2ecf20Sopenharmony_ci pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC 21938c2ecf20Sopenharmony_ci | BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE) 21948c2ecf20Sopenharmony_ci | BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 21958c2ecf20Sopenharmony_ci | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY); 21968c2ecf20Sopenharmony_ci pio[3] = raw_len; 21978c2ecf20Sopenharmony_ci pio[4] = transfer->sgl.dma_address; 21988c2ecf20Sopenharmony_ci pio[5] = this->auxiliary_phys; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), 22028c2ecf20Sopenharmony_ci DMA_TRANS_NONE, 0); 22038c2ecf20Sopenharmony_ci if (!desc) 22048c2ecf20Sopenharmony_ci return NULL; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci if (!this->bch) 22078c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, 22088c2ecf20Sopenharmony_ci DMA_DEV_TO_MEM, 22098c2ecf20Sopenharmony_ci MXS_DMA_CTRL_WAIT4END); 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci return desc; 22128c2ecf20Sopenharmony_ci} 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_cistatic struct dma_async_tx_descriptor *gpmi_chain_data_write( 22158c2ecf20Sopenharmony_ci struct gpmi_nand_data *this, const void *buf, int raw_len) 22168c2ecf20Sopenharmony_ci{ 22178c2ecf20Sopenharmony_ci struct dma_chan *channel = get_dma_chan(this); 22188c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 22198c2ecf20Sopenharmony_ci struct gpmi_transfer *transfer; 22208c2ecf20Sopenharmony_ci u32 pio[6] = {}; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci transfer = get_next_transfer(this); 22238c2ecf20Sopenharmony_ci if (!transfer) 22248c2ecf20Sopenharmony_ci return NULL; 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci transfer->direction = DMA_TO_DEVICE; 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci prepare_data_dma(this, buf, raw_len, &transfer->sgl, DMA_TO_DEVICE); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) 22318c2ecf20Sopenharmony_ci | BM_GPMI_CTRL0_WORD_LENGTH 22328c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_CS(this->nand.cur_cs, this) 22338c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_LOCK_CS(LOCK_CS_ENABLE, this) 22348c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) 22358c2ecf20Sopenharmony_ci | BF_GPMI_CTRL0_XFER_COUNT(raw_len); 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci if (this->bch) { 22388c2ecf20Sopenharmony_ci pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC 22398c2ecf20Sopenharmony_ci | BF_GPMI_ECCCTRL_ECC_CMD(BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE) 22408c2ecf20Sopenharmony_ci | BF_GPMI_ECCCTRL_BUFFER_MASK(BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | 22418c2ecf20Sopenharmony_ci BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY); 22428c2ecf20Sopenharmony_ci pio[3] = raw_len; 22438c2ecf20Sopenharmony_ci pio[4] = transfer->sgl.dma_address; 22448c2ecf20Sopenharmony_ci pio[5] = this->auxiliary_phys; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci desc = mxs_dmaengine_prep_pio(channel, pio, ARRAY_SIZE(pio), 22488c2ecf20Sopenharmony_ci DMA_TRANS_NONE, 22498c2ecf20Sopenharmony_ci (this->bch ? MXS_DMA_CTRL_WAIT4END : 0)); 22508c2ecf20Sopenharmony_ci if (!desc) 22518c2ecf20Sopenharmony_ci return NULL; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (!this->bch) 22548c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_sg(channel, &transfer->sgl, 1, 22558c2ecf20Sopenharmony_ci DMA_MEM_TO_DEV, 22568c2ecf20Sopenharmony_ci MXS_DMA_CTRL_WAIT4END); 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci return desc; 22598c2ecf20Sopenharmony_ci} 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_cistatic int gpmi_nfc_exec_op(struct nand_chip *chip, 22628c2ecf20Sopenharmony_ci const struct nand_operation *op, 22638c2ecf20Sopenharmony_ci bool check_only) 22648c2ecf20Sopenharmony_ci{ 22658c2ecf20Sopenharmony_ci const struct nand_op_instr *instr; 22668c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = nand_get_controller_data(chip); 22678c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc = NULL; 22688c2ecf20Sopenharmony_ci int i, ret, buf_len = 0, nbufs = 0; 22698c2ecf20Sopenharmony_ci u8 cmd = 0; 22708c2ecf20Sopenharmony_ci void *buf_read = NULL; 22718c2ecf20Sopenharmony_ci const void *buf_write = NULL; 22728c2ecf20Sopenharmony_ci bool direct = false; 22738c2ecf20Sopenharmony_ci struct completion *dma_completion, *bch_completion; 22748c2ecf20Sopenharmony_ci unsigned long to; 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (check_only) 22778c2ecf20Sopenharmony_ci return 0; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci this->ntransfers = 0; 22808c2ecf20Sopenharmony_ci for (i = 0; i < GPMI_MAX_TRANSFERS; i++) 22818c2ecf20Sopenharmony_ci this->transfers[i].direction = DMA_NONE; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(this->dev); 22848c2ecf20Sopenharmony_ci if (ret < 0) { 22858c2ecf20Sopenharmony_ci pm_runtime_put_noidle(this->dev); 22868c2ecf20Sopenharmony_ci return ret; 22878c2ecf20Sopenharmony_ci } 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci /* 22908c2ecf20Sopenharmony_ci * This driver currently supports only one NAND chip. Plus, dies share 22918c2ecf20Sopenharmony_ci * the same configuration. So once timings have been applied on the 22928c2ecf20Sopenharmony_ci * controller side, they will not change anymore. When the time will 22938c2ecf20Sopenharmony_ci * come, the check on must_apply_timings will have to be dropped. 22948c2ecf20Sopenharmony_ci */ 22958c2ecf20Sopenharmony_ci if (this->hw.must_apply_timings) { 22968c2ecf20Sopenharmony_ci this->hw.must_apply_timings = false; 22978c2ecf20Sopenharmony_ci ret = gpmi_nfc_apply_timings(this); 22988c2ecf20Sopenharmony_ci if (ret) 22998c2ecf20Sopenharmony_ci goto out_pm; 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs); 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 23058c2ecf20Sopenharmony_ci instr = &op->instrs[i]; 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci nand_op_trace(" ", instr); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci switch (instr->type) { 23108c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 23118c2ecf20Sopenharmony_ci desc = gpmi_chain_wait_ready(this); 23128c2ecf20Sopenharmony_ci break; 23138c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 23148c2ecf20Sopenharmony_ci cmd = instr->ctx.cmd.opcode; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci /* 23178c2ecf20Sopenharmony_ci * When this command has an address cycle chain it 23188c2ecf20Sopenharmony_ci * together with the address cycle 23198c2ecf20Sopenharmony_ci */ 23208c2ecf20Sopenharmony_ci if (i + 1 != op->ninstrs && 23218c2ecf20Sopenharmony_ci op->instrs[i + 1].type == NAND_OP_ADDR_INSTR) 23228c2ecf20Sopenharmony_ci continue; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci desc = gpmi_chain_command(this, cmd, NULL, 0); 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci break; 23278c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 23288c2ecf20Sopenharmony_ci desc = gpmi_chain_command(this, cmd, instr->ctx.addr.addrs, 23298c2ecf20Sopenharmony_ci instr->ctx.addr.naddrs); 23308c2ecf20Sopenharmony_ci break; 23318c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 23328c2ecf20Sopenharmony_ci buf_write = instr->ctx.data.buf.out; 23338c2ecf20Sopenharmony_ci buf_len = instr->ctx.data.len; 23348c2ecf20Sopenharmony_ci nbufs++; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci desc = gpmi_chain_data_write(this, buf_write, buf_len); 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci break; 23398c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 23408c2ecf20Sopenharmony_ci if (!instr->ctx.data.len) 23418c2ecf20Sopenharmony_ci break; 23428c2ecf20Sopenharmony_ci buf_read = instr->ctx.data.buf.in; 23438c2ecf20Sopenharmony_ci buf_len = instr->ctx.data.len; 23448c2ecf20Sopenharmony_ci nbufs++; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci desc = gpmi_chain_data_read(this, buf_read, buf_len, 23478c2ecf20Sopenharmony_ci &direct); 23488c2ecf20Sopenharmony_ci break; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci if (!desc) { 23528c2ecf20Sopenharmony_ci ret = -ENXIO; 23538c2ecf20Sopenharmony_ci goto unmap; 23548c2ecf20Sopenharmony_ci } 23558c2ecf20Sopenharmony_ci } 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci dev_dbg(this->dev, "%s setup done\n", __func__); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci if (nbufs > 1) { 23608c2ecf20Sopenharmony_ci dev_err(this->dev, "Multiple data instructions not supported\n"); 23618c2ecf20Sopenharmony_ci ret = -EINVAL; 23628c2ecf20Sopenharmony_ci goto unmap; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci if (this->bch) { 23668c2ecf20Sopenharmony_ci writel(this->bch_flashlayout0, 23678c2ecf20Sopenharmony_ci this->resources.bch_regs + HW_BCH_FLASH0LAYOUT0); 23688c2ecf20Sopenharmony_ci writel(this->bch_flashlayout1, 23698c2ecf20Sopenharmony_ci this->resources.bch_regs + HW_BCH_FLASH0LAYOUT1); 23708c2ecf20Sopenharmony_ci } 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci desc->callback = dma_irq_callback; 23738c2ecf20Sopenharmony_ci desc->callback_param = this; 23748c2ecf20Sopenharmony_ci dma_completion = &this->dma_done; 23758c2ecf20Sopenharmony_ci bch_completion = NULL; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci init_completion(dma_completion); 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci if (this->bch && buf_read) { 23808c2ecf20Sopenharmony_ci writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, 23818c2ecf20Sopenharmony_ci this->resources.bch_regs + HW_BCH_CTRL_SET); 23828c2ecf20Sopenharmony_ci bch_completion = &this->bch_done; 23838c2ecf20Sopenharmony_ci init_completion(bch_completion); 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci dmaengine_submit(desc); 23878c2ecf20Sopenharmony_ci dma_async_issue_pending(get_dma_chan(this)); 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci to = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000)); 23908c2ecf20Sopenharmony_ci if (!to) { 23918c2ecf20Sopenharmony_ci dev_err(this->dev, "DMA timeout, last DMA\n"); 23928c2ecf20Sopenharmony_ci gpmi_dump_info(this); 23938c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 23948c2ecf20Sopenharmony_ci goto unmap; 23958c2ecf20Sopenharmony_ci } 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci if (this->bch && buf_read) { 23988c2ecf20Sopenharmony_ci to = wait_for_completion_timeout(bch_completion, msecs_to_jiffies(1000)); 23998c2ecf20Sopenharmony_ci if (!to) { 24008c2ecf20Sopenharmony_ci dev_err(this->dev, "BCH timeout, last DMA\n"); 24018c2ecf20Sopenharmony_ci gpmi_dump_info(this); 24028c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 24038c2ecf20Sopenharmony_ci goto unmap; 24048c2ecf20Sopenharmony_ci } 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, 24088c2ecf20Sopenharmony_ci this->resources.bch_regs + HW_BCH_CTRL_CLR); 24098c2ecf20Sopenharmony_ci gpmi_clear_bch(this); 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci ret = 0; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ciunmap: 24148c2ecf20Sopenharmony_ci for (i = 0; i < this->ntransfers; i++) { 24158c2ecf20Sopenharmony_ci struct gpmi_transfer *transfer = &this->transfers[i]; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci if (transfer->direction != DMA_NONE) 24188c2ecf20Sopenharmony_ci dma_unmap_sg(this->dev, &transfer->sgl, 1, 24198c2ecf20Sopenharmony_ci transfer->direction); 24208c2ecf20Sopenharmony_ci } 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci if (!ret && buf_read && !direct) 24238c2ecf20Sopenharmony_ci memcpy(buf_read, this->data_buffer_dma, 24248c2ecf20Sopenharmony_ci gpmi_raw_len_to_len(this, buf_len)); 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci this->bch = false; 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ciout_pm: 24298c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(this->dev); 24308c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(this->dev); 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci return ret; 24338c2ecf20Sopenharmony_ci} 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_cistatic const struct nand_controller_ops gpmi_nand_controller_ops = { 24368c2ecf20Sopenharmony_ci .attach_chip = gpmi_nand_attach_chip, 24378c2ecf20Sopenharmony_ci .setup_interface = gpmi_setup_interface, 24388c2ecf20Sopenharmony_ci .exec_op = gpmi_nfc_exec_op, 24398c2ecf20Sopenharmony_ci}; 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_cistatic int gpmi_nand_init(struct gpmi_nand_data *this) 24428c2ecf20Sopenharmony_ci{ 24438c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 24448c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 24458c2ecf20Sopenharmony_ci int ret; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci /* init the MTD data structures */ 24488c2ecf20Sopenharmony_ci mtd->name = "gpmi-nand"; 24498c2ecf20Sopenharmony_ci mtd->dev.parent = this->dev; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */ 24528c2ecf20Sopenharmony_ci nand_set_controller_data(chip, this); 24538c2ecf20Sopenharmony_ci nand_set_flash_node(chip, this->pdev->dev.of_node); 24548c2ecf20Sopenharmony_ci chip->legacy.block_markbad = gpmi_block_markbad; 24558c2ecf20Sopenharmony_ci chip->badblock_pattern = &gpmi_bbt_descr; 24568c2ecf20Sopenharmony_ci chip->options |= NAND_NO_SUBPAGE_WRITE; 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ 24598c2ecf20Sopenharmony_ci this->swap_block_mark = !GPMI_IS_MX23(this); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci /* 24628c2ecf20Sopenharmony_ci * Allocate a temporary DMA buffer for reading ID in the 24638c2ecf20Sopenharmony_ci * nand_scan_ident(). 24648c2ecf20Sopenharmony_ci */ 24658c2ecf20Sopenharmony_ci this->bch_geometry.payload_size = 1024; 24668c2ecf20Sopenharmony_ci this->bch_geometry.auxiliary_size = 128; 24678c2ecf20Sopenharmony_ci ret = gpmi_alloc_dma_buffer(this); 24688c2ecf20Sopenharmony_ci if (ret) 24698c2ecf20Sopenharmony_ci return ret; 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci nand_controller_init(&this->base); 24728c2ecf20Sopenharmony_ci this->base.ops = &gpmi_nand_controller_ops; 24738c2ecf20Sopenharmony_ci chip->controller = &this->base; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1); 24768c2ecf20Sopenharmony_ci if (ret) 24778c2ecf20Sopenharmony_ci goto err_out; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci ret = nand_boot_init(this); 24808c2ecf20Sopenharmony_ci if (ret) 24818c2ecf20Sopenharmony_ci goto err_nand_cleanup; 24828c2ecf20Sopenharmony_ci ret = nand_create_bbt(chip); 24838c2ecf20Sopenharmony_ci if (ret) 24848c2ecf20Sopenharmony_ci goto err_nand_cleanup; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 24878c2ecf20Sopenharmony_ci if (ret) 24888c2ecf20Sopenharmony_ci goto err_nand_cleanup; 24898c2ecf20Sopenharmony_ci return 0; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_cierr_nand_cleanup: 24928c2ecf20Sopenharmony_ci nand_cleanup(chip); 24938c2ecf20Sopenharmony_cierr_out: 24948c2ecf20Sopenharmony_ci gpmi_free_dma_buffer(this); 24958c2ecf20Sopenharmony_ci return ret; 24968c2ecf20Sopenharmony_ci} 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_cistatic const struct of_device_id gpmi_nand_id_table[] = { 24998c2ecf20Sopenharmony_ci { 25008c2ecf20Sopenharmony_ci .compatible = "fsl,imx23-gpmi-nand", 25018c2ecf20Sopenharmony_ci .data = &gpmi_devdata_imx23, 25028c2ecf20Sopenharmony_ci }, { 25038c2ecf20Sopenharmony_ci .compatible = "fsl,imx28-gpmi-nand", 25048c2ecf20Sopenharmony_ci .data = &gpmi_devdata_imx28, 25058c2ecf20Sopenharmony_ci }, { 25068c2ecf20Sopenharmony_ci .compatible = "fsl,imx6q-gpmi-nand", 25078c2ecf20Sopenharmony_ci .data = &gpmi_devdata_imx6q, 25088c2ecf20Sopenharmony_ci }, { 25098c2ecf20Sopenharmony_ci .compatible = "fsl,imx6sx-gpmi-nand", 25108c2ecf20Sopenharmony_ci .data = &gpmi_devdata_imx6sx, 25118c2ecf20Sopenharmony_ci }, { 25128c2ecf20Sopenharmony_ci .compatible = "fsl,imx7d-gpmi-nand", 25138c2ecf20Sopenharmony_ci .data = &gpmi_devdata_imx7d, 25148c2ecf20Sopenharmony_ci }, {} 25158c2ecf20Sopenharmony_ci}; 25168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, gpmi_nand_id_table); 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_cistatic int gpmi_nand_probe(struct platform_device *pdev) 25198c2ecf20Sopenharmony_ci{ 25208c2ecf20Sopenharmony_ci struct gpmi_nand_data *this; 25218c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 25228c2ecf20Sopenharmony_ci int ret; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); 25258c2ecf20Sopenharmony_ci if (!this) 25268c2ecf20Sopenharmony_ci return -ENOMEM; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci of_id = of_match_device(gpmi_nand_id_table, &pdev->dev); 25298c2ecf20Sopenharmony_ci if (of_id) { 25308c2ecf20Sopenharmony_ci this->devdata = of_id->data; 25318c2ecf20Sopenharmony_ci } else { 25328c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to find the right device id.\n"); 25338c2ecf20Sopenharmony_ci return -ENODEV; 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, this); 25378c2ecf20Sopenharmony_ci this->pdev = pdev; 25388c2ecf20Sopenharmony_ci this->dev = &pdev->dev; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci ret = acquire_resources(this); 25418c2ecf20Sopenharmony_ci if (ret) 25428c2ecf20Sopenharmony_ci goto exit_acquire_resources; 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci ret = __gpmi_enable_clk(this, true); 25458c2ecf20Sopenharmony_ci if (ret) 25468c2ecf20Sopenharmony_ci goto exit_acquire_resources; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 500); 25498c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 25508c2ecf20Sopenharmony_ci pm_runtime_set_active(&pdev->dev); 25518c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 25528c2ecf20Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci ret = gpmi_init(this); 25558c2ecf20Sopenharmony_ci if (ret) 25568c2ecf20Sopenharmony_ci goto exit_nfc_init; 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci ret = gpmi_nand_init(this); 25598c2ecf20Sopenharmony_ci if (ret) 25608c2ecf20Sopenharmony_ci goto exit_nfc_init; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 25638c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci dev_info(this->dev, "driver registered.\n"); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci return 0; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ciexit_nfc_init: 25708c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 25718c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 25728c2ecf20Sopenharmony_ci release_resources(this); 25738c2ecf20Sopenharmony_ciexit_acquire_resources: 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci return ret; 25768c2ecf20Sopenharmony_ci} 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_cistatic int gpmi_nand_remove(struct platform_device *pdev) 25798c2ecf20Sopenharmony_ci{ 25808c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = platform_get_drvdata(pdev); 25818c2ecf20Sopenharmony_ci struct nand_chip *chip = &this->nand; 25828c2ecf20Sopenharmony_ci int ret; 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 25858c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 25888c2ecf20Sopenharmony_ci WARN_ON(ret); 25898c2ecf20Sopenharmony_ci nand_cleanup(chip); 25908c2ecf20Sopenharmony_ci gpmi_free_dma_buffer(this); 25918c2ecf20Sopenharmony_ci release_resources(this); 25928c2ecf20Sopenharmony_ci return 0; 25938c2ecf20Sopenharmony_ci} 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 25968c2ecf20Sopenharmony_cistatic int gpmi_pm_suspend(struct device *dev) 25978c2ecf20Sopenharmony_ci{ 25988c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci release_dma_channels(this); 26018c2ecf20Sopenharmony_ci return 0; 26028c2ecf20Sopenharmony_ci} 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_cistatic int gpmi_pm_resume(struct device *dev) 26058c2ecf20Sopenharmony_ci{ 26068c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 26078c2ecf20Sopenharmony_ci int ret; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci ret = acquire_dma_channels(this); 26108c2ecf20Sopenharmony_ci if (ret < 0) 26118c2ecf20Sopenharmony_ci return ret; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci /* re-init the GPMI registers */ 26148c2ecf20Sopenharmony_ci ret = gpmi_init(this); 26158c2ecf20Sopenharmony_ci if (ret) { 26168c2ecf20Sopenharmony_ci dev_err(this->dev, "Error setting GPMI : %d\n", ret); 26178c2ecf20Sopenharmony_ci return ret; 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci /* Set flag to get timing setup restored for next exec_op */ 26218c2ecf20Sopenharmony_ci if (this->hw.clk_rate) 26228c2ecf20Sopenharmony_ci this->hw.must_apply_timings = true; 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci /* re-init the BCH registers */ 26258c2ecf20Sopenharmony_ci ret = bch_set_geometry(this); 26268c2ecf20Sopenharmony_ci if (ret) { 26278c2ecf20Sopenharmony_ci dev_err(this->dev, "Error setting BCH : %d\n", ret); 26288c2ecf20Sopenharmony_ci return ret; 26298c2ecf20Sopenharmony_ci } 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci return 0; 26328c2ecf20Sopenharmony_ci} 26338c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_cistatic int __maybe_unused gpmi_runtime_suspend(struct device *dev) 26368c2ecf20Sopenharmony_ci{ 26378c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci return __gpmi_enable_clk(this, false); 26408c2ecf20Sopenharmony_ci} 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_cistatic int __maybe_unused gpmi_runtime_resume(struct device *dev) 26438c2ecf20Sopenharmony_ci{ 26448c2ecf20Sopenharmony_ci struct gpmi_nand_data *this = dev_get_drvdata(dev); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci return __gpmi_enable_clk(this, true); 26478c2ecf20Sopenharmony_ci} 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_cistatic const struct dev_pm_ops gpmi_pm_ops = { 26508c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(gpmi_pm_suspend, gpmi_pm_resume) 26518c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(gpmi_runtime_suspend, gpmi_runtime_resume, NULL) 26528c2ecf20Sopenharmony_ci}; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_cistatic struct platform_driver gpmi_nand_driver = { 26558c2ecf20Sopenharmony_ci .driver = { 26568c2ecf20Sopenharmony_ci .name = "gpmi-nand", 26578c2ecf20Sopenharmony_ci .pm = &gpmi_pm_ops, 26588c2ecf20Sopenharmony_ci .of_match_table = gpmi_nand_id_table, 26598c2ecf20Sopenharmony_ci }, 26608c2ecf20Sopenharmony_ci .probe = gpmi_nand_probe, 26618c2ecf20Sopenharmony_ci .remove = gpmi_nand_remove, 26628c2ecf20Sopenharmony_ci}; 26638c2ecf20Sopenharmony_cimodule_platform_driver(gpmi_nand_driver); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc."); 26668c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("i.MX GPMI NAND Flash Controller Driver"); 26678c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2668