162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ST Microelectronics 462306a36Sopenharmony_ci * Flexible Static Memory Controller (FSMC) 562306a36Sopenharmony_ci * Driver for NAND portions 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright © 2010 ST Microelectronics 862306a36Sopenharmony_ci * Vipin Kumar <vipin.kumar@st.com> 962306a36Sopenharmony_ci * Ashish Priyadarshi 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8) 1262306a36Sopenharmony_ci * Copyright © 2007 STMicroelectronics Pvt. Ltd. 1362306a36Sopenharmony_ci * Copyright © 2009 Alessandro Rubini 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/clk.h> 1762306a36Sopenharmony_ci#include <linux/completion.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/dmaengine.h> 2062306a36Sopenharmony_ci#include <linux/dma-direction.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <linux/err.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <linux/resource.h> 2662306a36Sopenharmony_ci#include <linux/sched.h> 2762306a36Sopenharmony_ci#include <linux/types.h> 2862306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2962306a36Sopenharmony_ci#include <linux/mtd/nand-ecc-sw-hamming.h> 3062306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 3162306a36Sopenharmony_ci#include <linux/platform_device.h> 3262306a36Sopenharmony_ci#include <linux/of.h> 3362306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 3462306a36Sopenharmony_ci#include <linux/io.h> 3562306a36Sopenharmony_ci#include <linux/slab.h> 3662306a36Sopenharmony_ci#include <linux/amba/bus.h> 3762306a36Sopenharmony_ci#include <mtd/mtd-abi.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* fsmc controller registers for NOR flash */ 4062306a36Sopenharmony_ci#define CTRL 0x0 4162306a36Sopenharmony_ci /* ctrl register definitions */ 4262306a36Sopenharmony_ci #define BANK_ENABLE BIT(0) 4362306a36Sopenharmony_ci #define MUXED BIT(1) 4462306a36Sopenharmony_ci #define NOR_DEV (2 << 2) 4562306a36Sopenharmony_ci #define WIDTH_16 BIT(4) 4662306a36Sopenharmony_ci #define RSTPWRDWN BIT(6) 4762306a36Sopenharmony_ci #define WPROT BIT(7) 4862306a36Sopenharmony_ci #define WRT_ENABLE BIT(12) 4962306a36Sopenharmony_ci #define WAIT_ENB BIT(13) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define CTRL_TIM 0x4 5262306a36Sopenharmony_ci /* ctrl_tim register definitions */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define FSMC_NOR_BANK_SZ 0x8 5562306a36Sopenharmony_ci#define FSMC_NOR_REG_SIZE 0x40 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define FSMC_NOR_REG(base, bank, reg) ((base) + \ 5862306a36Sopenharmony_ci (FSMC_NOR_BANK_SZ * (bank)) + \ 5962306a36Sopenharmony_ci (reg)) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* fsmc controller registers for NAND flash */ 6262306a36Sopenharmony_ci#define FSMC_PC 0x00 6362306a36Sopenharmony_ci /* pc register definitions */ 6462306a36Sopenharmony_ci #define FSMC_RESET BIT(0) 6562306a36Sopenharmony_ci #define FSMC_WAITON BIT(1) 6662306a36Sopenharmony_ci #define FSMC_ENABLE BIT(2) 6762306a36Sopenharmony_ci #define FSMC_DEVTYPE_NAND BIT(3) 6862306a36Sopenharmony_ci #define FSMC_DEVWID_16 BIT(4) 6962306a36Sopenharmony_ci #define FSMC_ECCEN BIT(6) 7062306a36Sopenharmony_ci #define FSMC_ECCPLEN_256 BIT(7) 7162306a36Sopenharmony_ci #define FSMC_TCLR_SHIFT (9) 7262306a36Sopenharmony_ci #define FSMC_TCLR_MASK (0xF) 7362306a36Sopenharmony_ci #define FSMC_TAR_SHIFT (13) 7462306a36Sopenharmony_ci #define FSMC_TAR_MASK (0xF) 7562306a36Sopenharmony_ci#define STS 0x04 7662306a36Sopenharmony_ci /* sts register definitions */ 7762306a36Sopenharmony_ci #define FSMC_CODE_RDY BIT(15) 7862306a36Sopenharmony_ci#define COMM 0x08 7962306a36Sopenharmony_ci /* comm register definitions */ 8062306a36Sopenharmony_ci #define FSMC_TSET_SHIFT 0 8162306a36Sopenharmony_ci #define FSMC_TSET_MASK 0xFF 8262306a36Sopenharmony_ci #define FSMC_TWAIT_SHIFT 8 8362306a36Sopenharmony_ci #define FSMC_TWAIT_MASK 0xFF 8462306a36Sopenharmony_ci #define FSMC_THOLD_SHIFT 16 8562306a36Sopenharmony_ci #define FSMC_THOLD_MASK 0xFF 8662306a36Sopenharmony_ci #define FSMC_THIZ_SHIFT 24 8762306a36Sopenharmony_ci #define FSMC_THIZ_MASK 0xFF 8862306a36Sopenharmony_ci#define ATTRIB 0x0C 8962306a36Sopenharmony_ci#define IOATA 0x10 9062306a36Sopenharmony_ci#define ECC1 0x14 9162306a36Sopenharmony_ci#define ECC2 0x18 9262306a36Sopenharmony_ci#define ECC3 0x1C 9362306a36Sopenharmony_ci#define FSMC_NAND_BANK_SZ 0x20 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * According to SPEAr300 Reference Manual (RM0082) 9962306a36Sopenharmony_ci * TOUDEL = 7ns (Output delay from the flip-flops to the board) 10062306a36Sopenharmony_ci * TINDEL = 5ns (Input delay from the board to the flipflop) 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci#define TOUTDEL 7000 10362306a36Sopenharmony_ci#define TINDEL 5000 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct fsmc_nand_timings { 10662306a36Sopenharmony_ci u8 tclr; 10762306a36Sopenharmony_ci u8 tar; 10862306a36Sopenharmony_ci u8 thiz; 10962306a36Sopenharmony_ci u8 thold; 11062306a36Sopenharmony_ci u8 twait; 11162306a36Sopenharmony_ci u8 tset; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cienum access_mode { 11562306a36Sopenharmony_ci USE_DMA_ACCESS = 1, 11662306a36Sopenharmony_ci USE_WORD_ACCESS, 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * struct fsmc_nand_data - structure for FSMC NAND device state 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * @base: Inherit from the nand_controller struct 12362306a36Sopenharmony_ci * @pid: Part ID on the AMBA PrimeCell format 12462306a36Sopenharmony_ci * @nand: Chip related info for a NAND flash. 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * @bank: Bank number for probed device. 12762306a36Sopenharmony_ci * @dev: Parent device 12862306a36Sopenharmony_ci * @mode: Access mode 12962306a36Sopenharmony_ci * @clk: Clock structure for FSMC. 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * @read_dma_chan: DMA channel for read access 13262306a36Sopenharmony_ci * @write_dma_chan: DMA channel for write access to NAND 13362306a36Sopenharmony_ci * @dma_access_complete: Completion structure 13462306a36Sopenharmony_ci * 13562306a36Sopenharmony_ci * @dev_timings: NAND timings 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * @data_pa: NAND Physical port for Data. 13862306a36Sopenharmony_ci * @data_va: NAND port for Data. 13962306a36Sopenharmony_ci * @cmd_va: NAND port for Command. 14062306a36Sopenharmony_ci * @addr_va: NAND port for Address. 14162306a36Sopenharmony_ci * @regs_va: Registers base address for a given bank. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_cistruct fsmc_nand_data { 14462306a36Sopenharmony_ci struct nand_controller base; 14562306a36Sopenharmony_ci u32 pid; 14662306a36Sopenharmony_ci struct nand_chip nand; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci unsigned int bank; 14962306a36Sopenharmony_ci struct device *dev; 15062306a36Sopenharmony_ci enum access_mode mode; 15162306a36Sopenharmony_ci struct clk *clk; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* DMA related objects */ 15462306a36Sopenharmony_ci struct dma_chan *read_dma_chan; 15562306a36Sopenharmony_ci struct dma_chan *write_dma_chan; 15662306a36Sopenharmony_ci struct completion dma_access_complete; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci struct fsmc_nand_timings *dev_timings; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci dma_addr_t data_pa; 16162306a36Sopenharmony_ci void __iomem *data_va; 16262306a36Sopenharmony_ci void __iomem *cmd_va; 16362306a36Sopenharmony_ci void __iomem *addr_va; 16462306a36Sopenharmony_ci void __iomem *regs_va; 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, 16862306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (section >= chip->ecc.steps) 17362306a36Sopenharmony_ci return -ERANGE; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci oobregion->offset = (section * 16) + 2; 17662306a36Sopenharmony_ci oobregion->length = 3; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int fsmc_ecc1_ooblayout_free(struct mtd_info *mtd, int section, 18262306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (section >= chip->ecc.steps) 18762306a36Sopenharmony_ci return -ERANGE; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci oobregion->offset = (section * 16) + 8; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (section < chip->ecc.steps - 1) 19262306a36Sopenharmony_ci oobregion->length = 8; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsmc_ecc1_ooblayout_ops = { 20062306a36Sopenharmony_ci .ecc = fsmc_ecc1_ooblayout_ecc, 20162306a36Sopenharmony_ci .free = fsmc_ecc1_ooblayout_free, 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * ECC placement definitions in oobfree type format. 20662306a36Sopenharmony_ci * There are 13 bytes of ecc for every 512 byte block and it has to be read 20762306a36Sopenharmony_ci * consecutively and immediately after the 512 byte data block for hardware to 20862306a36Sopenharmony_ci * generate the error bit offsets in 512 byte data. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistatic int fsmc_ecc4_ooblayout_ecc(struct mtd_info *mtd, int section, 21162306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (section >= chip->ecc.steps) 21662306a36Sopenharmony_ci return -ERANGE; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci oobregion->length = chip->ecc.bytes; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!section && mtd->writesize <= 512) 22162306a36Sopenharmony_ci oobregion->offset = 0; 22262306a36Sopenharmony_ci else 22362306a36Sopenharmony_ci oobregion->offset = (section * 16) + 2; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int fsmc_ecc4_ooblayout_free(struct mtd_info *mtd, int section, 22962306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (section >= chip->ecc.steps) 23462306a36Sopenharmony_ci return -ERANGE; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci oobregion->offset = (section * 16) + 15; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (section < chip->ecc.steps - 1) 23962306a36Sopenharmony_ci oobregion->length = 3; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = { 24762306a36Sopenharmony_ci .ecc = fsmc_ecc4_ooblayout_ecc, 24862306a36Sopenharmony_ci .free = fsmc_ecc4_ooblayout_free, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic inline struct fsmc_nand_data *nand_to_fsmc(struct nand_chip *chip) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci return container_of(chip, struct fsmc_nand_data, nand); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* 25762306a36Sopenharmony_ci * fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * This routine initializes timing parameters related to NAND memory access in 26062306a36Sopenharmony_ci * FSMC registers 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_cistatic void fsmc_nand_setup(struct fsmc_nand_data *host, 26362306a36Sopenharmony_ci struct fsmc_nand_timings *tims) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci u32 value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON; 26662306a36Sopenharmony_ci u32 tclr, tar, thiz, thold, twait, tset; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT; 26962306a36Sopenharmony_ci tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT; 27062306a36Sopenharmony_ci thiz = (tims->thiz & FSMC_THIZ_MASK) << FSMC_THIZ_SHIFT; 27162306a36Sopenharmony_ci thold = (tims->thold & FSMC_THOLD_MASK) << FSMC_THOLD_SHIFT; 27262306a36Sopenharmony_ci twait = (tims->twait & FSMC_TWAIT_MASK) << FSMC_TWAIT_SHIFT; 27362306a36Sopenharmony_ci tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (host->nand.options & NAND_BUSWIDTH_16) 27662306a36Sopenharmony_ci value |= FSMC_DEVWID_16; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci writel_relaxed(value | tclr | tar, host->regs_va + FSMC_PC); 27962306a36Sopenharmony_ci writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM); 28062306a36Sopenharmony_ci writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int fsmc_calc_timings(struct fsmc_nand_data *host, 28462306a36Sopenharmony_ci const struct nand_sdr_timings *sdrt, 28562306a36Sopenharmony_ci struct fsmc_nand_timings *tims) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci unsigned long hclk = clk_get_rate(host->clk); 28862306a36Sopenharmony_ci unsigned long hclkn = NSEC_PER_SEC / hclk; 28962306a36Sopenharmony_ci u32 thiz, thold, twait, tset, twait_min; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (sdrt->tRC_min < 30000) 29262306a36Sopenharmony_ci return -EOPNOTSUPP; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci tims->tar = DIV_ROUND_UP(sdrt->tAR_min / 1000, hclkn) - 1; 29562306a36Sopenharmony_ci if (tims->tar > FSMC_TAR_MASK) 29662306a36Sopenharmony_ci tims->tar = FSMC_TAR_MASK; 29762306a36Sopenharmony_ci tims->tclr = DIV_ROUND_UP(sdrt->tCLR_min / 1000, hclkn) - 1; 29862306a36Sopenharmony_ci if (tims->tclr > FSMC_TCLR_MASK) 29962306a36Sopenharmony_ci tims->tclr = FSMC_TCLR_MASK; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci thiz = sdrt->tCS_min - sdrt->tWP_min; 30262306a36Sopenharmony_ci tims->thiz = DIV_ROUND_UP(thiz / 1000, hclkn); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci thold = sdrt->tDH_min; 30562306a36Sopenharmony_ci if (thold < sdrt->tCH_min) 30662306a36Sopenharmony_ci thold = sdrt->tCH_min; 30762306a36Sopenharmony_ci if (thold < sdrt->tCLH_min) 30862306a36Sopenharmony_ci thold = sdrt->tCLH_min; 30962306a36Sopenharmony_ci if (thold < sdrt->tWH_min) 31062306a36Sopenharmony_ci thold = sdrt->tWH_min; 31162306a36Sopenharmony_ci if (thold < sdrt->tALH_min) 31262306a36Sopenharmony_ci thold = sdrt->tALH_min; 31362306a36Sopenharmony_ci if (thold < sdrt->tREH_min) 31462306a36Sopenharmony_ci thold = sdrt->tREH_min; 31562306a36Sopenharmony_ci tims->thold = DIV_ROUND_UP(thold / 1000, hclkn); 31662306a36Sopenharmony_ci if (tims->thold == 0) 31762306a36Sopenharmony_ci tims->thold = 1; 31862306a36Sopenharmony_ci else if (tims->thold > FSMC_THOLD_MASK) 31962306a36Sopenharmony_ci tims->thold = FSMC_THOLD_MASK; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci tset = max(sdrt->tCS_min - sdrt->tWP_min, 32262306a36Sopenharmony_ci sdrt->tCEA_max - sdrt->tREA_max); 32362306a36Sopenharmony_ci tims->tset = DIV_ROUND_UP(tset / 1000, hclkn) - 1; 32462306a36Sopenharmony_ci if (tims->tset == 0) 32562306a36Sopenharmony_ci tims->tset = 1; 32662306a36Sopenharmony_ci else if (tims->tset > FSMC_TSET_MASK) 32762306a36Sopenharmony_ci tims->tset = FSMC_TSET_MASK; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * According to SPEAr300 Reference Manual (RM0082) which gives more 33162306a36Sopenharmony_ci * information related to FSMSC timings than the SPEAr600 one (RM0305), 33262306a36Sopenharmony_ci * twait >= tCEA - (tset * TCLK) + TOUTDEL + TINDEL 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci twait_min = sdrt->tCEA_max - ((tims->tset + 1) * hclkn * 1000) 33562306a36Sopenharmony_ci + TOUTDEL + TINDEL; 33662306a36Sopenharmony_ci twait = max3(sdrt->tRP_min, sdrt->tWP_min, twait_min); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci tims->twait = DIV_ROUND_UP(twait / 1000, hclkn) - 1; 33962306a36Sopenharmony_ci if (tims->twait == 0) 34062306a36Sopenharmony_ci tims->twait = 1; 34162306a36Sopenharmony_ci else if (tims->twait > FSMC_TWAIT_MASK) 34262306a36Sopenharmony_ci tims->twait = FSMC_TWAIT_MASK; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int fsmc_setup_interface(struct nand_chip *nand, int csline, 34862306a36Sopenharmony_ci const struct nand_interface_config *conf) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(nand); 35162306a36Sopenharmony_ci struct fsmc_nand_timings tims; 35262306a36Sopenharmony_ci const struct nand_sdr_timings *sdrt; 35362306a36Sopenharmony_ci int ret; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci sdrt = nand_get_sdr_timings(conf); 35662306a36Sopenharmony_ci if (IS_ERR(sdrt)) 35762306a36Sopenharmony_ci return PTR_ERR(sdrt); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ret = fsmc_calc_timings(host, sdrt, &tims); 36062306a36Sopenharmony_ci if (ret) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci fsmc_nand_setup(host, &tims); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return 0; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic void fsmc_enable_hwecc(struct nand_chip *chip, int mode) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(chip); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCPLEN_256, 37962306a36Sopenharmony_ci host->regs_va + FSMC_PC); 38062306a36Sopenharmony_ci writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCEN, 38162306a36Sopenharmony_ci host->regs_va + FSMC_PC); 38262306a36Sopenharmony_ci writel_relaxed(readl(host->regs_va + FSMC_PC) | FSMC_ECCEN, 38362306a36Sopenharmony_ci host->regs_va + FSMC_PC); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* 38762306a36Sopenharmony_ci * fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by 38862306a36Sopenharmony_ci * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to 38962306a36Sopenharmony_ci * max of 8-bits) 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_cistatic int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const u8 *data, 39262306a36Sopenharmony_ci u8 *ecc) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(chip); 39562306a36Sopenharmony_ci u32 ecc_tmp; 39662306a36Sopenharmony_ci unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci do { 39962306a36Sopenharmony_ci if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY) 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci cond_resched(); 40362306a36Sopenharmony_ci } while (!time_after_eq(jiffies, deadline)); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (time_after_eq(jiffies, deadline)) { 40662306a36Sopenharmony_ci dev_err(host->dev, "calculate ecc timed out\n"); 40762306a36Sopenharmony_ci return -ETIMEDOUT; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ecc_tmp = readl_relaxed(host->regs_va + ECC1); 41162306a36Sopenharmony_ci ecc[0] = ecc_tmp; 41262306a36Sopenharmony_ci ecc[1] = ecc_tmp >> 8; 41362306a36Sopenharmony_ci ecc[2] = ecc_tmp >> 16; 41462306a36Sopenharmony_ci ecc[3] = ecc_tmp >> 24; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ecc_tmp = readl_relaxed(host->regs_va + ECC2); 41762306a36Sopenharmony_ci ecc[4] = ecc_tmp; 41862306a36Sopenharmony_ci ecc[5] = ecc_tmp >> 8; 41962306a36Sopenharmony_ci ecc[6] = ecc_tmp >> 16; 42062306a36Sopenharmony_ci ecc[7] = ecc_tmp >> 24; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ecc_tmp = readl_relaxed(host->regs_va + ECC3); 42362306a36Sopenharmony_ci ecc[8] = ecc_tmp; 42462306a36Sopenharmony_ci ecc[9] = ecc_tmp >> 8; 42562306a36Sopenharmony_ci ecc[10] = ecc_tmp >> 16; 42662306a36Sopenharmony_ci ecc[11] = ecc_tmp >> 24; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ecc_tmp = readl_relaxed(host->regs_va + STS); 42962306a36Sopenharmony_ci ecc[12] = ecc_tmp >> 16; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by 43662306a36Sopenharmony_ci * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to 43762306a36Sopenharmony_ci * max of 1-bit) 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_cistatic int fsmc_read_hwecc_ecc1(struct nand_chip *chip, const u8 *data, 44062306a36Sopenharmony_ci u8 *ecc) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(chip); 44362306a36Sopenharmony_ci u32 ecc_tmp; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ecc_tmp = readl_relaxed(host->regs_va + ECC1); 44662306a36Sopenharmony_ci ecc[0] = ecc_tmp; 44762306a36Sopenharmony_ci ecc[1] = ecc_tmp >> 8; 44862306a36Sopenharmony_ci ecc[2] = ecc_tmp >> 16; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic int fsmc_correct_ecc1(struct nand_chip *chip, 45462306a36Sopenharmony_ci unsigned char *buf, 45562306a36Sopenharmony_ci unsigned char *read_ecc, 45662306a36Sopenharmony_ci unsigned char *calc_ecc) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci bool sm_order = chip->ecc.options & NAND_ECC_SOFT_HAMMING_SM_ORDER; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return ecc_sw_hamming_correct(buf, read_ecc, calc_ecc, 46162306a36Sopenharmony_ci chip->ecc.size, sm_order); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci/* Count the number of 0's in buff upto a max of max_bits */ 46562306a36Sopenharmony_cistatic int count_written_bits(u8 *buff, int size, int max_bits) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci int k, written_bits = 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci for (k = 0; k < size; k++) { 47062306a36Sopenharmony_ci written_bits += hweight8(~buff[k]); 47162306a36Sopenharmony_ci if (written_bits > max_bits) 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return written_bits; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void dma_complete(void *param) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct fsmc_nand_data *host = param; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci complete(&host->dma_access_complete); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len, 48662306a36Sopenharmony_ci enum dma_data_direction direction) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct dma_chan *chan; 48962306a36Sopenharmony_ci struct dma_device *dma_dev; 49062306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 49162306a36Sopenharmony_ci dma_addr_t dma_dst, dma_src, dma_addr; 49262306a36Sopenharmony_ci dma_cookie_t cookie; 49362306a36Sopenharmony_ci unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci unsigned long time_left; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (direction == DMA_TO_DEVICE) 49862306a36Sopenharmony_ci chan = host->write_dma_chan; 49962306a36Sopenharmony_ci else if (direction == DMA_FROM_DEVICE) 50062306a36Sopenharmony_ci chan = host->read_dma_chan; 50162306a36Sopenharmony_ci else 50262306a36Sopenharmony_ci return -EINVAL; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci dma_dev = chan->device; 50562306a36Sopenharmony_ci dma_addr = dma_map_single(dma_dev->dev, buffer, len, direction); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (direction == DMA_TO_DEVICE) { 50862306a36Sopenharmony_ci dma_src = dma_addr; 50962306a36Sopenharmony_ci dma_dst = host->data_pa; 51062306a36Sopenharmony_ci } else { 51162306a36Sopenharmony_ci dma_src = host->data_pa; 51262306a36Sopenharmony_ci dma_dst = dma_addr; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, 51662306a36Sopenharmony_ci len, flags); 51762306a36Sopenharmony_ci if (!tx) { 51862306a36Sopenharmony_ci dev_err(host->dev, "device_prep_dma_memcpy error\n"); 51962306a36Sopenharmony_ci ret = -EIO; 52062306a36Sopenharmony_ci goto unmap_dma; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci tx->callback = dma_complete; 52462306a36Sopenharmony_ci tx->callback_param = host; 52562306a36Sopenharmony_ci cookie = tx->tx_submit(tx); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ret = dma_submit_error(cookie); 52862306a36Sopenharmony_ci if (ret) { 52962306a36Sopenharmony_ci dev_err(host->dev, "dma_submit_error %d\n", cookie); 53062306a36Sopenharmony_ci goto unmap_dma; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci dma_async_issue_pending(chan); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci time_left = 53662306a36Sopenharmony_ci wait_for_completion_timeout(&host->dma_access_complete, 53762306a36Sopenharmony_ci msecs_to_jiffies(3000)); 53862306a36Sopenharmony_ci if (time_left == 0) { 53962306a36Sopenharmony_ci dmaengine_terminate_all(chan); 54062306a36Sopenharmony_ci dev_err(host->dev, "wait_for_completion_timeout\n"); 54162306a36Sopenharmony_ci ret = -ETIMEDOUT; 54262306a36Sopenharmony_ci goto unmap_dma; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci ret = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciunmap_dma: 54862306a36Sopenharmony_ci dma_unmap_single(dma_dev->dev, dma_addr, len, direction); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return ret; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci/* 55462306a36Sopenharmony_ci * fsmc_write_buf - write buffer to chip 55562306a36Sopenharmony_ci * @host: FSMC NAND controller 55662306a36Sopenharmony_ci * @buf: data buffer 55762306a36Sopenharmony_ci * @len: number of bytes to write 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_cistatic void fsmc_write_buf(struct fsmc_nand_data *host, const u8 *buf, 56062306a36Sopenharmony_ci int len) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci int i; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) && 56562306a36Sopenharmony_ci IS_ALIGNED(len, sizeof(u32))) { 56662306a36Sopenharmony_ci u32 *p = (u32 *)buf; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci len = len >> 2; 56962306a36Sopenharmony_ci for (i = 0; i < len; i++) 57062306a36Sopenharmony_ci writel_relaxed(p[i], host->data_va); 57162306a36Sopenharmony_ci } else { 57262306a36Sopenharmony_ci for (i = 0; i < len; i++) 57362306a36Sopenharmony_ci writeb_relaxed(buf[i], host->data_va); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* 57862306a36Sopenharmony_ci * fsmc_read_buf - read chip data into buffer 57962306a36Sopenharmony_ci * @host: FSMC NAND controller 58062306a36Sopenharmony_ci * @buf: buffer to store date 58162306a36Sopenharmony_ci * @len: number of bytes to read 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_cistatic void fsmc_read_buf(struct fsmc_nand_data *host, u8 *buf, int len) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci int i; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) && 58862306a36Sopenharmony_ci IS_ALIGNED(len, sizeof(u32))) { 58962306a36Sopenharmony_ci u32 *p = (u32 *)buf; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci len = len >> 2; 59262306a36Sopenharmony_ci for (i = 0; i < len; i++) 59362306a36Sopenharmony_ci p[i] = readl_relaxed(host->data_va); 59462306a36Sopenharmony_ci } else { 59562306a36Sopenharmony_ci for (i = 0; i < len; i++) 59662306a36Sopenharmony_ci buf[i] = readb_relaxed(host->data_va); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* 60162306a36Sopenharmony_ci * fsmc_read_buf_dma - read chip data into buffer 60262306a36Sopenharmony_ci * @host: FSMC NAND controller 60362306a36Sopenharmony_ci * @buf: buffer to store date 60462306a36Sopenharmony_ci * @len: number of bytes to read 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_cistatic void fsmc_read_buf_dma(struct fsmc_nand_data *host, u8 *buf, 60762306a36Sopenharmony_ci int len) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci dma_xfer(host, buf, len, DMA_FROM_DEVICE); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/* 61362306a36Sopenharmony_ci * fsmc_write_buf_dma - write buffer to chip 61462306a36Sopenharmony_ci * @host: FSMC NAND controller 61562306a36Sopenharmony_ci * @buf: data buffer 61662306a36Sopenharmony_ci * @len: number of bytes to write 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic void fsmc_write_buf_dma(struct fsmc_nand_data *host, const u8 *buf, 61962306a36Sopenharmony_ci int len) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* 62562306a36Sopenharmony_ci * fsmc_exec_op - hook called by the core to execute NAND operations 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci * This controller is simple enough and thus does not need to use the parser 62862306a36Sopenharmony_ci * provided by the core, instead, handle every situation here. 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_cistatic int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op, 63162306a36Sopenharmony_ci bool check_only) 63262306a36Sopenharmony_ci{ 63362306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(chip); 63462306a36Sopenharmony_ci const struct nand_op_instr *instr = NULL; 63562306a36Sopenharmony_ci int ret = 0; 63662306a36Sopenharmony_ci unsigned int op_id; 63762306a36Sopenharmony_ci int i; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (check_only) 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci pr_debug("Executing operation [%d instructions]:\n", op->ninstrs); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci for (op_id = 0; op_id < op->ninstrs; op_id++) { 64562306a36Sopenharmony_ci instr = &op->instrs[op_id]; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci nand_op_trace(" ", instr); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci switch (instr->type) { 65062306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 65162306a36Sopenharmony_ci writeb_relaxed(instr->ctx.cmd.opcode, host->cmd_va); 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 65562306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) 65662306a36Sopenharmony_ci writeb_relaxed(instr->ctx.addr.addrs[i], 65762306a36Sopenharmony_ci host->addr_va); 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 66162306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) 66262306a36Sopenharmony_ci fsmc_read_buf_dma(host, instr->ctx.data.buf.in, 66362306a36Sopenharmony_ci instr->ctx.data.len); 66462306a36Sopenharmony_ci else 66562306a36Sopenharmony_ci fsmc_read_buf(host, instr->ctx.data.buf.in, 66662306a36Sopenharmony_ci instr->ctx.data.len); 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 67062306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) 67162306a36Sopenharmony_ci fsmc_write_buf_dma(host, 67262306a36Sopenharmony_ci instr->ctx.data.buf.out, 67362306a36Sopenharmony_ci instr->ctx.data.len); 67462306a36Sopenharmony_ci else 67562306a36Sopenharmony_ci fsmc_write_buf(host, instr->ctx.data.buf.out, 67662306a36Sopenharmony_ci instr->ctx.data.len); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 68062306a36Sopenharmony_ci ret = nand_soft_waitrdy(chip, 68162306a36Sopenharmony_ci instr->ctx.waitrdy.timeout_ms); 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (instr->delay_ns) 68662306a36Sopenharmony_ci ndelay(instr->delay_ns); 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return ret; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/* 69362306a36Sopenharmony_ci * fsmc_read_page_hwecc 69462306a36Sopenharmony_ci * @chip: nand chip info structure 69562306a36Sopenharmony_ci * @buf: buffer to store read data 69662306a36Sopenharmony_ci * @oob_required: caller expects OOB data read to chip->oob_poi 69762306a36Sopenharmony_ci * @page: page number to read 69862306a36Sopenharmony_ci * 69962306a36Sopenharmony_ci * This routine is needed for fsmc version 8 as reading from NAND chip has to be 70062306a36Sopenharmony_ci * performed in a strict sequence as follows: 70162306a36Sopenharmony_ci * data(512 byte) -> ecc(13 byte) 70262306a36Sopenharmony_ci * After this read, fsmc hardware generates and reports error data bits(up to a 70362306a36Sopenharmony_ci * max of 8 bits) 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic int fsmc_read_page_hwecc(struct nand_chip *chip, u8 *buf, 70662306a36Sopenharmony_ci int oob_required, int page) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 70962306a36Sopenharmony_ci int i, j, s, stat, eccsize = chip->ecc.size; 71062306a36Sopenharmony_ci int eccbytes = chip->ecc.bytes; 71162306a36Sopenharmony_ci int eccsteps = chip->ecc.steps; 71262306a36Sopenharmony_ci u8 *p = buf; 71362306a36Sopenharmony_ci u8 *ecc_calc = chip->ecc.calc_buf; 71462306a36Sopenharmony_ci u8 *ecc_code = chip->ecc.code_buf; 71562306a36Sopenharmony_ci int off, len, ret, group = 0; 71662306a36Sopenharmony_ci /* 71762306a36Sopenharmony_ci * ecc_oob is intentionally taken as u16. In 16bit devices, we 71862306a36Sopenharmony_ci * end up reading 14 bytes (7 words) from oob. The local array is 71962306a36Sopenharmony_ci * to maintain word alignment 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci u16 ecc_oob[7]; 72262306a36Sopenharmony_ci u8 *oob = (u8 *)&ecc_oob[0]; 72362306a36Sopenharmony_ci unsigned int max_bitflips = 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { 72662306a36Sopenharmony_ci nand_read_page_op(chip, page, s * eccsize, NULL, 0); 72762306a36Sopenharmony_ci chip->ecc.hwctl(chip, NAND_ECC_READ); 72862306a36Sopenharmony_ci ret = nand_read_data_op(chip, p, eccsize, false, false); 72962306a36Sopenharmony_ci if (ret) 73062306a36Sopenharmony_ci return ret; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci for (j = 0; j < eccbytes;) { 73362306a36Sopenharmony_ci struct mtd_oob_region oobregion; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci ret = mtd_ooblayout_ecc(mtd, group++, &oobregion); 73662306a36Sopenharmony_ci if (ret) 73762306a36Sopenharmony_ci return ret; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci off = oobregion.offset; 74062306a36Sopenharmony_ci len = oobregion.length; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* 74362306a36Sopenharmony_ci * length is intentionally kept a higher multiple of 2 74462306a36Sopenharmony_ci * to read at least 13 bytes even in case of 16 bit NAND 74562306a36Sopenharmony_ci * devices 74662306a36Sopenharmony_ci */ 74762306a36Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 74862306a36Sopenharmony_ci len = roundup(len, 2); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci nand_read_oob_op(chip, page, off, oob + j, len); 75162306a36Sopenharmony_ci j += len; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci memcpy(&ecc_code[i], oob, chip->ecc.bytes); 75562306a36Sopenharmony_ci chip->ecc.calculate(chip, p, &ecc_calc[i]); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci stat = chip->ecc.correct(chip, p, &ecc_code[i], &ecc_calc[i]); 75862306a36Sopenharmony_ci if (stat < 0) { 75962306a36Sopenharmony_ci mtd->ecc_stats.failed++; 76062306a36Sopenharmony_ci } else { 76162306a36Sopenharmony_ci mtd->ecc_stats.corrected += stat; 76262306a36Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, stat); 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return max_bitflips; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* 77062306a36Sopenharmony_ci * fsmc_bch8_correct_data 77162306a36Sopenharmony_ci * @mtd: mtd info structure 77262306a36Sopenharmony_ci * @dat: buffer of read data 77362306a36Sopenharmony_ci * @read_ecc: ecc read from device spare area 77462306a36Sopenharmony_ci * @calc_ecc: ecc calculated from read data 77562306a36Sopenharmony_ci * 77662306a36Sopenharmony_ci * calc_ecc is a 104 bit information containing maximum of 8 error 77762306a36Sopenharmony_ci * offset information of 13 bits each in 512 bytes of read data. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_cistatic int fsmc_bch8_correct_data(struct nand_chip *chip, u8 *dat, 78062306a36Sopenharmony_ci u8 *read_ecc, u8 *calc_ecc) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(chip); 78362306a36Sopenharmony_ci u32 err_idx[8]; 78462306a36Sopenharmony_ci u32 num_err, i; 78562306a36Sopenharmony_ci u32 ecc1, ecc2, ecc3, ecc4; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* no bit flipping */ 79062306a36Sopenharmony_ci if (likely(num_err == 0)) 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* too many errors */ 79462306a36Sopenharmony_ci if (unlikely(num_err > 8)) { 79562306a36Sopenharmony_ci /* 79662306a36Sopenharmony_ci * This is a temporary erase check. A newly erased page read 79762306a36Sopenharmony_ci * would result in an ecc error because the oob data is also 79862306a36Sopenharmony_ci * erased to FF and the calculated ecc for an FF data is not 79962306a36Sopenharmony_ci * FF..FF. 80062306a36Sopenharmony_ci * This is a workaround to skip performing correction in case 80162306a36Sopenharmony_ci * data is FF..FF 80262306a36Sopenharmony_ci * 80362306a36Sopenharmony_ci * Logic: 80462306a36Sopenharmony_ci * For every page, each bit written as 0 is counted until these 80562306a36Sopenharmony_ci * number of bits are greater than 8 (the maximum correction 80662306a36Sopenharmony_ci * capability of FSMC for each 512 + 13 bytes) 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci int bits_ecc = count_written_bits(read_ecc, chip->ecc.bytes, 8); 81062306a36Sopenharmony_ci int bits_data = count_written_bits(dat, chip->ecc.size, 8); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if ((bits_ecc + bits_data) <= 8) { 81362306a36Sopenharmony_ci if (bits_data) 81462306a36Sopenharmony_ci memset(dat, 0xff, chip->ecc.size); 81562306a36Sopenharmony_ci return bits_data; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return -EBADMSG; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci /* 82262306a36Sopenharmony_ci * ------------------- calc_ecc[] bit wise -----------|--13 bits--| 82362306a36Sopenharmony_ci * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--| 82462306a36Sopenharmony_ci * 82562306a36Sopenharmony_ci * calc_ecc is a 104 bit information containing maximum of 8 error 82662306a36Sopenharmony_ci * offset information of 13 bits each. calc_ecc is copied into a 82762306a36Sopenharmony_ci * u64 array and error offset indexes are populated in err_idx 82862306a36Sopenharmony_ci * array 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci ecc1 = readl_relaxed(host->regs_va + ECC1); 83162306a36Sopenharmony_ci ecc2 = readl_relaxed(host->regs_va + ECC2); 83262306a36Sopenharmony_ci ecc3 = readl_relaxed(host->regs_va + ECC3); 83362306a36Sopenharmony_ci ecc4 = readl_relaxed(host->regs_va + STS); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci err_idx[0] = (ecc1 >> 0) & 0x1FFF; 83662306a36Sopenharmony_ci err_idx[1] = (ecc1 >> 13) & 0x1FFF; 83762306a36Sopenharmony_ci err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); 83862306a36Sopenharmony_ci err_idx[3] = (ecc2 >> 7) & 0x1FFF; 83962306a36Sopenharmony_ci err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); 84062306a36Sopenharmony_ci err_idx[5] = (ecc3 >> 1) & 0x1FFF; 84162306a36Sopenharmony_ci err_idx[6] = (ecc3 >> 14) & 0x1FFF; 84262306a36Sopenharmony_ci err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci i = 0; 84562306a36Sopenharmony_ci while (num_err--) { 84662306a36Sopenharmony_ci err_idx[i] ^= 3; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (err_idx[i] < chip->ecc.size * 8) { 84962306a36Sopenharmony_ci int err = err_idx[i]; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci dat[err >> 3] ^= BIT(err & 7); 85262306a36Sopenharmony_ci i++; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci return i; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic bool filter(struct dma_chan *chan, void *slave) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci chan->private = slave; 86162306a36Sopenharmony_ci return true; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int fsmc_nand_probe_config_dt(struct platform_device *pdev, 86562306a36Sopenharmony_ci struct fsmc_nand_data *host, 86662306a36Sopenharmony_ci struct nand_chip *nand) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 86962306a36Sopenharmony_ci u32 val; 87062306a36Sopenharmony_ci int ret; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci nand->options = 0; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (!of_property_read_u32(np, "bank-width", &val)) { 87562306a36Sopenharmony_ci if (val == 2) { 87662306a36Sopenharmony_ci nand->options |= NAND_BUSWIDTH_16; 87762306a36Sopenharmony_ci } else if (val != 1) { 87862306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid bank-width %u\n", val); 87962306a36Sopenharmony_ci return -EINVAL; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (of_property_read_bool(np, "nand-skip-bbtscan")) 88462306a36Sopenharmony_ci nand->options |= NAND_SKIP_BBTSCAN; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci host->dev_timings = devm_kzalloc(&pdev->dev, 88762306a36Sopenharmony_ci sizeof(*host->dev_timings), 88862306a36Sopenharmony_ci GFP_KERNEL); 88962306a36Sopenharmony_ci if (!host->dev_timings) 89062306a36Sopenharmony_ci return -ENOMEM; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings, 89362306a36Sopenharmony_ci sizeof(*host->dev_timings)); 89462306a36Sopenharmony_ci if (ret) 89562306a36Sopenharmony_ci host->dev_timings = NULL; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* Set default NAND bank to 0 */ 89862306a36Sopenharmony_ci host->bank = 0; 89962306a36Sopenharmony_ci if (!of_property_read_u32(np, "bank", &val)) { 90062306a36Sopenharmony_ci if (val > 3) { 90162306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid bank %u\n", val); 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci host->bank = val; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci return 0; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic int fsmc_nand_attach_chip(struct nand_chip *nand) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 91262306a36Sopenharmony_ci struct fsmc_nand_data *host = nand_to_fsmc(nand); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_INVALID) 91562306a36Sopenharmony_ci nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (!nand->ecc.size) 91862306a36Sopenharmony_ci nand->ecc.size = 512; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (AMBA_REV_BITS(host->pid) >= 8) { 92162306a36Sopenharmony_ci nand->ecc.read_page = fsmc_read_page_hwecc; 92262306a36Sopenharmony_ci nand->ecc.calculate = fsmc_read_hwecc_ecc4; 92362306a36Sopenharmony_ci nand->ecc.correct = fsmc_bch8_correct_data; 92462306a36Sopenharmony_ci nand->ecc.bytes = 13; 92562306a36Sopenharmony_ci nand->ecc.strength = 8; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (AMBA_REV_BITS(host->pid) >= 8) { 92962306a36Sopenharmony_ci switch (mtd->oobsize) { 93062306a36Sopenharmony_ci case 16: 93162306a36Sopenharmony_ci case 64: 93262306a36Sopenharmony_ci case 128: 93362306a36Sopenharmony_ci case 224: 93462306a36Sopenharmony_ci case 256: 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci default: 93762306a36Sopenharmony_ci dev_warn(host->dev, 93862306a36Sopenharmony_ci "No oob scheme defined for oobsize %d\n", 93962306a36Sopenharmony_ci mtd->oobsize); 94062306a36Sopenharmony_ci return -EINVAL; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &fsmc_ecc4_ooblayout_ops); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return 0; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci switch (nand->ecc.engine_type) { 94962306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 95062306a36Sopenharmony_ci dev_info(host->dev, "Using 1-bit HW ECC scheme\n"); 95162306a36Sopenharmony_ci nand->ecc.calculate = fsmc_read_hwecc_ecc1; 95262306a36Sopenharmony_ci nand->ecc.correct = fsmc_correct_ecc1; 95362306a36Sopenharmony_ci nand->ecc.hwctl = fsmc_enable_hwecc; 95462306a36Sopenharmony_ci nand->ecc.bytes = 3; 95562306a36Sopenharmony_ci nand->ecc.strength = 1; 95662306a36Sopenharmony_ci nand->ecc.options |= NAND_ECC_SOFT_HAMMING_SM_ORDER; 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 96062306a36Sopenharmony_ci if (nand->ecc.algo == NAND_ECC_ALGO_BCH) { 96162306a36Sopenharmony_ci dev_info(host->dev, 96262306a36Sopenharmony_ci "Using 4-bit SW BCH ECC scheme\n"); 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci break; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_DIE: 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci default: 97162306a36Sopenharmony_ci dev_err(host->dev, "Unsupported ECC mode!\n"); 97262306a36Sopenharmony_ci return -ENOTSUPP; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci /* 97662306a36Sopenharmony_ci * Don't set layout for BCH4 SW ECC. This will be 97762306a36Sopenharmony_ci * generated later during BCH initialization. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ci if (nand->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { 98062306a36Sopenharmony_ci switch (mtd->oobsize) { 98162306a36Sopenharmony_ci case 16: 98262306a36Sopenharmony_ci case 64: 98362306a36Sopenharmony_ci case 128: 98462306a36Sopenharmony_ci mtd_set_ooblayout(mtd, 98562306a36Sopenharmony_ci &fsmc_ecc1_ooblayout_ops); 98662306a36Sopenharmony_ci break; 98762306a36Sopenharmony_ci default: 98862306a36Sopenharmony_ci dev_warn(host->dev, 98962306a36Sopenharmony_ci "No oob scheme defined for oobsize %d\n", 99062306a36Sopenharmony_ci mtd->oobsize); 99162306a36Sopenharmony_ci return -EINVAL; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic const struct nand_controller_ops fsmc_nand_controller_ops = { 99962306a36Sopenharmony_ci .attach_chip = fsmc_nand_attach_chip, 100062306a36Sopenharmony_ci .exec_op = fsmc_exec_op, 100162306a36Sopenharmony_ci .setup_interface = fsmc_setup_interface, 100262306a36Sopenharmony_ci}; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci/** 100562306a36Sopenharmony_ci * fsmc_nand_disable() - Disables the NAND bank 100662306a36Sopenharmony_ci * @host: The instance to disable 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_cistatic void fsmc_nand_disable(struct fsmc_nand_data *host) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci u32 val; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci val = readl(host->regs_va + FSMC_PC); 101362306a36Sopenharmony_ci val &= ~FSMC_ENABLE; 101462306a36Sopenharmony_ci writel(val, host->regs_va + FSMC_PC); 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci/* 101862306a36Sopenharmony_ci * fsmc_nand_probe - Probe function 101962306a36Sopenharmony_ci * @pdev: platform device structure 102062306a36Sopenharmony_ci */ 102162306a36Sopenharmony_cistatic int __init fsmc_nand_probe(struct platform_device *pdev) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct fsmc_nand_data *host; 102462306a36Sopenharmony_ci struct mtd_info *mtd; 102562306a36Sopenharmony_ci struct nand_chip *nand; 102662306a36Sopenharmony_ci struct resource *res; 102762306a36Sopenharmony_ci void __iomem *base; 102862306a36Sopenharmony_ci dma_cap_mask_t mask; 102962306a36Sopenharmony_ci int ret = 0; 103062306a36Sopenharmony_ci u32 pid; 103162306a36Sopenharmony_ci int i; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci /* Allocate memory for the device structure (and zero it) */ 103462306a36Sopenharmony_ci host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); 103562306a36Sopenharmony_ci if (!host) 103662306a36Sopenharmony_ci return -ENOMEM; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci nand = &host->nand; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci ret = fsmc_nand_probe_config_dt(pdev, host, nand); 104162306a36Sopenharmony_ci if (ret) 104262306a36Sopenharmony_ci return ret; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); 104562306a36Sopenharmony_ci host->data_va = devm_ioremap_resource(&pdev->dev, res); 104662306a36Sopenharmony_ci if (IS_ERR(host->data_va)) 104762306a36Sopenharmony_ci return PTR_ERR(host->data_va); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci host->data_pa = (dma_addr_t)res->start; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); 105262306a36Sopenharmony_ci host->addr_va = devm_ioremap_resource(&pdev->dev, res); 105362306a36Sopenharmony_ci if (IS_ERR(host->addr_va)) 105462306a36Sopenharmony_ci return PTR_ERR(host->addr_va); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); 105762306a36Sopenharmony_ci host->cmd_va = devm_ioremap_resource(&pdev->dev, res); 105862306a36Sopenharmony_ci if (IS_ERR(host->cmd_va)) 105962306a36Sopenharmony_ci return PTR_ERR(host->cmd_va); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); 106262306a36Sopenharmony_ci base = devm_ioremap_resource(&pdev->dev, res); 106362306a36Sopenharmony_ci if (IS_ERR(base)) 106462306a36Sopenharmony_ci return PTR_ERR(base); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci host->regs_va = base + FSMC_NOR_REG_SIZE + 106762306a36Sopenharmony_ci (host->bank * FSMC_NAND_BANK_SZ); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci host->clk = devm_clk_get_enabled(&pdev->dev, NULL); 107062306a36Sopenharmony_ci if (IS_ERR(host->clk)) { 107162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to fetch block clock\n"); 107262306a36Sopenharmony_ci return PTR_ERR(host->clk); 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci /* 107662306a36Sopenharmony_ci * This device ID is actually a common AMBA ID as used on the 107762306a36Sopenharmony_ci * AMBA PrimeCell bus. However it is not a PrimeCell. 107862306a36Sopenharmony_ci */ 107962306a36Sopenharmony_ci for (pid = 0, i = 0; i < 4; i++) 108062306a36Sopenharmony_ci pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 108162306a36Sopenharmony_ci 255) << (i * 8); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci host->pid = pid; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci dev_info(&pdev->dev, 108662306a36Sopenharmony_ci "FSMC device partno %03x, manufacturer %02x, revision %02x, config %02x\n", 108762306a36Sopenharmony_ci AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid), 108862306a36Sopenharmony_ci AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid)); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci host->dev = &pdev->dev; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) 109362306a36Sopenharmony_ci init_completion(&host->dma_access_complete); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci /* Link all private pointers */ 109662306a36Sopenharmony_ci mtd = nand_to_mtd(&host->nand); 109762306a36Sopenharmony_ci nand_set_flash_node(nand, pdev->dev.of_node); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci mtd->dev.parent = &pdev->dev; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci nand->badblockbits = 7; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) { 110462306a36Sopenharmony_ci dma_cap_zero(mask); 110562306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, mask); 110662306a36Sopenharmony_ci host->read_dma_chan = dma_request_channel(mask, filter, NULL); 110762306a36Sopenharmony_ci if (!host->read_dma_chan) { 110862306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get read dma channel\n"); 110962306a36Sopenharmony_ci ret = -ENODEV; 111062306a36Sopenharmony_ci goto disable_fsmc; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci host->write_dma_chan = dma_request_channel(mask, filter, NULL); 111362306a36Sopenharmony_ci if (!host->write_dma_chan) { 111462306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get write dma channel\n"); 111562306a36Sopenharmony_ci ret = -ENODEV; 111662306a36Sopenharmony_ci goto release_dma_read_chan; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci if (host->dev_timings) { 112162306a36Sopenharmony_ci fsmc_nand_setup(host, host->dev_timings); 112262306a36Sopenharmony_ci nand->options |= NAND_KEEP_TIMINGS; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci nand_controller_init(&host->base); 112662306a36Sopenharmony_ci host->base.ops = &fsmc_nand_controller_ops; 112762306a36Sopenharmony_ci nand->controller = &host->base; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci /* 113062306a36Sopenharmony_ci * Scan to find existence of the device 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_ci ret = nand_scan(nand, 1); 113362306a36Sopenharmony_ci if (ret) 113462306a36Sopenharmony_ci goto release_dma_write_chan; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci mtd->name = "nand"; 113762306a36Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 113862306a36Sopenharmony_ci if (ret) 113962306a36Sopenharmony_ci goto cleanup_nand; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 114262306a36Sopenharmony_ci dev_info(&pdev->dev, "FSMC NAND driver registration successful\n"); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci return 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_cicleanup_nand: 114762306a36Sopenharmony_ci nand_cleanup(nand); 114862306a36Sopenharmony_cirelease_dma_write_chan: 114962306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) 115062306a36Sopenharmony_ci dma_release_channel(host->write_dma_chan); 115162306a36Sopenharmony_cirelease_dma_read_chan: 115262306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) 115362306a36Sopenharmony_ci dma_release_channel(host->read_dma_chan); 115462306a36Sopenharmony_cidisable_fsmc: 115562306a36Sopenharmony_ci fsmc_nand_disable(host); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci return ret; 115862306a36Sopenharmony_ci} 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci/* 116162306a36Sopenharmony_ci * Clean up routine 116262306a36Sopenharmony_ci */ 116362306a36Sopenharmony_cistatic void fsmc_nand_remove(struct platform_device *pdev) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct fsmc_nand_data *host = platform_get_drvdata(pdev); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (host) { 116862306a36Sopenharmony_ci struct nand_chip *chip = &host->nand; 116962306a36Sopenharmony_ci int ret; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 117262306a36Sopenharmony_ci WARN_ON(ret); 117362306a36Sopenharmony_ci nand_cleanup(chip); 117462306a36Sopenharmony_ci fsmc_nand_disable(host); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (host->mode == USE_DMA_ACCESS) { 117762306a36Sopenharmony_ci dma_release_channel(host->write_dma_chan); 117862306a36Sopenharmony_ci dma_release_channel(host->read_dma_chan); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 118462306a36Sopenharmony_cistatic int fsmc_nand_suspend(struct device *dev) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct fsmc_nand_data *host = dev_get_drvdata(dev); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (host) 118962306a36Sopenharmony_ci clk_disable_unprepare(host->clk); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci return 0; 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic int fsmc_nand_resume(struct device *dev) 119562306a36Sopenharmony_ci{ 119662306a36Sopenharmony_ci struct fsmc_nand_data *host = dev_get_drvdata(dev); 119762306a36Sopenharmony_ci int ret; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (host) { 120062306a36Sopenharmony_ci ret = clk_prepare_enable(host->clk); 120162306a36Sopenharmony_ci if (ret) { 120262306a36Sopenharmony_ci dev_err(dev, "failed to enable clk\n"); 120362306a36Sopenharmony_ci return ret; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci if (host->dev_timings) 120662306a36Sopenharmony_ci fsmc_nand_setup(host, host->dev_timings); 120762306a36Sopenharmony_ci nand_reset(&host->nand, 0); 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return 0; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci#endif 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic const struct of_device_id fsmc_nand_id_table[] = { 121762306a36Sopenharmony_ci { .compatible = "st,spear600-fsmc-nand" }, 121862306a36Sopenharmony_ci { .compatible = "stericsson,fsmc-nand" }, 121962306a36Sopenharmony_ci {} 122062306a36Sopenharmony_ci}; 122162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fsmc_nand_id_table); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic struct platform_driver fsmc_nand_driver = { 122462306a36Sopenharmony_ci .remove_new = fsmc_nand_remove, 122562306a36Sopenharmony_ci .driver = { 122662306a36Sopenharmony_ci .name = "fsmc-nand", 122762306a36Sopenharmony_ci .of_match_table = fsmc_nand_id_table, 122862306a36Sopenharmony_ci .pm = &fsmc_nand_pm_ops, 122962306a36Sopenharmony_ci }, 123062306a36Sopenharmony_ci}; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cimodule_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 123562306a36Sopenharmony_ciMODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi"); 123662306a36Sopenharmony_ciMODULE_DESCRIPTION("NAND driver for SPEAr Platforms"); 1237