162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NXP LPC32XX NAND SLC driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Kevin Wells <kevin.wells@nxp.com> 762306a36Sopenharmony_ci * Roland Stigge <stigge@antcom.de> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright © 2011 NXP Semiconductors 1062306a36Sopenharmony_ci * Copyright © 2012 Roland Stigge 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1762306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 1862306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1962306a36Sopenharmony_ci#include <linux/clk.h> 2062306a36Sopenharmony_ci#include <linux/err.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/io.h> 2362306a36Sopenharmony_ci#include <linux/mm.h> 2462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2562306a36Sopenharmony_ci#include <linux/dmaengine.h> 2662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2762306a36Sopenharmony_ci#include <linux/of.h> 2862306a36Sopenharmony_ci#include <linux/mtd/lpc32xx_slc.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define LPC32XX_MODNAME "lpc32xx-nand" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/********************************************************************** 3362306a36Sopenharmony_ci* SLC NAND controller register offsets 3462306a36Sopenharmony_ci**********************************************************************/ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SLC_DATA(x) (x + 0x000) 3762306a36Sopenharmony_ci#define SLC_ADDR(x) (x + 0x004) 3862306a36Sopenharmony_ci#define SLC_CMD(x) (x + 0x008) 3962306a36Sopenharmony_ci#define SLC_STOP(x) (x + 0x00C) 4062306a36Sopenharmony_ci#define SLC_CTRL(x) (x + 0x010) 4162306a36Sopenharmony_ci#define SLC_CFG(x) (x + 0x014) 4262306a36Sopenharmony_ci#define SLC_STAT(x) (x + 0x018) 4362306a36Sopenharmony_ci#define SLC_INT_STAT(x) (x + 0x01C) 4462306a36Sopenharmony_ci#define SLC_IEN(x) (x + 0x020) 4562306a36Sopenharmony_ci#define SLC_ISR(x) (x + 0x024) 4662306a36Sopenharmony_ci#define SLC_ICR(x) (x + 0x028) 4762306a36Sopenharmony_ci#define SLC_TAC(x) (x + 0x02C) 4862306a36Sopenharmony_ci#define SLC_TC(x) (x + 0x030) 4962306a36Sopenharmony_ci#define SLC_ECC(x) (x + 0x034) 5062306a36Sopenharmony_ci#define SLC_DMA_DATA(x) (x + 0x038) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/********************************************************************** 5362306a36Sopenharmony_ci* slc_ctrl register definitions 5462306a36Sopenharmony_ci**********************************************************************/ 5562306a36Sopenharmony_ci#define SLCCTRL_SW_RESET (1 << 2) /* Reset the NAND controller bit */ 5662306a36Sopenharmony_ci#define SLCCTRL_ECC_CLEAR (1 << 1) /* Reset ECC bit */ 5762306a36Sopenharmony_ci#define SLCCTRL_DMA_START (1 << 0) /* Start DMA channel bit */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/********************************************************************** 6062306a36Sopenharmony_ci* slc_cfg register definitions 6162306a36Sopenharmony_ci**********************************************************************/ 6262306a36Sopenharmony_ci#define SLCCFG_CE_LOW (1 << 5) /* Force CE low bit */ 6362306a36Sopenharmony_ci#define SLCCFG_DMA_ECC (1 << 4) /* Enable DMA ECC bit */ 6462306a36Sopenharmony_ci#define SLCCFG_ECC_EN (1 << 3) /* ECC enable bit */ 6562306a36Sopenharmony_ci#define SLCCFG_DMA_BURST (1 << 2) /* DMA burst bit */ 6662306a36Sopenharmony_ci#define SLCCFG_DMA_DIR (1 << 1) /* DMA write(0)/read(1) bit */ 6762306a36Sopenharmony_ci#define SLCCFG_WIDTH (1 << 0) /* External device width, 0=8bit */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/********************************************************************** 7062306a36Sopenharmony_ci* slc_stat register definitions 7162306a36Sopenharmony_ci**********************************************************************/ 7262306a36Sopenharmony_ci#define SLCSTAT_DMA_FIFO (1 << 2) /* DMA FIFO has data bit */ 7362306a36Sopenharmony_ci#define SLCSTAT_SLC_FIFO (1 << 1) /* SLC FIFO has data bit */ 7462306a36Sopenharmony_ci#define SLCSTAT_NAND_READY (1 << 0) /* NAND device is ready bit */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/********************************************************************** 7762306a36Sopenharmony_ci* slc_int_stat, slc_ien, slc_isr, and slc_icr register definitions 7862306a36Sopenharmony_ci**********************************************************************/ 7962306a36Sopenharmony_ci#define SLCSTAT_INT_TC (1 << 1) /* Transfer count bit */ 8062306a36Sopenharmony_ci#define SLCSTAT_INT_RDY_EN (1 << 0) /* Ready interrupt bit */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/********************************************************************** 8362306a36Sopenharmony_ci* slc_tac register definitions 8462306a36Sopenharmony_ci**********************************************************************/ 8562306a36Sopenharmony_ci/* Computation of clock cycles on basis of controller and device clock rates */ 8662306a36Sopenharmony_ci#define SLCTAC_CLOCKS(c, n, s) (min_t(u32, DIV_ROUND_UP(c, n) - 1, 0xF) << s) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Clock setting for RDY write sample wait time in 2*n clocks */ 8962306a36Sopenharmony_ci#define SLCTAC_WDR(n) (((n) & 0xF) << 28) 9062306a36Sopenharmony_ci/* Write pulse width in clock cycles, 1 to 16 clocks */ 9162306a36Sopenharmony_ci#define SLCTAC_WWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 24)) 9262306a36Sopenharmony_ci/* Write hold time of control and data signals, 1 to 16 clocks */ 9362306a36Sopenharmony_ci#define SLCTAC_WHOLD(c, n) (SLCTAC_CLOCKS(c, n, 20)) 9462306a36Sopenharmony_ci/* Write setup time of control and data signals, 1 to 16 clocks */ 9562306a36Sopenharmony_ci#define SLCTAC_WSETUP(c, n) (SLCTAC_CLOCKS(c, n, 16)) 9662306a36Sopenharmony_ci/* Clock setting for RDY read sample wait time in 2*n clocks */ 9762306a36Sopenharmony_ci#define SLCTAC_RDR(n) (((n) & 0xF) << 12) 9862306a36Sopenharmony_ci/* Read pulse width in clock cycles, 1 to 16 clocks */ 9962306a36Sopenharmony_ci#define SLCTAC_RWIDTH(c, n) (SLCTAC_CLOCKS(c, n, 8)) 10062306a36Sopenharmony_ci/* Read hold time of control and data signals, 1 to 16 clocks */ 10162306a36Sopenharmony_ci#define SLCTAC_RHOLD(c, n) (SLCTAC_CLOCKS(c, n, 4)) 10262306a36Sopenharmony_ci/* Read setup time of control and data signals, 1 to 16 clocks */ 10362306a36Sopenharmony_ci#define SLCTAC_RSETUP(c, n) (SLCTAC_CLOCKS(c, n, 0)) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/********************************************************************** 10662306a36Sopenharmony_ci* slc_ecc register definitions 10762306a36Sopenharmony_ci**********************************************************************/ 10862306a36Sopenharmony_ci/* ECC line party fetch macro */ 10962306a36Sopenharmony_ci#define SLCECC_TO_LINEPAR(n) (((n) >> 6) & 0x7FFF) 11062306a36Sopenharmony_ci#define SLCECC_TO_COLPAR(n) ((n) & 0x3F) 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * DMA requires storage space for the DMA local buffer and the hardware ECC 11462306a36Sopenharmony_ci * storage area. The DMA local buffer is only used if DMA mapping fails 11562306a36Sopenharmony_ci * during runtime. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci#define LPC32XX_DMA_DATA_SIZE 4096 11862306a36Sopenharmony_ci#define LPC32XX_ECC_SAVE_SIZE ((4096 / 256) * 4) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* Number of bytes used for ECC stored in NAND per 256 bytes */ 12162306a36Sopenharmony_ci#define LPC32XX_SLC_DEV_ECC_BYTES 3 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * If the NAND base clock frequency can't be fetched, this frequency will be 12562306a36Sopenharmony_ci * used instead as the base. This rate is used to setup the timing registers 12662306a36Sopenharmony_ci * used for NAND accesses. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci#define LPC32XX_DEF_BUS_RATE 133250000 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* Milliseconds for DMA FIFO timeout (unlikely anyway) */ 13162306a36Sopenharmony_ci#define LPC32XX_DMA_TIMEOUT 100 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* 13462306a36Sopenharmony_ci * NAND ECC Layout for small page NAND devices 13562306a36Sopenharmony_ci * Note: For large and huge page devices, the default layouts are used 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_cistatic int lpc32xx_ooblayout_ecc(struct mtd_info *mtd, int section, 13862306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci if (section) 14162306a36Sopenharmony_ci return -ERANGE; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci oobregion->length = 6; 14462306a36Sopenharmony_ci oobregion->offset = 10; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int lpc32xx_ooblayout_free(struct mtd_info *mtd, int section, 15062306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (section > 1) 15362306a36Sopenharmony_ci return -ERANGE; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (!section) { 15662306a36Sopenharmony_ci oobregion->offset = 0; 15762306a36Sopenharmony_ci oobregion->length = 4; 15862306a36Sopenharmony_ci } else { 15962306a36Sopenharmony_ci oobregion->offset = 6; 16062306a36Sopenharmony_ci oobregion->length = 4; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops lpc32xx_ooblayout_ops = { 16762306a36Sopenharmony_ci .ecc = lpc32xx_ooblayout_ecc, 16862306a36Sopenharmony_ci .free = lpc32xx_ooblayout_free, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic u8 bbt_pattern[] = {'B', 'b', 't', '0' }; 17262306a36Sopenharmony_cistatic u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* 17562306a36Sopenharmony_ci * Small page FLASH BBT descriptors, marker at offset 0, version at offset 6 17662306a36Sopenharmony_ci * Note: Large page devices used the default layout 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_smallpage_main_descr = { 17962306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 18062306a36Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 18162306a36Sopenharmony_ci .offs = 0, 18262306a36Sopenharmony_ci .len = 4, 18362306a36Sopenharmony_ci .veroffs = 6, 18462306a36Sopenharmony_ci .maxblocks = 4, 18562306a36Sopenharmony_ci .pattern = bbt_pattern 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct nand_bbt_descr bbt_smallpage_mirror_descr = { 18962306a36Sopenharmony_ci .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 19062306a36Sopenharmony_ci | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, 19162306a36Sopenharmony_ci .offs = 0, 19262306a36Sopenharmony_ci .len = 4, 19362306a36Sopenharmony_ci .veroffs = 6, 19462306a36Sopenharmony_ci .maxblocks = 4, 19562306a36Sopenharmony_ci .pattern = mirror_pattern 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* 19962306a36Sopenharmony_ci * NAND platform configuration structure 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistruct lpc32xx_nand_cfg_slc { 20262306a36Sopenharmony_ci uint32_t wdr_clks; 20362306a36Sopenharmony_ci uint32_t wwidth; 20462306a36Sopenharmony_ci uint32_t whold; 20562306a36Sopenharmony_ci uint32_t wsetup; 20662306a36Sopenharmony_ci uint32_t rdr_clks; 20762306a36Sopenharmony_ci uint32_t rwidth; 20862306a36Sopenharmony_ci uint32_t rhold; 20962306a36Sopenharmony_ci uint32_t rsetup; 21062306a36Sopenharmony_ci struct mtd_partition *parts; 21162306a36Sopenharmony_ci unsigned num_parts; 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistruct lpc32xx_nand_host { 21562306a36Sopenharmony_ci struct nand_chip nand_chip; 21662306a36Sopenharmony_ci struct lpc32xx_slc_platform_data *pdata; 21762306a36Sopenharmony_ci struct clk *clk; 21862306a36Sopenharmony_ci struct gpio_desc *wp_gpio; 21962306a36Sopenharmony_ci void __iomem *io_base; 22062306a36Sopenharmony_ci struct lpc32xx_nand_cfg_slc *ncfg; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci struct completion comp; 22362306a36Sopenharmony_ci struct dma_chan *dma_chan; 22462306a36Sopenharmony_ci uint32_t dma_buf_len; 22562306a36Sopenharmony_ci struct dma_slave_config dma_slave_config; 22662306a36Sopenharmony_ci struct scatterlist sgl; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * DMA and CPU addresses of ECC work area and data buffer 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci uint32_t *ecc_buf; 23262306a36Sopenharmony_ci uint8_t *data_buf; 23362306a36Sopenharmony_ci dma_addr_t io_base_dma; 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void lpc32xx_nand_setup(struct lpc32xx_nand_host *host) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci uint32_t clkrate, tmp; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Reset SLC controller */ 24162306a36Sopenharmony_ci writel(SLCCTRL_SW_RESET, SLC_CTRL(host->io_base)); 24262306a36Sopenharmony_ci udelay(1000); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Basic setup */ 24562306a36Sopenharmony_ci writel(0, SLC_CFG(host->io_base)); 24662306a36Sopenharmony_ci writel(0, SLC_IEN(host->io_base)); 24762306a36Sopenharmony_ci writel((SLCSTAT_INT_TC | SLCSTAT_INT_RDY_EN), 24862306a36Sopenharmony_ci SLC_ICR(host->io_base)); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Get base clock for SLC block */ 25162306a36Sopenharmony_ci clkrate = clk_get_rate(host->clk); 25262306a36Sopenharmony_ci if (clkrate == 0) 25362306a36Sopenharmony_ci clkrate = LPC32XX_DEF_BUS_RATE; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Compute clock setup values */ 25662306a36Sopenharmony_ci tmp = SLCTAC_WDR(host->ncfg->wdr_clks) | 25762306a36Sopenharmony_ci SLCTAC_WWIDTH(clkrate, host->ncfg->wwidth) | 25862306a36Sopenharmony_ci SLCTAC_WHOLD(clkrate, host->ncfg->whold) | 25962306a36Sopenharmony_ci SLCTAC_WSETUP(clkrate, host->ncfg->wsetup) | 26062306a36Sopenharmony_ci SLCTAC_RDR(host->ncfg->rdr_clks) | 26162306a36Sopenharmony_ci SLCTAC_RWIDTH(clkrate, host->ncfg->rwidth) | 26262306a36Sopenharmony_ci SLCTAC_RHOLD(clkrate, host->ncfg->rhold) | 26362306a36Sopenharmony_ci SLCTAC_RSETUP(clkrate, host->ncfg->rsetup); 26462306a36Sopenharmony_ci writel(tmp, SLC_TAC(host->io_base)); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * Hardware specific access to control lines 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic void lpc32xx_nand_cmd_ctrl(struct nand_chip *chip, int cmd, 27162306a36Sopenharmony_ci unsigned int ctrl) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci uint32_t tmp; 27462306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Does CE state need to be changed? */ 27762306a36Sopenharmony_ci tmp = readl(SLC_CFG(host->io_base)); 27862306a36Sopenharmony_ci if (ctrl & NAND_NCE) 27962306a36Sopenharmony_ci tmp |= SLCCFG_CE_LOW; 28062306a36Sopenharmony_ci else 28162306a36Sopenharmony_ci tmp &= ~SLCCFG_CE_LOW; 28262306a36Sopenharmony_ci writel(tmp, SLC_CFG(host->io_base)); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (cmd != NAND_CMD_NONE) { 28562306a36Sopenharmony_ci if (ctrl & NAND_CLE) 28662306a36Sopenharmony_ci writel(cmd, SLC_CMD(host->io_base)); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci writel(cmd, SLC_ADDR(host->io_base)); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * Read the Device Ready pin 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_cistatic int lpc32xx_nand_device_ready(struct nand_chip *chip) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 29862306a36Sopenharmony_ci int rdy = 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if ((readl(SLC_STAT(host->io_base)) & SLCSTAT_NAND_READY) != 0) 30162306a36Sopenharmony_ci rdy = 1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return rdy; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* 30762306a36Sopenharmony_ci * Enable NAND write protect 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_cistatic void lpc32xx_wp_enable(struct lpc32xx_nand_host *host) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci if (host->wp_gpio) 31262306a36Sopenharmony_ci gpiod_set_value_cansleep(host->wp_gpio, 1); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* 31662306a36Sopenharmony_ci * Disable NAND write protect 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistatic void lpc32xx_wp_disable(struct lpc32xx_nand_host *host) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci if (host->wp_gpio) 32162306a36Sopenharmony_ci gpiod_set_value_cansleep(host->wp_gpio, 0); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* 32562306a36Sopenharmony_ci * Prepares SLC for transfers with H/W ECC enabled 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_cistatic void lpc32xx_nand_ecc_enable(struct nand_chip *chip, int mode) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci /* Hardware ECC is enabled automatically in hardware as needed */ 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci/* 33362306a36Sopenharmony_ci * Calculates the ECC for the data 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_cistatic int lpc32xx_nand_ecc_calculate(struct nand_chip *chip, 33662306a36Sopenharmony_ci const unsigned char *buf, 33762306a36Sopenharmony_ci unsigned char *code) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * ECC is calculated automatically in hardware during syndrome read 34162306a36Sopenharmony_ci * and write operations, so it doesn't need to be calculated here. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* 34762306a36Sopenharmony_ci * Read a single byte from NAND device 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic uint8_t lpc32xx_nand_read_byte(struct nand_chip *chip) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return (uint8_t)readl(SLC_DATA(host->io_base)); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* 35762306a36Sopenharmony_ci * Simple device read without ECC 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_cistatic void lpc32xx_nand_read_buf(struct nand_chip *chip, u_char *buf, int len) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Direct device read with no ECC */ 36462306a36Sopenharmony_ci while (len-- > 0) 36562306a36Sopenharmony_ci *buf++ = (uint8_t)readl(SLC_DATA(host->io_base)); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/* 36962306a36Sopenharmony_ci * Simple device write without ECC 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_cistatic void lpc32xx_nand_write_buf(struct nand_chip *chip, const uint8_t *buf, 37262306a36Sopenharmony_ci int len) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Direct device write with no ECC */ 37762306a36Sopenharmony_ci while (len-- > 0) 37862306a36Sopenharmony_ci writel((uint32_t)*buf++, SLC_DATA(host->io_base)); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* 38262306a36Sopenharmony_ci * Read the OOB data from the device without ECC using FIFO method 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_cistatic int lpc32xx_nand_read_oob_syndrome(struct nand_chip *chip, int page) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci/* 39262306a36Sopenharmony_ci * Write the OOB data to the device without ECC using FIFO method 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic int lpc32xx_nand_write_oob_syndrome(struct nand_chip *chip, int page) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return nand_prog_page_op(chip, page, mtd->writesize, chip->oob_poi, 39962306a36Sopenharmony_ci mtd->oobsize); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/* 40362306a36Sopenharmony_ci * Fills in the ECC fields in the OOB buffer with the hardware generated ECC 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_cistatic void lpc32xx_slc_ecc_copy(uint8_t *spare, const uint32_t *ecc, int count) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci int i; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci for (i = 0; i < (count * 3); i += 3) { 41062306a36Sopenharmony_ci uint32_t ce = ecc[i / 3]; 41162306a36Sopenharmony_ci ce = ~(ce << 2) & 0xFFFFFF; 41262306a36Sopenharmony_ci spare[i + 2] = (uint8_t)(ce & 0xFF); 41362306a36Sopenharmony_ci ce >>= 8; 41462306a36Sopenharmony_ci spare[i + 1] = (uint8_t)(ce & 0xFF); 41562306a36Sopenharmony_ci ce >>= 8; 41662306a36Sopenharmony_ci spare[i] = (uint8_t)(ce & 0xFF); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void lpc32xx_dma_complete_func(void *completion) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci complete(completion); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma, 42662306a36Sopenharmony_ci void *mem, int len, enum dma_transfer_direction dir) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 42962306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 43062306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 43162306a36Sopenharmony_ci int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; 43262306a36Sopenharmony_ci int res; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci host->dma_slave_config.direction = dir; 43562306a36Sopenharmony_ci host->dma_slave_config.src_addr = dma; 43662306a36Sopenharmony_ci host->dma_slave_config.dst_addr = dma; 43762306a36Sopenharmony_ci host->dma_slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 43862306a36Sopenharmony_ci host->dma_slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 43962306a36Sopenharmony_ci host->dma_slave_config.src_maxburst = 4; 44062306a36Sopenharmony_ci host->dma_slave_config.dst_maxburst = 4; 44162306a36Sopenharmony_ci /* DMA controller does flow control: */ 44262306a36Sopenharmony_ci host->dma_slave_config.device_fc = false; 44362306a36Sopenharmony_ci if (dmaengine_slave_config(host->dma_chan, &host->dma_slave_config)) { 44462306a36Sopenharmony_ci dev_err(mtd->dev.parent, "Failed to setup DMA slave\n"); 44562306a36Sopenharmony_ci return -ENXIO; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci sg_init_one(&host->sgl, mem, len); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1, 45162306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 45262306a36Sopenharmony_ci if (res != 1) { 45362306a36Sopenharmony_ci dev_err(mtd->dev.parent, "Failed to map sg list\n"); 45462306a36Sopenharmony_ci return -ENXIO; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(host->dma_chan, &host->sgl, 1, dir, 45762306a36Sopenharmony_ci flags); 45862306a36Sopenharmony_ci if (!desc) { 45962306a36Sopenharmony_ci dev_err(mtd->dev.parent, "Failed to prepare slave sg\n"); 46062306a36Sopenharmony_ci goto out1; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci init_completion(&host->comp); 46462306a36Sopenharmony_ci desc->callback = lpc32xx_dma_complete_func; 46562306a36Sopenharmony_ci desc->callback_param = &host->comp; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci dmaengine_submit(desc); 46862306a36Sopenharmony_ci dma_async_issue_pending(host->dma_chan); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci wait_for_completion_timeout(&host->comp, msecs_to_jiffies(1000)); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, 47362306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ciout1: 47762306a36Sopenharmony_ci dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1, 47862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 47962306a36Sopenharmony_ci return -ENXIO; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* 48362306a36Sopenharmony_ci * DMA read/write transfers with ECC support 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_cistatic int lpc32xx_xfer(struct mtd_info *mtd, uint8_t *buf, int eccsubpages, 48662306a36Sopenharmony_ci int read) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 48962306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 49062306a36Sopenharmony_ci int i, status = 0; 49162306a36Sopenharmony_ci unsigned long timeout; 49262306a36Sopenharmony_ci int res; 49362306a36Sopenharmony_ci enum dma_transfer_direction dir = 49462306a36Sopenharmony_ci read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; 49562306a36Sopenharmony_ci uint8_t *dma_buf; 49662306a36Sopenharmony_ci bool dma_mapped; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if ((void *)buf <= high_memory) { 49962306a36Sopenharmony_ci dma_buf = buf; 50062306a36Sopenharmony_ci dma_mapped = true; 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci dma_buf = host->data_buf; 50362306a36Sopenharmony_ci dma_mapped = false; 50462306a36Sopenharmony_ci if (!read) 50562306a36Sopenharmony_ci memcpy(host->data_buf, buf, mtd->writesize); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (read) { 50962306a36Sopenharmony_ci writel(readl(SLC_CFG(host->io_base)) | 51062306a36Sopenharmony_ci SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | 51162306a36Sopenharmony_ci SLCCFG_DMA_BURST, SLC_CFG(host->io_base)); 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci writel((readl(SLC_CFG(host->io_base)) | 51462306a36Sopenharmony_ci SLCCFG_ECC_EN | SLCCFG_DMA_ECC | SLCCFG_DMA_BURST) & 51562306a36Sopenharmony_ci ~SLCCFG_DMA_DIR, 51662306a36Sopenharmony_ci SLC_CFG(host->io_base)); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Clear initial ECC */ 52062306a36Sopenharmony_ci writel(SLCCTRL_ECC_CLEAR, SLC_CTRL(host->io_base)); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Transfer size is data area only */ 52362306a36Sopenharmony_ci writel(mtd->writesize, SLC_TC(host->io_base)); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Start transfer in the NAND controller */ 52662306a36Sopenharmony_ci writel(readl(SLC_CTRL(host->io_base)) | SLCCTRL_DMA_START, 52762306a36Sopenharmony_ci SLC_CTRL(host->io_base)); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci for (i = 0; i < chip->ecc.steps; i++) { 53062306a36Sopenharmony_ci /* Data */ 53162306a36Sopenharmony_ci res = lpc32xx_xmit_dma(mtd, SLC_DMA_DATA(host->io_base_dma), 53262306a36Sopenharmony_ci dma_buf + i * chip->ecc.size, 53362306a36Sopenharmony_ci mtd->writesize / chip->ecc.steps, dir); 53462306a36Sopenharmony_ci if (res) 53562306a36Sopenharmony_ci return res; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Always _read_ ECC */ 53862306a36Sopenharmony_ci if (i == chip->ecc.steps - 1) 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci if (!read) /* ECC availability delayed on write */ 54162306a36Sopenharmony_ci udelay(10); 54262306a36Sopenharmony_ci res = lpc32xx_xmit_dma(mtd, SLC_ECC(host->io_base_dma), 54362306a36Sopenharmony_ci &host->ecc_buf[i], 4, DMA_DEV_TO_MEM); 54462306a36Sopenharmony_ci if (res) 54562306a36Sopenharmony_ci return res; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* 54962306a36Sopenharmony_ci * According to NXP, the DMA can be finished here, but the NAND 55062306a36Sopenharmony_ci * controller may still have buffered data. After porting to using the 55162306a36Sopenharmony_ci * dmaengine DMA driver (amba-pl080), the condition (DMA_FIFO empty) 55262306a36Sopenharmony_ci * appears to be always true, according to tests. Keeping the check for 55362306a36Sopenharmony_ci * safety reasons for now. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) { 55662306a36Sopenharmony_ci dev_warn(mtd->dev.parent, "FIFO not empty!\n"); 55762306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(LPC32XX_DMA_TIMEOUT); 55862306a36Sopenharmony_ci while ((readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO) && 55962306a36Sopenharmony_ci time_before(jiffies, timeout)) 56062306a36Sopenharmony_ci cpu_relax(); 56162306a36Sopenharmony_ci if (!time_before(jiffies, timeout)) { 56262306a36Sopenharmony_ci dev_err(mtd->dev.parent, "FIFO held data too long\n"); 56362306a36Sopenharmony_ci status = -EIO; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Read last calculated ECC value */ 56862306a36Sopenharmony_ci if (!read) 56962306a36Sopenharmony_ci udelay(10); 57062306a36Sopenharmony_ci host->ecc_buf[chip->ecc.steps - 1] = 57162306a36Sopenharmony_ci readl(SLC_ECC(host->io_base)); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Flush DMA */ 57462306a36Sopenharmony_ci dmaengine_terminate_all(host->dma_chan); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (readl(SLC_STAT(host->io_base)) & SLCSTAT_DMA_FIFO || 57762306a36Sopenharmony_ci readl(SLC_TC(host->io_base))) { 57862306a36Sopenharmony_ci /* Something is left in the FIFO, something is wrong */ 57962306a36Sopenharmony_ci dev_err(mtd->dev.parent, "DMA FIFO failure\n"); 58062306a36Sopenharmony_ci status = -EIO; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Stop DMA & HW ECC */ 58462306a36Sopenharmony_ci writel(readl(SLC_CTRL(host->io_base)) & ~SLCCTRL_DMA_START, 58562306a36Sopenharmony_ci SLC_CTRL(host->io_base)); 58662306a36Sopenharmony_ci writel(readl(SLC_CFG(host->io_base)) & 58762306a36Sopenharmony_ci ~(SLCCFG_DMA_DIR | SLCCFG_ECC_EN | SLCCFG_DMA_ECC | 58862306a36Sopenharmony_ci SLCCFG_DMA_BURST), SLC_CFG(host->io_base)); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!dma_mapped && read) 59162306a36Sopenharmony_ci memcpy(buf, host->data_buf, mtd->writesize); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return status; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci/* 59762306a36Sopenharmony_ci * Read the data and OOB data from the device, use ECC correction with the 59862306a36Sopenharmony_ci * data, disable ECC for the OOB data 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_cistatic int lpc32xx_nand_read_page_syndrome(struct nand_chip *chip, uint8_t *buf, 60162306a36Sopenharmony_ci int oob_required, int page) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 60462306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 60562306a36Sopenharmony_ci struct mtd_oob_region oobregion = { }; 60662306a36Sopenharmony_ci int stat, i, status, error; 60762306a36Sopenharmony_ci uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE]; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* Issue read command */ 61062306a36Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Read data and oob, calculate ECC */ 61362306a36Sopenharmony_ci status = lpc32xx_xfer(mtd, buf, chip->ecc.steps, 1); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Get OOB data */ 61662306a36Sopenharmony_ci chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* Convert to stored ECC format */ 61962306a36Sopenharmony_ci lpc32xx_slc_ecc_copy(tmpecc, (uint32_t *) host->ecc_buf, chip->ecc.steps); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Pointer to ECC data retrieved from NAND spare area */ 62262306a36Sopenharmony_ci error = mtd_ooblayout_ecc(mtd, 0, &oobregion); 62362306a36Sopenharmony_ci if (error) 62462306a36Sopenharmony_ci return error; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci oobecc = chip->oob_poi + oobregion.offset; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci for (i = 0; i < chip->ecc.steps; i++) { 62962306a36Sopenharmony_ci stat = chip->ecc.correct(chip, buf, oobecc, 63062306a36Sopenharmony_ci &tmpecc[i * chip->ecc.bytes]); 63162306a36Sopenharmony_ci if (stat < 0) 63262306a36Sopenharmony_ci mtd->ecc_stats.failed++; 63362306a36Sopenharmony_ci else 63462306a36Sopenharmony_ci mtd->ecc_stats.corrected += stat; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci buf += chip->ecc.size; 63762306a36Sopenharmony_ci oobecc += chip->ecc.bytes; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return status; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* 64462306a36Sopenharmony_ci * Read the data and OOB data from the device, no ECC correction with the 64562306a36Sopenharmony_ci * data or OOB data 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_cistatic int lpc32xx_nand_read_page_raw_syndrome(struct nand_chip *chip, 64862306a36Sopenharmony_ci uint8_t *buf, int oob_required, 64962306a36Sopenharmony_ci int page) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Issue read command */ 65462306a36Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Raw reads can just use the FIFO interface */ 65762306a36Sopenharmony_ci chip->legacy.read_buf(chip, buf, chip->ecc.size * chip->ecc.steps); 65862306a36Sopenharmony_ci chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* 66462306a36Sopenharmony_ci * Write the data and OOB data to the device, use ECC with the data, 66562306a36Sopenharmony_ci * disable ECC for the OOB data 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_cistatic int lpc32xx_nand_write_page_syndrome(struct nand_chip *chip, 66862306a36Sopenharmony_ci const uint8_t *buf, 66962306a36Sopenharmony_ci int oob_required, int page) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 67262306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 67362306a36Sopenharmony_ci struct mtd_oob_region oobregion = { }; 67462306a36Sopenharmony_ci uint8_t *pb; 67562306a36Sopenharmony_ci int error; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Write data, calculate ECC on outbound data */ 68062306a36Sopenharmony_ci error = lpc32xx_xfer(mtd, (uint8_t *)buf, chip->ecc.steps, 0); 68162306a36Sopenharmony_ci if (error) 68262306a36Sopenharmony_ci return error; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * The calculated ECC needs some manual work done to it before 68662306a36Sopenharmony_ci * committing it to NAND. Process the calculated ECC and place 68762306a36Sopenharmony_ci * the resultant values directly into the OOB buffer. */ 68862306a36Sopenharmony_ci error = mtd_ooblayout_ecc(mtd, 0, &oobregion); 68962306a36Sopenharmony_ci if (error) 69062306a36Sopenharmony_ci return error; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci pb = chip->oob_poi + oobregion.offset; 69362306a36Sopenharmony_ci lpc32xx_slc_ecc_copy(pb, (uint32_t *)host->ecc_buf, chip->ecc.steps); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* Write ECC data to device */ 69662306a36Sopenharmony_ci chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/* 70262306a36Sopenharmony_ci * Write the data and OOB data to the device, no ECC correction with the 70362306a36Sopenharmony_ci * data or OOB data 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic int lpc32xx_nand_write_page_raw_syndrome(struct nand_chip *chip, 70662306a36Sopenharmony_ci const uint8_t *buf, 70762306a36Sopenharmony_ci int oob_required, int page) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Raw writes can just use the FIFO interface */ 71262306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, buf, 71362306a36Sopenharmony_ci chip->ecc.size * chip->ecc.steps); 71462306a36Sopenharmony_ci chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); 72262306a36Sopenharmony_ci dma_cap_mask_t mask; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (!host->pdata || !host->pdata->dma_filter) { 72562306a36Sopenharmony_ci dev_err(mtd->dev.parent, "no DMA platform data\n"); 72662306a36Sopenharmony_ci return -ENOENT; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci dma_cap_zero(mask); 73062306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 73162306a36Sopenharmony_ci host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, 73262306a36Sopenharmony_ci "nand-slc"); 73362306a36Sopenharmony_ci if (!host->dma_chan) { 73462306a36Sopenharmony_ci dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); 73562306a36Sopenharmony_ci return -EBUSY; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic struct lpc32xx_nand_cfg_slc *lpc32xx_parse_dt(struct device *dev) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct lpc32xx_nand_cfg_slc *ncfg; 74462306a36Sopenharmony_ci struct device_node *np = dev->of_node; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci ncfg = devm_kzalloc(dev, sizeof(*ncfg), GFP_KERNEL); 74762306a36Sopenharmony_ci if (!ncfg) 74862306a36Sopenharmony_ci return NULL; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci of_property_read_u32(np, "nxp,wdr-clks", &ncfg->wdr_clks); 75162306a36Sopenharmony_ci of_property_read_u32(np, "nxp,wwidth", &ncfg->wwidth); 75262306a36Sopenharmony_ci of_property_read_u32(np, "nxp,whold", &ncfg->whold); 75362306a36Sopenharmony_ci of_property_read_u32(np, "nxp,wsetup", &ncfg->wsetup); 75462306a36Sopenharmony_ci of_property_read_u32(np, "nxp,rdr-clks", &ncfg->rdr_clks); 75562306a36Sopenharmony_ci of_property_read_u32(np, "nxp,rwidth", &ncfg->rwidth); 75662306a36Sopenharmony_ci of_property_read_u32(np, "nxp,rhold", &ncfg->rhold); 75762306a36Sopenharmony_ci of_property_read_u32(np, "nxp,rsetup", &ncfg->rsetup); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (!ncfg->wdr_clks || !ncfg->wwidth || !ncfg->whold || 76062306a36Sopenharmony_ci !ncfg->wsetup || !ncfg->rdr_clks || !ncfg->rwidth || 76162306a36Sopenharmony_ci !ncfg->rhold || !ncfg->rsetup) { 76262306a36Sopenharmony_ci dev_err(dev, "chip parameters not specified correctly\n"); 76362306a36Sopenharmony_ci return NULL; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return ncfg; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic int lpc32xx_nand_attach_chip(struct nand_chip *chip) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 77262306a36Sopenharmony_ci struct lpc32xx_nand_host *host = nand_get_controller_data(chip); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* OOB and ECC CPU and DMA work areas */ 77862306a36Sopenharmony_ci host->ecc_buf = (uint32_t *)(host->data_buf + LPC32XX_DMA_DATA_SIZE); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* 78162306a36Sopenharmony_ci * Small page FLASH has a unique OOB layout, but large and huge 78262306a36Sopenharmony_ci * page FLASH use the standard layout. Small page FLASH uses a 78362306a36Sopenharmony_ci * custom BBT marker layout. 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci if (mtd->writesize <= 512) 78662306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &lpc32xx_ooblayout_ops); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; 78962306a36Sopenharmony_ci /* These sizes remain the same regardless of page size */ 79062306a36Sopenharmony_ci chip->ecc.size = 256; 79162306a36Sopenharmony_ci chip->ecc.strength = 1; 79262306a36Sopenharmony_ci chip->ecc.bytes = LPC32XX_SLC_DEV_ECC_BYTES; 79362306a36Sopenharmony_ci chip->ecc.prepad = 0; 79462306a36Sopenharmony_ci chip->ecc.postpad = 0; 79562306a36Sopenharmony_ci chip->ecc.read_page_raw = lpc32xx_nand_read_page_raw_syndrome; 79662306a36Sopenharmony_ci chip->ecc.read_page = lpc32xx_nand_read_page_syndrome; 79762306a36Sopenharmony_ci chip->ecc.write_page_raw = lpc32xx_nand_write_page_raw_syndrome; 79862306a36Sopenharmony_ci chip->ecc.write_page = lpc32xx_nand_write_page_syndrome; 79962306a36Sopenharmony_ci chip->ecc.write_oob = lpc32xx_nand_write_oob_syndrome; 80062306a36Sopenharmony_ci chip->ecc.read_oob = lpc32xx_nand_read_oob_syndrome; 80162306a36Sopenharmony_ci chip->ecc.calculate = lpc32xx_nand_ecc_calculate; 80262306a36Sopenharmony_ci chip->ecc.correct = rawnand_sw_hamming_correct; 80362306a36Sopenharmony_ci chip->ecc.hwctl = lpc32xx_nand_ecc_enable; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* 80662306a36Sopenharmony_ci * Use a custom BBT marker setup for small page FLASH that 80762306a36Sopenharmony_ci * won't interfere with the ECC layout. Large and huge page 80862306a36Sopenharmony_ci * FLASH use the standard layout. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ci if ((chip->bbt_options & NAND_BBT_USE_FLASH) && 81162306a36Sopenharmony_ci mtd->writesize <= 512) { 81262306a36Sopenharmony_ci chip->bbt_td = &bbt_smallpage_main_descr; 81362306a36Sopenharmony_ci chip->bbt_md = &bbt_smallpage_mirror_descr; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return 0; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic const struct nand_controller_ops lpc32xx_nand_controller_ops = { 82062306a36Sopenharmony_ci .attach_chip = lpc32xx_nand_attach_chip, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci/* 82462306a36Sopenharmony_ci * Probe for NAND controller 82562306a36Sopenharmony_ci */ 82662306a36Sopenharmony_cistatic int lpc32xx_nand_probe(struct platform_device *pdev) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct lpc32xx_nand_host *host; 82962306a36Sopenharmony_ci struct mtd_info *mtd; 83062306a36Sopenharmony_ci struct nand_chip *chip; 83162306a36Sopenharmony_ci struct resource *rc; 83262306a36Sopenharmony_ci int res; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* Allocate memory for the device structure (and zero it) */ 83562306a36Sopenharmony_ci host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 83662306a36Sopenharmony_ci if (!host) 83762306a36Sopenharmony_ci return -ENOMEM; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &rc); 84062306a36Sopenharmony_ci if (IS_ERR(host->io_base)) 84162306a36Sopenharmony_ci return PTR_ERR(host->io_base); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci host->io_base_dma = rc->start; 84462306a36Sopenharmony_ci if (pdev->dev.of_node) 84562306a36Sopenharmony_ci host->ncfg = lpc32xx_parse_dt(&pdev->dev); 84662306a36Sopenharmony_ci if (!host->ncfg) { 84762306a36Sopenharmony_ci dev_err(&pdev->dev, 84862306a36Sopenharmony_ci "Missing or bad NAND config from device tree\n"); 84962306a36Sopenharmony_ci return -ENOENT; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Start with WP disabled, if available */ 85362306a36Sopenharmony_ci host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW); 85462306a36Sopenharmony_ci res = PTR_ERR_OR_ZERO(host->wp_gpio); 85562306a36Sopenharmony_ci if (res) { 85662306a36Sopenharmony_ci if (res != -EPROBE_DEFER) 85762306a36Sopenharmony_ci dev_err(&pdev->dev, "WP GPIO is not available: %d\n", 85862306a36Sopenharmony_ci res); 85962306a36Sopenharmony_ci return res; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci gpiod_set_consumer_name(host->wp_gpio, "NAND WP"); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci host->pdata = dev_get_platdata(&pdev->dev); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci chip = &host->nand_chip; 86762306a36Sopenharmony_ci mtd = nand_to_mtd(chip); 86862306a36Sopenharmony_ci nand_set_controller_data(chip, host); 86962306a36Sopenharmony_ci nand_set_flash_node(chip, pdev->dev.of_node); 87062306a36Sopenharmony_ci mtd->owner = THIS_MODULE; 87162306a36Sopenharmony_ci mtd->dev.parent = &pdev->dev; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci /* Get NAND clock */ 87462306a36Sopenharmony_ci host->clk = devm_clk_get_enabled(&pdev->dev, NULL); 87562306a36Sopenharmony_ci if (IS_ERR(host->clk)) { 87662306a36Sopenharmony_ci dev_err(&pdev->dev, "Clock failure\n"); 87762306a36Sopenharmony_ci res = -ENOENT; 87862306a36Sopenharmony_ci goto enable_wp; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Set NAND IO addresses and command/ready functions */ 88262306a36Sopenharmony_ci chip->legacy.IO_ADDR_R = SLC_DATA(host->io_base); 88362306a36Sopenharmony_ci chip->legacy.IO_ADDR_W = SLC_DATA(host->io_base); 88462306a36Sopenharmony_ci chip->legacy.cmd_ctrl = lpc32xx_nand_cmd_ctrl; 88562306a36Sopenharmony_ci chip->legacy.dev_ready = lpc32xx_nand_device_ready; 88662306a36Sopenharmony_ci chip->legacy.chip_delay = 20; /* 20us command delay time */ 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci /* Init NAND controller */ 88962306a36Sopenharmony_ci lpc32xx_nand_setup(host); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* NAND callbacks for LPC32xx SLC hardware */ 89462306a36Sopenharmony_ci chip->legacy.read_byte = lpc32xx_nand_read_byte; 89562306a36Sopenharmony_ci chip->legacy.read_buf = lpc32xx_nand_read_buf; 89662306a36Sopenharmony_ci chip->legacy.write_buf = lpc32xx_nand_write_buf; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* 89962306a36Sopenharmony_ci * Allocate a large enough buffer for a single huge page plus 90062306a36Sopenharmony_ci * extra space for the spare area and ECC storage area 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_ci host->dma_buf_len = LPC32XX_DMA_DATA_SIZE + LPC32XX_ECC_SAVE_SIZE; 90362306a36Sopenharmony_ci host->data_buf = devm_kzalloc(&pdev->dev, host->dma_buf_len, 90462306a36Sopenharmony_ci GFP_KERNEL); 90562306a36Sopenharmony_ci if (host->data_buf == NULL) { 90662306a36Sopenharmony_ci res = -ENOMEM; 90762306a36Sopenharmony_ci goto enable_wp; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci res = lpc32xx_nand_dma_setup(host); 91162306a36Sopenharmony_ci if (res) { 91262306a36Sopenharmony_ci res = -EIO; 91362306a36Sopenharmony_ci goto enable_wp; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Find NAND device */ 91762306a36Sopenharmony_ci chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops; 91862306a36Sopenharmony_ci res = nand_scan(chip, 1); 91962306a36Sopenharmony_ci if (res) 92062306a36Sopenharmony_ci goto release_dma; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci mtd->name = "nxp_lpc3220_slc"; 92362306a36Sopenharmony_ci res = mtd_device_register(mtd, host->ncfg->parts, 92462306a36Sopenharmony_ci host->ncfg->num_parts); 92562306a36Sopenharmony_ci if (res) 92662306a36Sopenharmony_ci goto cleanup_nand; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cicleanup_nand: 93162306a36Sopenharmony_ci nand_cleanup(chip); 93262306a36Sopenharmony_cirelease_dma: 93362306a36Sopenharmony_ci dma_release_channel(host->dma_chan); 93462306a36Sopenharmony_cienable_wp: 93562306a36Sopenharmony_ci lpc32xx_wp_enable(host); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return res; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci/* 94162306a36Sopenharmony_ci * Remove NAND device. 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic void lpc32xx_nand_remove(struct platform_device *pdev) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci uint32_t tmp; 94662306a36Sopenharmony_ci struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); 94762306a36Sopenharmony_ci struct nand_chip *chip = &host->nand_chip; 94862306a36Sopenharmony_ci int ret; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 95162306a36Sopenharmony_ci WARN_ON(ret); 95262306a36Sopenharmony_ci nand_cleanup(chip); 95362306a36Sopenharmony_ci dma_release_channel(host->dma_chan); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* Force CE high */ 95662306a36Sopenharmony_ci tmp = readl(SLC_CTRL(host->io_base)); 95762306a36Sopenharmony_ci tmp &= ~SLCCFG_CE_LOW; 95862306a36Sopenharmony_ci writel(tmp, SLC_CTRL(host->io_base)); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci lpc32xx_wp_enable(host); 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic int lpc32xx_nand_resume(struct platform_device *pdev) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); 96662306a36Sopenharmony_ci int ret; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* Re-enable NAND clock */ 96962306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk); 97062306a36Sopenharmony_ci if (ret) 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* Fresh init of NAND controller */ 97462306a36Sopenharmony_ci lpc32xx_nand_setup(host); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Disable write protect */ 97762306a36Sopenharmony_ci lpc32xx_wp_disable(host); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return 0; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic int lpc32xx_nand_suspend(struct platform_device *pdev, pm_message_t pm) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci uint32_t tmp; 98562306a36Sopenharmony_ci struct lpc32xx_nand_host *host = platform_get_drvdata(pdev); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci /* Force CE high */ 98862306a36Sopenharmony_ci tmp = readl(SLC_CTRL(host->io_base)); 98962306a36Sopenharmony_ci tmp &= ~SLCCFG_CE_LOW; 99062306a36Sopenharmony_ci writel(tmp, SLC_CTRL(host->io_base)); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Enable write protect for safety */ 99362306a36Sopenharmony_ci lpc32xx_wp_enable(host); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Disable clock */ 99662306a36Sopenharmony_ci clk_disable_unprepare(host->clk); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci return 0; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic const struct of_device_id lpc32xx_nand_match[] = { 100262306a36Sopenharmony_ci { .compatible = "nxp,lpc3220-slc" }, 100362306a36Sopenharmony_ci { /* sentinel */ }, 100462306a36Sopenharmony_ci}; 100562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc32xx_nand_match); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic struct platform_driver lpc32xx_nand_driver = { 100862306a36Sopenharmony_ci .probe = lpc32xx_nand_probe, 100962306a36Sopenharmony_ci .remove_new = lpc32xx_nand_remove, 101062306a36Sopenharmony_ci .resume = pm_ptr(lpc32xx_nand_resume), 101162306a36Sopenharmony_ci .suspend = pm_ptr(lpc32xx_nand_suspend), 101262306a36Sopenharmony_ci .driver = { 101362306a36Sopenharmony_ci .name = LPC32XX_MODNAME, 101462306a36Sopenharmony_ci .of_match_table = lpc32xx_nand_match, 101562306a36Sopenharmony_ci }, 101662306a36Sopenharmony_ci}; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_cimodule_platform_driver(lpc32xx_nand_driver); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 102162306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 102262306a36Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 102362306a36Sopenharmony_ciMODULE_DESCRIPTION("NAND driver for the NXP LPC32XX SLC controller"); 1024