1b74e6985SXiaolei Li // SPDX-License-Identifier: GPL-2.0 OR MIT 21d6b1e46SJorge Ramirez-Ortiz /* 31d6b1e46SJorge Ramirez-Ortiz * MTK NAND Flash controller driver. 41d6b1e46SJorge Ramirez-Ortiz * Copyright (C) 2016 MediaTek Inc. 51d6b1e46SJorge Ramirez-Ortiz * Authors: Xiaolei Li <xiaolei.li@mediatek.com> 61d6b1e46SJorge Ramirez-Ortiz * Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org> 71d6b1e46SJorge Ramirez-Ortiz */ 81d6b1e46SJorge Ramirez-Ortiz 91d6b1e46SJorge Ramirez-Ortiz #include <linux/platform_device.h> 101d6b1e46SJorge Ramirez-Ortiz #include <linux/dma-mapping.h> 111d6b1e46SJorge Ramirez-Ortiz #include <linux/interrupt.h> 121d6b1e46SJorge Ramirez-Ortiz #include <linux/delay.h> 131d6b1e46SJorge Ramirez-Ortiz #include <linux/clk.h> 14d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h> 151d6b1e46SJorge Ramirez-Ortiz #include <linux/mtd/mtd.h> 161d6b1e46SJorge Ramirez-Ortiz #include <linux/module.h> 171d6b1e46SJorge Ramirez-Ortiz #include <linux/iopoll.h> 181d6b1e46SJorge Ramirez-Ortiz #include <linux/of.h> 197ec4a37cSXiaolei Li #include <linux/of_device.h> 201d6b1e46SJorge Ramirez-Ortiz #include "mtk_ecc.h" 211d6b1e46SJorge Ramirez-Ortiz 221d6b1e46SJorge Ramirez-Ortiz /* NAND controller register definition */ 231d6b1e46SJorge Ramirez-Ortiz #define NFI_CNFG (0x00) 241d6b1e46SJorge Ramirez-Ortiz #define CNFG_AHB BIT(0) 251d6b1e46SJorge Ramirez-Ortiz #define CNFG_READ_EN BIT(1) 261d6b1e46SJorge Ramirez-Ortiz #define CNFG_DMA_BURST_EN BIT(2) 271d6b1e46SJorge Ramirez-Ortiz #define CNFG_BYTE_RW BIT(6) 281d6b1e46SJorge Ramirez-Ortiz #define CNFG_HW_ECC_EN BIT(8) 291d6b1e46SJorge Ramirez-Ortiz #define CNFG_AUTO_FMT_EN BIT(9) 301d6b1e46SJorge Ramirez-Ortiz #define CNFG_OP_CUST (6 << 12) 311d6b1e46SJorge Ramirez-Ortiz #define NFI_PAGEFMT (0x04) 321d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_FDM_ECC_SHIFT (12) 331d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_FDM_SHIFT (8) 341d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_SEC_SEL_512 BIT(2) 351d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_512_2K (0) 361d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_2K_4K (1) 371d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_4K_8K (2) 381d6b1e46SJorge Ramirez-Ortiz #define PAGEFMT_8K_16K (3) 391d6b1e46SJorge Ramirez-Ortiz /* NFI control */ 401d6b1e46SJorge Ramirez-Ortiz #define NFI_CON (0x08) 411d6b1e46SJorge Ramirez-Ortiz #define CON_FIFO_FLUSH BIT(0) 421d6b1e46SJorge Ramirez-Ortiz #define CON_NFI_RST BIT(1) 431d6b1e46SJorge Ramirez-Ortiz #define CON_BRD BIT(8) /* burst read */ 441d6b1e46SJorge Ramirez-Ortiz #define CON_BWR BIT(9) /* burst write */ 451d6b1e46SJorge Ramirez-Ortiz #define CON_SEC_SHIFT (12) 461d6b1e46SJorge Ramirez-Ortiz /* Timming control register */ 471d6b1e46SJorge Ramirez-Ortiz #define NFI_ACCCON (0x0C) 481d6b1e46SJorge Ramirez-Ortiz #define NFI_INTR_EN (0x10) 491d6b1e46SJorge Ramirez-Ortiz #define INTR_AHB_DONE_EN BIT(6) 501d6b1e46SJorge Ramirez-Ortiz #define NFI_INTR_STA (0x14) 511d6b1e46SJorge Ramirez-Ortiz #define NFI_CMD (0x20) 521d6b1e46SJorge Ramirez-Ortiz #define NFI_ADDRNOB (0x30) 531d6b1e46SJorge Ramirez-Ortiz #define NFI_COLADDR (0x34) 541d6b1e46SJorge Ramirez-Ortiz #define NFI_ROWADDR (0x38) 551d6b1e46SJorge Ramirez-Ortiz #define NFI_STRDATA (0x40) 561d6b1e46SJorge Ramirez-Ortiz #define STAR_EN (1) 571d6b1e46SJorge Ramirez-Ortiz #define STAR_DE (0) 581d6b1e46SJorge Ramirez-Ortiz #define NFI_CNRNB (0x44) 591d6b1e46SJorge Ramirez-Ortiz #define NFI_DATAW (0x50) 601d6b1e46SJorge Ramirez-Ortiz #define NFI_DATAR (0x54) 611d6b1e46SJorge Ramirez-Ortiz #define NFI_PIO_DIRDY (0x58) 621d6b1e46SJorge Ramirez-Ortiz #define PIO_DI_RDY (0x01) 631d6b1e46SJorge Ramirez-Ortiz #define NFI_STA (0x60) 641d6b1e46SJorge Ramirez-Ortiz #define STA_CMD BIT(0) 651d6b1e46SJorge Ramirez-Ortiz #define STA_ADDR BIT(1) 661d6b1e46SJorge Ramirez-Ortiz #define STA_BUSY BIT(8) 671d6b1e46SJorge Ramirez-Ortiz #define STA_EMP_PAGE BIT(12) 681d6b1e46SJorge Ramirez-Ortiz #define NFI_FSM_CUSTDATA (0xe << 16) 691d6b1e46SJorge Ramirez-Ortiz #define NFI_FSM_MASK (0xf << 16) 701d6b1e46SJorge Ramirez-Ortiz #define NFI_ADDRCNTR (0x70) 711d6b1e46SJorge Ramirez-Ortiz #define CNTR_MASK GENMASK(16, 12) 72559e58e7SRogerCC Lin #define ADDRCNTR_SEC_SHIFT (12) 73559e58e7SRogerCC Lin #define ADDRCNTR_SEC(val) \ 74559e58e7SRogerCC Lin (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT) 751d6b1e46SJorge Ramirez-Ortiz #define NFI_STRADDR (0x80) 761d6b1e46SJorge Ramirez-Ortiz #define NFI_BYTELEN (0x84) 771d6b1e46SJorge Ramirez-Ortiz #define NFI_CSEL (0x90) 781d6b1e46SJorge Ramirez-Ortiz #define NFI_FDML(x) (0xA0 + (x) * sizeof(u32) * 2) 791d6b1e46SJorge Ramirez-Ortiz #define NFI_FDMM(x) (0xA4 + (x) * sizeof(u32) * 2) 801d6b1e46SJorge Ramirez-Ortiz #define NFI_FDM_MAX_SIZE (8) 811d6b1e46SJorge Ramirez-Ortiz #define NFI_FDM_MIN_SIZE (1) 8242d13a09SXiaolei Li #define NFI_DEBUG_CON1 (0x220) 8342d13a09SXiaolei Li #define STROBE_MASK GENMASK(4, 3) 8442d13a09SXiaolei Li #define STROBE_SHIFT (3) 8542d13a09SXiaolei Li #define MAX_STROBE_DLY (3) 861d6b1e46SJorge Ramirez-Ortiz #define NFI_MASTER_STA (0x224) 871d6b1e46SJorge Ramirez-Ortiz #define MASTER_STA_MASK (0x0FFF) 881d6b1e46SJorge Ramirez-Ortiz #define NFI_EMPTY_THRESH (0x23C) 891d6b1e46SJorge Ramirez-Ortiz 901d6b1e46SJorge Ramirez-Ortiz #define MTK_NAME "mtk-nand" 911d6b1e46SJorge Ramirez-Ortiz #define KB(x) ((x) * 1024UL) 921d6b1e46SJorge Ramirez-Ortiz #define MB(x) (KB(x) * 1024UL) 931d6b1e46SJorge Ramirez-Ortiz 941d6b1e46SJorge Ramirez-Ortiz #define MTK_TIMEOUT (500000) 951d6b1e46SJorge Ramirez-Ortiz #define MTK_RESET_TIMEOUT (1000000) 961d6b1e46SJorge Ramirez-Ortiz #define MTK_NAND_MAX_NSELS (2) 977ec4a37cSXiaolei Li #define MTK_NFC_MIN_SPARE (16) 98edfee361SXiaolei Li #define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \ 99edfee361SXiaolei Li ((tpoecs) << 28 | (tprecs) << 22 | (tc2r) << 16 | \ 100edfee361SXiaolei Li (tw2r) << 12 | (twh) << 8 | (twst) << 4 | (trlt)) 1017ec4a37cSXiaolei Li 1027ec4a37cSXiaolei Li struct mtk_nfc_caps { 1037ec4a37cSXiaolei Li const u8 *spare_size; 1047ec4a37cSXiaolei Li u8 num_spare_size; 1057ec4a37cSXiaolei Li u8 pageformat_spare_shift; 106edfee361SXiaolei Li u8 nfi_clk_div; 107b45ee550SRogerCC Lin u8 max_sector; 108b45ee550SRogerCC Lin u32 max_sector_size; 1097ec4a37cSXiaolei Li }; 1101d6b1e46SJorge Ramirez-Ortiz 1111d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_bad_mark_ctl { 1121d6b1e46SJorge Ramirez-Ortiz void (*bm_swap)(struct mtd_info *, u8 *buf, int raw); 1131d6b1e46SJorge Ramirez-Ortiz u32 sec; 1141d6b1e46SJorge Ramirez-Ortiz u32 pos; 1151d6b1e46SJorge Ramirez-Ortiz }; 1161d6b1e46SJorge Ramirez-Ortiz 1171d6b1e46SJorge Ramirez-Ortiz /* 1181d6b1e46SJorge Ramirez-Ortiz * FDM: region used to store free OOB data 1191d6b1e46SJorge Ramirez-Ortiz */ 1201d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm { 1211d6b1e46SJorge Ramirez-Ortiz u32 reg_size; 1221d6b1e46SJorge Ramirez-Ortiz u32 ecc_size; 1231d6b1e46SJorge Ramirez-Ortiz }; 1241d6b1e46SJorge Ramirez-Ortiz 1251d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip { 1261d6b1e46SJorge Ramirez-Ortiz struct list_head node; 1271d6b1e46SJorge Ramirez-Ortiz struct nand_chip nand; 1281d6b1e46SJorge Ramirez-Ortiz 1291d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_bad_mark_ctl bad_mark; 1301d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm fdm; 1311d6b1e46SJorge Ramirez-Ortiz u32 spare_per_sector; 1321d6b1e46SJorge Ramirez-Ortiz 1331d6b1e46SJorge Ramirez-Ortiz int nsels; 13449f1c330SGustavo A. R. Silva u8 sels[]; 1351d6b1e46SJorge Ramirez-Ortiz /* nothing after this field */ 1361d6b1e46SJorge Ramirez-Ortiz }; 1371d6b1e46SJorge Ramirez-Ortiz 1381d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_clk { 1391d6b1e46SJorge Ramirez-Ortiz struct clk *nfi_clk; 1401d6b1e46SJorge Ramirez-Ortiz struct clk *pad_clk; 1411d6b1e46SJorge Ramirez-Ortiz }; 1421d6b1e46SJorge Ramirez-Ortiz 1431d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc { 1447da45139SMiquel Raynal struct nand_controller controller; 1451d6b1e46SJorge Ramirez-Ortiz struct mtk_ecc_config ecc_cfg; 1461d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_clk clk; 1471d6b1e46SJorge Ramirez-Ortiz struct mtk_ecc *ecc; 1481d6b1e46SJorge Ramirez-Ortiz 1491d6b1e46SJorge Ramirez-Ortiz struct device *dev; 1507ec4a37cSXiaolei Li const struct mtk_nfc_caps *caps; 1511d6b1e46SJorge Ramirez-Ortiz void __iomem *regs; 1521d6b1e46SJorge Ramirez-Ortiz 1531d6b1e46SJorge Ramirez-Ortiz struct completion done; 1541d6b1e46SJorge Ramirez-Ortiz struct list_head chips; 1551d6b1e46SJorge Ramirez-Ortiz 1561d6b1e46SJorge Ramirez-Ortiz u8 *buffer; 1578dbd7b10SXiaolei Li 1588dbd7b10SXiaolei Li unsigned long assigned_cs; 1591d6b1e46SJorge Ramirez-Ortiz }; 1601d6b1e46SJorge Ramirez-Ortiz 1617ec4a37cSXiaolei Li /* 1627ec4a37cSXiaolei Li * supported spare size of each IP. 1637ec4a37cSXiaolei Li * order should be the same with the spare size bitfiled defination of 1647ec4a37cSXiaolei Li * register NFI_PAGEFMT. 1657ec4a37cSXiaolei Li */ 1667ec4a37cSXiaolei Li static const u8 spare_size_mt2701[] = { 1677ec4a37cSXiaolei Li 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 63, 64 1687ec4a37cSXiaolei Li }; 1697ec4a37cSXiaolei Li 17030ee809eSXiaolei Li static const u8 spare_size_mt2712[] = { 17130ee809eSXiaolei Li 16, 26, 27, 28, 32, 36, 40, 44, 48, 49, 50, 51, 52, 62, 61, 63, 64, 67, 17230ee809eSXiaolei Li 74 17330ee809eSXiaolei Li }; 17430ee809eSXiaolei Li 17598dea8d7SRogerCC Lin static const u8 spare_size_mt7622[] = { 17698dea8d7SRogerCC Lin 16, 26, 27, 28 17798dea8d7SRogerCC Lin }; 17898dea8d7SRogerCC Lin 1791d6b1e46SJorge Ramirez-Ortiz static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand) 1801d6b1e46SJorge Ramirez-Ortiz { 1811d6b1e46SJorge Ramirez-Ortiz return container_of(nand, struct mtk_nfc_nand_chip, nand); 1821d6b1e46SJorge Ramirez-Ortiz } 1831d6b1e46SJorge Ramirez-Ortiz 1841d6b1e46SJorge Ramirez-Ortiz static inline u8 *data_ptr(struct nand_chip *chip, const u8 *p, int i) 1851d6b1e46SJorge Ramirez-Ortiz { 1861d6b1e46SJorge Ramirez-Ortiz return (u8 *)p + i * chip->ecc.size; 1871d6b1e46SJorge Ramirez-Ortiz } 1881d6b1e46SJorge Ramirez-Ortiz 1891d6b1e46SJorge Ramirez-Ortiz static inline u8 *oob_ptr(struct nand_chip *chip, int i) 1901d6b1e46SJorge Ramirez-Ortiz { 1911d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 1921d6b1e46SJorge Ramirez-Ortiz u8 *poi; 1931d6b1e46SJorge Ramirez-Ortiz 1941d6b1e46SJorge Ramirez-Ortiz /* map the sector's FDM data to free oob: 1951d6b1e46SJorge Ramirez-Ortiz * the beginning of the oob area stores the FDM data of bad mark sectors 1961d6b1e46SJorge Ramirez-Ortiz */ 1971d6b1e46SJorge Ramirez-Ortiz 1981d6b1e46SJorge Ramirez-Ortiz if (i < mtk_nand->bad_mark.sec) 1991d6b1e46SJorge Ramirez-Ortiz poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size; 2001d6b1e46SJorge Ramirez-Ortiz else if (i == mtk_nand->bad_mark.sec) 2011d6b1e46SJorge Ramirez-Ortiz poi = chip->oob_poi; 2021d6b1e46SJorge Ramirez-Ortiz else 2031d6b1e46SJorge Ramirez-Ortiz poi = chip->oob_poi + i * mtk_nand->fdm.reg_size; 2041d6b1e46SJorge Ramirez-Ortiz 2051d6b1e46SJorge Ramirez-Ortiz return poi; 2061d6b1e46SJorge Ramirez-Ortiz } 2071d6b1e46SJorge Ramirez-Ortiz 2081d6b1e46SJorge Ramirez-Ortiz static inline int mtk_data_len(struct nand_chip *chip) 2091d6b1e46SJorge Ramirez-Ortiz { 2101d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 2111d6b1e46SJorge Ramirez-Ortiz 2121d6b1e46SJorge Ramirez-Ortiz return chip->ecc.size + mtk_nand->spare_per_sector; 2131d6b1e46SJorge Ramirez-Ortiz } 2141d6b1e46SJorge Ramirez-Ortiz 2151d6b1e46SJorge Ramirez-Ortiz static inline u8 *mtk_data_ptr(struct nand_chip *chip, int i) 2161d6b1e46SJorge Ramirez-Ortiz { 2171d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 2181d6b1e46SJorge Ramirez-Ortiz 2191d6b1e46SJorge Ramirez-Ortiz return nfc->buffer + i * mtk_data_len(chip); 2201d6b1e46SJorge Ramirez-Ortiz } 2211d6b1e46SJorge Ramirez-Ortiz 2221d6b1e46SJorge Ramirez-Ortiz static inline u8 *mtk_oob_ptr(struct nand_chip *chip, int i) 2231d6b1e46SJorge Ramirez-Ortiz { 2241d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 2251d6b1e46SJorge Ramirez-Ortiz 2261d6b1e46SJorge Ramirez-Ortiz return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size; 2271d6b1e46SJorge Ramirez-Ortiz } 2281d6b1e46SJorge Ramirez-Ortiz 2291d6b1e46SJorge Ramirez-Ortiz static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg) 2301d6b1e46SJorge Ramirez-Ortiz { 2311d6b1e46SJorge Ramirez-Ortiz writel(val, nfc->regs + reg); 2321d6b1e46SJorge Ramirez-Ortiz } 2331d6b1e46SJorge Ramirez-Ortiz 2341d6b1e46SJorge Ramirez-Ortiz static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg) 2351d6b1e46SJorge Ramirez-Ortiz { 2361d6b1e46SJorge Ramirez-Ortiz writew(val, nfc->regs + reg); 2371d6b1e46SJorge Ramirez-Ortiz } 2381d6b1e46SJorge Ramirez-Ortiz 2391d6b1e46SJorge Ramirez-Ortiz static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg) 2401d6b1e46SJorge Ramirez-Ortiz { 2411d6b1e46SJorge Ramirez-Ortiz writeb(val, nfc->regs + reg); 2421d6b1e46SJorge Ramirez-Ortiz } 2431d6b1e46SJorge Ramirez-Ortiz 2441d6b1e46SJorge Ramirez-Ortiz static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg) 2451d6b1e46SJorge Ramirez-Ortiz { 2461d6b1e46SJorge Ramirez-Ortiz return readl_relaxed(nfc->regs + reg); 2471d6b1e46SJorge Ramirez-Ortiz } 2481d6b1e46SJorge Ramirez-Ortiz 2491d6b1e46SJorge Ramirez-Ortiz static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg) 2501d6b1e46SJorge Ramirez-Ortiz { 2511d6b1e46SJorge Ramirez-Ortiz return readw_relaxed(nfc->regs + reg); 2521d6b1e46SJorge Ramirez-Ortiz } 2531d6b1e46SJorge Ramirez-Ortiz 2541d6b1e46SJorge Ramirez-Ortiz static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg) 2551d6b1e46SJorge Ramirez-Ortiz { 2561d6b1e46SJorge Ramirez-Ortiz return readb_relaxed(nfc->regs + reg); 2571d6b1e46SJorge Ramirez-Ortiz } 2581d6b1e46SJorge Ramirez-Ortiz 2591d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_hw_reset(struct mtk_nfc *nfc) 2601d6b1e46SJorge Ramirez-Ortiz { 2611d6b1e46SJorge Ramirez-Ortiz struct device *dev = nfc->dev; 2621d6b1e46SJorge Ramirez-Ortiz u32 val; 2631d6b1e46SJorge Ramirez-Ortiz int ret; 2641d6b1e46SJorge Ramirez-Ortiz 2651d6b1e46SJorge Ramirez-Ortiz /* reset all registers and force the NFI master to terminate */ 2661d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); 2671d6b1e46SJorge Ramirez-Ortiz 2681d6b1e46SJorge Ramirez-Ortiz /* wait for the master to finish the last transaction */ 2691d6b1e46SJorge Ramirez-Ortiz ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val, 2701d6b1e46SJorge Ramirez-Ortiz !(val & MASTER_STA_MASK), 50, 2711d6b1e46SJorge Ramirez-Ortiz MTK_RESET_TIMEOUT); 2721d6b1e46SJorge Ramirez-Ortiz if (ret) 2731d6b1e46SJorge Ramirez-Ortiz dev_warn(dev, "master active in reset [0x%x] = 0x%x\n", 2741d6b1e46SJorge Ramirez-Ortiz NFI_MASTER_STA, val); 2751d6b1e46SJorge Ramirez-Ortiz 2761d6b1e46SJorge Ramirez-Ortiz /* ensure any status register affected by the NFI master is reset */ 2771d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON); 2781d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, STAR_DE, NFI_STRDATA); 2791d6b1e46SJorge Ramirez-Ortiz } 2801d6b1e46SJorge Ramirez-Ortiz 2811d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command) 2821d6b1e46SJorge Ramirez-Ortiz { 2831d6b1e46SJorge Ramirez-Ortiz struct device *dev = nfc->dev; 2841d6b1e46SJorge Ramirez-Ortiz u32 val; 2851d6b1e46SJorge Ramirez-Ortiz int ret; 2861d6b1e46SJorge Ramirez-Ortiz 2871d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, command, NFI_CMD); 2881d6b1e46SJorge Ramirez-Ortiz 2891d6b1e46SJorge Ramirez-Ortiz ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, 2901d6b1e46SJorge Ramirez-Ortiz !(val & STA_CMD), 10, MTK_TIMEOUT); 2911d6b1e46SJorge Ramirez-Ortiz if (ret) { 2921d6b1e46SJorge Ramirez-Ortiz dev_warn(dev, "nfi core timed out entering command mode\n"); 2931d6b1e46SJorge Ramirez-Ortiz return -EIO; 2941d6b1e46SJorge Ramirez-Ortiz } 2951d6b1e46SJorge Ramirez-Ortiz 2961d6b1e46SJorge Ramirez-Ortiz return 0; 2971d6b1e46SJorge Ramirez-Ortiz } 2981d6b1e46SJorge Ramirez-Ortiz 2991d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr) 3001d6b1e46SJorge Ramirez-Ortiz { 3011d6b1e46SJorge Ramirez-Ortiz struct device *dev = nfc->dev; 3021d6b1e46SJorge Ramirez-Ortiz u32 val; 3031d6b1e46SJorge Ramirez-Ortiz int ret; 3041d6b1e46SJorge Ramirez-Ortiz 3051d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, addr, NFI_COLADDR); 3061d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, 0, NFI_ROWADDR); 3071d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, 1, NFI_ADDRNOB); 3081d6b1e46SJorge Ramirez-Ortiz 3091d6b1e46SJorge Ramirez-Ortiz ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val, 3101d6b1e46SJorge Ramirez-Ortiz !(val & STA_ADDR), 10, MTK_TIMEOUT); 3111d6b1e46SJorge Ramirez-Ortiz if (ret) { 3121d6b1e46SJorge Ramirez-Ortiz dev_warn(dev, "nfi core timed out entering address mode\n"); 3131d6b1e46SJorge Ramirez-Ortiz return -EIO; 3141d6b1e46SJorge Ramirez-Ortiz } 3151d6b1e46SJorge Ramirez-Ortiz 3161d6b1e46SJorge Ramirez-Ortiz return 0; 3171d6b1e46SJorge Ramirez-Ortiz } 3181d6b1e46SJorge Ramirez-Ortiz 3191d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd) 3201d6b1e46SJorge Ramirez-Ortiz { 3211d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 3221d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 3231d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 3247ec4a37cSXiaolei Li u32 fmt, spare, i; 3251d6b1e46SJorge Ramirez-Ortiz 3261d6b1e46SJorge Ramirez-Ortiz if (!mtd->writesize) 3271d6b1e46SJorge Ramirez-Ortiz return 0; 3281d6b1e46SJorge Ramirez-Ortiz 3291d6b1e46SJorge Ramirez-Ortiz spare = mtk_nand->spare_per_sector; 3301d6b1e46SJorge Ramirez-Ortiz 3311d6b1e46SJorge Ramirez-Ortiz switch (mtd->writesize) { 3321d6b1e46SJorge Ramirez-Ortiz case 512: 3331d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512; 3341d6b1e46SJorge Ramirez-Ortiz break; 3351d6b1e46SJorge Ramirez-Ortiz case KB(2): 3361d6b1e46SJorge Ramirez-Ortiz if (chip->ecc.size == 512) 3371d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512; 3381d6b1e46SJorge Ramirez-Ortiz else 3391d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_512_2K; 3401d6b1e46SJorge Ramirez-Ortiz break; 3411d6b1e46SJorge Ramirez-Ortiz case KB(4): 3421d6b1e46SJorge Ramirez-Ortiz if (chip->ecc.size == 512) 3431d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512; 3441d6b1e46SJorge Ramirez-Ortiz else 3451d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_2K_4K; 3461d6b1e46SJorge Ramirez-Ortiz break; 3471d6b1e46SJorge Ramirez-Ortiz case KB(8): 3481d6b1e46SJorge Ramirez-Ortiz if (chip->ecc.size == 512) 3491d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512; 3501d6b1e46SJorge Ramirez-Ortiz else 3511d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_4K_8K; 3521d6b1e46SJorge Ramirez-Ortiz break; 3531d6b1e46SJorge Ramirez-Ortiz case KB(16): 3541d6b1e46SJorge Ramirez-Ortiz fmt = PAGEFMT_8K_16K; 3551d6b1e46SJorge Ramirez-Ortiz break; 3561d6b1e46SJorge Ramirez-Ortiz default: 3571d6b1e46SJorge Ramirez-Ortiz dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize); 3581d6b1e46SJorge Ramirez-Ortiz return -EINVAL; 3591d6b1e46SJorge Ramirez-Ortiz } 3601d6b1e46SJorge Ramirez-Ortiz 3611d6b1e46SJorge Ramirez-Ortiz /* 3621d6b1e46SJorge Ramirez-Ortiz * the hardware will double the value for this eccsize, so we need to 3631d6b1e46SJorge Ramirez-Ortiz * halve it 3641d6b1e46SJorge Ramirez-Ortiz */ 3651d6b1e46SJorge Ramirez-Ortiz if (chip->ecc.size == 1024) 3661d6b1e46SJorge Ramirez-Ortiz spare >>= 1; 3671d6b1e46SJorge Ramirez-Ortiz 3687ec4a37cSXiaolei Li for (i = 0; i < nfc->caps->num_spare_size; i++) { 3697ec4a37cSXiaolei Li if (nfc->caps->spare_size[i] == spare) 3701d6b1e46SJorge Ramirez-Ortiz break; 3717ec4a37cSXiaolei Li } 3727ec4a37cSXiaolei Li 3737ec4a37cSXiaolei Li if (i == nfc->caps->num_spare_size) { 3747ec4a37cSXiaolei Li dev_err(nfc->dev, "invalid spare size %d\n", spare); 3751d6b1e46SJorge Ramirez-Ortiz return -EINVAL; 3761d6b1e46SJorge Ramirez-Ortiz } 3771d6b1e46SJorge Ramirez-Ortiz 3787ec4a37cSXiaolei Li fmt |= i << nfc->caps->pageformat_spare_shift; 3797ec4a37cSXiaolei Li 3801d6b1e46SJorge Ramirez-Ortiz fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT; 3811d6b1e46SJorge Ramirez-Ortiz fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT; 382582212ceSXiaolei Li nfi_writel(nfc, fmt, NFI_PAGEFMT); 3831d6b1e46SJorge Ramirez-Ortiz 3841d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.strength = chip->ecc.strength; 3851d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size; 3861d6b1e46SJorge Ramirez-Ortiz 3871d6b1e46SJorge Ramirez-Ortiz return 0; 3881d6b1e46SJorge Ramirez-Ortiz } 3891d6b1e46SJorge Ramirez-Ortiz 3901d6b1e46SJorge Ramirez-Ortiz static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc) 3911d6b1e46SJorge Ramirez-Ortiz { 3921d6b1e46SJorge Ramirez-Ortiz int rc; 3931d6b1e46SJorge Ramirez-Ortiz u8 val; 3941d6b1e46SJorge Ramirez-Ortiz 3951d6b1e46SJorge Ramirez-Ortiz rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val, 3961d6b1e46SJorge Ramirez-Ortiz val & PIO_DI_RDY, 10, MTK_TIMEOUT); 3971d6b1e46SJorge Ramirez-Ortiz if (rc < 0) 3981d6b1e46SJorge Ramirez-Ortiz dev_err(nfc->dev, "data not ready\n"); 3991d6b1e46SJorge Ramirez-Ortiz } 4001d6b1e46SJorge Ramirez-Ortiz 4017e534323SBoris Brezillon static inline u8 mtk_nfc_read_byte(struct nand_chip *chip) 4021d6b1e46SJorge Ramirez-Ortiz { 4031d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 4041d6b1e46SJorge Ramirez-Ortiz u32 reg; 4051d6b1e46SJorge Ramirez-Ortiz 4061d6b1e46SJorge Ramirez-Ortiz /* after each byte read, the NFI_STA reg is reset by the hardware */ 4071d6b1e46SJorge Ramirez-Ortiz reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; 4081d6b1e46SJorge Ramirez-Ortiz if (reg != NFI_FSM_CUSTDATA) { 4091d6b1e46SJorge Ramirez-Ortiz reg = nfi_readw(nfc, NFI_CNFG); 4101d6b1e46SJorge Ramirez-Ortiz reg |= CNFG_BYTE_RW | CNFG_READ_EN; 4111d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 4121d6b1e46SJorge Ramirez-Ortiz 4131d6b1e46SJorge Ramirez-Ortiz /* 4141d6b1e46SJorge Ramirez-Ortiz * set to max sector to allow the HW to continue reading over 4151d6b1e46SJorge Ramirez-Ortiz * unaligned accesses 4161d6b1e46SJorge Ramirez-Ortiz */ 417b45ee550SRogerCC Lin reg = (nfc->caps->max_sector << CON_SEC_SHIFT) | CON_BRD; 4181d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, reg, NFI_CON); 4191d6b1e46SJorge Ramirez-Ortiz 4201d6b1e46SJorge Ramirez-Ortiz /* trigger to fetch data */ 4211d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, STAR_EN, NFI_STRDATA); 4221d6b1e46SJorge Ramirez-Ortiz } 4231d6b1e46SJorge Ramirez-Ortiz 4241d6b1e46SJorge Ramirez-Ortiz mtk_nfc_wait_ioready(nfc); 4251d6b1e46SJorge Ramirez-Ortiz 4261d6b1e46SJorge Ramirez-Ortiz return nfi_readb(nfc, NFI_DATAR); 4271d6b1e46SJorge Ramirez-Ortiz } 4281d6b1e46SJorge Ramirez-Ortiz 4297e534323SBoris Brezillon static void mtk_nfc_read_buf(struct nand_chip *chip, u8 *buf, int len) 4301d6b1e46SJorge Ramirez-Ortiz { 4311d6b1e46SJorge Ramirez-Ortiz int i; 4321d6b1e46SJorge Ramirez-Ortiz 4331d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < len; i++) 4347e534323SBoris Brezillon buf[i] = mtk_nfc_read_byte(chip); 4351d6b1e46SJorge Ramirez-Ortiz } 4361d6b1e46SJorge Ramirez-Ortiz 437c0739d85SBoris Brezillon static void mtk_nfc_write_byte(struct nand_chip *chip, u8 byte) 4381d6b1e46SJorge Ramirez-Ortiz { 439c0739d85SBoris Brezillon struct mtk_nfc *nfc = nand_get_controller_data(chip); 4401d6b1e46SJorge Ramirez-Ortiz u32 reg; 4411d6b1e46SJorge Ramirez-Ortiz 4421d6b1e46SJorge Ramirez-Ortiz reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK; 4431d6b1e46SJorge Ramirez-Ortiz 4441d6b1e46SJorge Ramirez-Ortiz if (reg != NFI_FSM_CUSTDATA) { 4451d6b1e46SJorge Ramirez-Ortiz reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW; 4461d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 4471d6b1e46SJorge Ramirez-Ortiz 448b45ee550SRogerCC Lin reg = nfc->caps->max_sector << CON_SEC_SHIFT | CON_BWR; 4491d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, reg, NFI_CON); 4501d6b1e46SJorge Ramirez-Ortiz 4511d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, STAR_EN, NFI_STRDATA); 4521d6b1e46SJorge Ramirez-Ortiz } 4531d6b1e46SJorge Ramirez-Ortiz 4541d6b1e46SJorge Ramirez-Ortiz mtk_nfc_wait_ioready(nfc); 4551d6b1e46SJorge Ramirez-Ortiz nfi_writeb(nfc, byte, NFI_DATAW); 4561d6b1e46SJorge Ramirez-Ortiz } 4571d6b1e46SJorge Ramirez-Ortiz 458c0739d85SBoris Brezillon static void mtk_nfc_write_buf(struct nand_chip *chip, const u8 *buf, int len) 4591d6b1e46SJorge Ramirez-Ortiz { 4601d6b1e46SJorge Ramirez-Ortiz int i; 4611d6b1e46SJorge Ramirez-Ortiz 4621d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < len; i++) 463c0739d85SBoris Brezillon mtk_nfc_write_byte(chip, buf[i]); 4641d6b1e46SJorge Ramirez-Ortiz } 4651d6b1e46SJorge Ramirez-Ortiz 4665197360fSBoris Brezillon static int mtk_nfc_exec_instr(struct nand_chip *chip, 4675197360fSBoris Brezillon const struct nand_op_instr *instr) 4685197360fSBoris Brezillon { 4695197360fSBoris Brezillon struct mtk_nfc *nfc = nand_get_controller_data(chip); 4705197360fSBoris Brezillon unsigned int i; 4715197360fSBoris Brezillon u32 status; 4725197360fSBoris Brezillon 4735197360fSBoris Brezillon switch (instr->type) { 4745197360fSBoris Brezillon case NAND_OP_CMD_INSTR: 4755197360fSBoris Brezillon mtk_nfc_send_command(nfc, instr->ctx.cmd.opcode); 4765197360fSBoris Brezillon return 0; 4775197360fSBoris Brezillon case NAND_OP_ADDR_INSTR: 4785197360fSBoris Brezillon for (i = 0; i < instr->ctx.addr.naddrs; i++) 4795197360fSBoris Brezillon mtk_nfc_send_address(nfc, instr->ctx.addr.addrs[i]); 4805197360fSBoris Brezillon return 0; 4815197360fSBoris Brezillon case NAND_OP_DATA_IN_INSTR: 4825197360fSBoris Brezillon mtk_nfc_read_buf(chip, instr->ctx.data.buf.in, 4835197360fSBoris Brezillon instr->ctx.data.len); 4845197360fSBoris Brezillon return 0; 4855197360fSBoris Brezillon case NAND_OP_DATA_OUT_INSTR: 4865197360fSBoris Brezillon mtk_nfc_write_buf(chip, instr->ctx.data.buf.out, 4875197360fSBoris Brezillon instr->ctx.data.len); 4885197360fSBoris Brezillon return 0; 4895197360fSBoris Brezillon case NAND_OP_WAITRDY_INSTR: 4905197360fSBoris Brezillon return readl_poll_timeout(nfc->regs + NFI_STA, status, 491*2fb164f0SHauke Mehrtens !(status & STA_BUSY), 20, 492*2fb164f0SHauke Mehrtens instr->ctx.waitrdy.timeout_ms * 1000); 4935197360fSBoris Brezillon default: 4945197360fSBoris Brezillon break; 4955197360fSBoris Brezillon } 4965197360fSBoris Brezillon 4975197360fSBoris Brezillon return -EINVAL; 4985197360fSBoris Brezillon } 4995197360fSBoris Brezillon 5005197360fSBoris Brezillon static void mtk_nfc_select_target(struct nand_chip *nand, unsigned int cs) 5015197360fSBoris Brezillon { 5025197360fSBoris Brezillon struct mtk_nfc *nfc = nand_get_controller_data(nand); 5035197360fSBoris Brezillon struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand); 5045197360fSBoris Brezillon 5055197360fSBoris Brezillon mtk_nfc_hw_runtime_config(nand_to_mtd(nand)); 5065197360fSBoris Brezillon 5075197360fSBoris Brezillon nfi_writel(nfc, mtk_nand->sels[cs], NFI_CSEL); 5085197360fSBoris Brezillon } 5095197360fSBoris Brezillon 5105197360fSBoris Brezillon static int mtk_nfc_exec_op(struct nand_chip *chip, 5115197360fSBoris Brezillon const struct nand_operation *op, 5125197360fSBoris Brezillon bool check_only) 5135197360fSBoris Brezillon { 5145197360fSBoris Brezillon struct mtk_nfc *nfc = nand_get_controller_data(chip); 5155197360fSBoris Brezillon unsigned int i; 5165197360fSBoris Brezillon int ret = 0; 5175197360fSBoris Brezillon 5185197360fSBoris Brezillon if (check_only) 5195197360fSBoris Brezillon return 0; 5205197360fSBoris Brezillon 5215197360fSBoris Brezillon mtk_nfc_hw_reset(nfc); 5225197360fSBoris Brezillon nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG); 5235197360fSBoris Brezillon mtk_nfc_select_target(chip, op->cs); 5245197360fSBoris Brezillon 5255197360fSBoris Brezillon for (i = 0; i < op->ninstrs; i++) { 5265197360fSBoris Brezillon ret = mtk_nfc_exec_instr(chip, &op->instrs[i]); 5275197360fSBoris Brezillon if (ret) 5285197360fSBoris Brezillon break; 5295197360fSBoris Brezillon } 5305197360fSBoris Brezillon 5315197360fSBoris Brezillon return ret; 5325197360fSBoris Brezillon } 5335197360fSBoris Brezillon 5344c46667bSMiquel Raynal static int mtk_nfc_setup_interface(struct nand_chip *chip, int csline, 5354c46667bSMiquel Raynal const struct nand_interface_config *conf) 536edfee361SXiaolei Li { 537858838b8SBoris Brezillon struct mtk_nfc *nfc = nand_get_controller_data(chip); 538edfee361SXiaolei Li const struct nand_sdr_timings *timings; 539e1884ffdSXiaolei Li u32 rate, tpoecs, tprecs, tc2r, tw2r, twh, twst = 0, trlt = 0; 54042d13a09SXiaolei Li u32 temp, tsel = 0; 541edfee361SXiaolei Li 542edfee361SXiaolei Li timings = nand_get_sdr_timings(conf); 543edfee361SXiaolei Li if (IS_ERR(timings)) 544edfee361SXiaolei Li return -ENOTSUPP; 545edfee361SXiaolei Li 546edfee361SXiaolei Li if (csline == NAND_DATA_IFACE_CHECK_ONLY) 547edfee361SXiaolei Li return 0; 548edfee361SXiaolei Li 549edfee361SXiaolei Li rate = clk_get_rate(nfc->clk.nfi_clk); 550edfee361SXiaolei Li /* There is a frequency divider in some IPs */ 551edfee361SXiaolei Li rate /= nfc->caps->nfi_clk_div; 552edfee361SXiaolei Li 553edfee361SXiaolei Li /* turn clock rate into KHZ */ 554edfee361SXiaolei Li rate /= 1000; 555edfee361SXiaolei Li 556edfee361SXiaolei Li tpoecs = max(timings->tALH_min, timings->tCLH_min) / 1000; 557edfee361SXiaolei Li tpoecs = DIV_ROUND_UP(tpoecs * rate, 1000000); 558edfee361SXiaolei Li tpoecs &= 0xf; 559edfee361SXiaolei Li 560edfee361SXiaolei Li tprecs = max(timings->tCLS_min, timings->tALS_min) / 1000; 561edfee361SXiaolei Li tprecs = DIV_ROUND_UP(tprecs * rate, 1000000); 562edfee361SXiaolei Li tprecs &= 0x3f; 563edfee361SXiaolei Li 564edfee361SXiaolei Li /* sdr interface has no tCR which means CE# low to RE# low */ 565edfee361SXiaolei Li tc2r = 0; 566edfee361SXiaolei Li 567edfee361SXiaolei Li tw2r = timings->tWHR_min / 1000; 568edfee361SXiaolei Li tw2r = DIV_ROUND_UP(tw2r * rate, 1000000); 569edfee361SXiaolei Li tw2r = DIV_ROUND_UP(tw2r - 1, 2); 570edfee361SXiaolei Li tw2r &= 0xf; 571edfee361SXiaolei Li 572edfee361SXiaolei Li twh = max(timings->tREH_min, timings->tWH_min) / 1000; 573edfee361SXiaolei Li twh = DIV_ROUND_UP(twh * rate, 1000000) - 1; 574edfee361SXiaolei Li twh &= 0xf; 575edfee361SXiaolei Li 576e1884ffdSXiaolei Li /* Calculate real WE#/RE# hold time in nanosecond */ 57742d13a09SXiaolei Li temp = (twh + 1) * 1000000 / rate; 578e1884ffdSXiaolei Li /* nanosecond to picosecond */ 57942d13a09SXiaolei Li temp *= 1000; 580e1884ffdSXiaolei Li 581e1884ffdSXiaolei Li /* 582e1884ffdSXiaolei Li * WE# low level time should be expaned to meet WE# pulse time 583e1884ffdSXiaolei Li * and WE# cycle time at the same time. 584e1884ffdSXiaolei Li */ 58542d13a09SXiaolei Li if (temp < timings->tWC_min) 58642d13a09SXiaolei Li twst = timings->tWC_min - temp; 587e1884ffdSXiaolei Li twst = max(timings->tWP_min, twst) / 1000; 588edfee361SXiaolei Li twst = DIV_ROUND_UP(twst * rate, 1000000) - 1; 589edfee361SXiaolei Li twst &= 0xf; 590edfee361SXiaolei Li 591e1884ffdSXiaolei Li /* 59242d13a09SXiaolei Li * RE# low level time should be expaned to meet RE# pulse time 59342d13a09SXiaolei Li * and RE# cycle time at the same time. 594e1884ffdSXiaolei Li */ 59542d13a09SXiaolei Li if (temp < timings->tRC_min) 59642d13a09SXiaolei Li trlt = timings->tRC_min - temp; 59742d13a09SXiaolei Li trlt = max(trlt, timings->tRP_min) / 1000; 598edfee361SXiaolei Li trlt = DIV_ROUND_UP(trlt * rate, 1000000) - 1; 599edfee361SXiaolei Li trlt &= 0xf; 600edfee361SXiaolei Li 60142d13a09SXiaolei Li /* Calculate RE# pulse time in nanosecond. */ 60242d13a09SXiaolei Li temp = (trlt + 1) * 1000000 / rate; 60342d13a09SXiaolei Li /* nanosecond to picosecond */ 60442d13a09SXiaolei Li temp *= 1000; 60542d13a09SXiaolei Li /* 60642d13a09SXiaolei Li * If RE# access time is bigger than RE# pulse time, 60742d13a09SXiaolei Li * delay sampling data timing. 60842d13a09SXiaolei Li */ 60942d13a09SXiaolei Li if (temp < timings->tREA_max) { 61042d13a09SXiaolei Li tsel = timings->tREA_max / 1000; 61142d13a09SXiaolei Li tsel = DIV_ROUND_UP(tsel * rate, 1000000); 61242d13a09SXiaolei Li tsel -= (trlt + 1); 61342d13a09SXiaolei Li if (tsel > MAX_STROBE_DLY) { 61442d13a09SXiaolei Li trlt += tsel - MAX_STROBE_DLY; 61542d13a09SXiaolei Li tsel = MAX_STROBE_DLY; 61642d13a09SXiaolei Li } 61742d13a09SXiaolei Li } 61842d13a09SXiaolei Li temp = nfi_readl(nfc, NFI_DEBUG_CON1); 61942d13a09SXiaolei Li temp &= ~STROBE_MASK; 62042d13a09SXiaolei Li temp |= tsel << STROBE_SHIFT; 62142d13a09SXiaolei Li nfi_writel(nfc, temp, NFI_DEBUG_CON1); 62242d13a09SXiaolei Li 623edfee361SXiaolei Li /* 624edfee361SXiaolei Li * ACCON: access timing control register 625edfee361SXiaolei Li * ------------------------------------- 626edfee361SXiaolei Li * 31:28: tpoecs, minimum required time for CS post pulling down after 627edfee361SXiaolei Li * accessing the device 628edfee361SXiaolei Li * 27:22: tprecs, minimum required time for CS pre pulling down before 629edfee361SXiaolei Li * accessing the device 630edfee361SXiaolei Li * 21:16: tc2r, minimum required time from NCEB low to NREB low 631edfee361SXiaolei Li * 15:12: tw2r, minimum required time from NWEB high to NREB low. 632edfee361SXiaolei Li * 11:08: twh, write enable hold time 633edfee361SXiaolei Li * 07:04: twst, write wait states 634edfee361SXiaolei Li * 03:00: trlt, read wait states 635edfee361SXiaolei Li */ 636edfee361SXiaolei Li trlt = ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt); 637edfee361SXiaolei Li nfi_writel(nfc, trlt, NFI_ACCCON); 638edfee361SXiaolei Li 639edfee361SXiaolei Li return 0; 640edfee361SXiaolei Li } 641edfee361SXiaolei Li 6421d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data) 6431d6b1e46SJorge Ramirez-Ortiz { 6441d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 6451d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 6461d6b1e46SJorge Ramirez-Ortiz int size = chip->ecc.size + mtk_nand->fdm.reg_size; 6471d6b1e46SJorge Ramirez-Ortiz 6481d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.mode = ECC_DMA_MODE; 6491d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.op = ECC_ENCODE; 6501d6b1e46SJorge Ramirez-Ortiz 6511d6b1e46SJorge Ramirez-Ortiz return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size); 6521d6b1e46SJorge Ramirez-Ortiz } 6531d6b1e46SJorge Ramirez-Ortiz 6541d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, u8 *b, int c) 6551d6b1e46SJorge Ramirez-Ortiz { 6561d6b1e46SJorge Ramirez-Ortiz /* nop */ 6571d6b1e46SJorge Ramirez-Ortiz } 6581d6b1e46SJorge Ramirez-Ortiz 6591d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, u8 *buf, int raw) 6601d6b1e46SJorge Ramirez-Ortiz { 6611d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 6621d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip); 6631d6b1e46SJorge Ramirez-Ortiz u32 bad_pos = nand->bad_mark.pos; 6641d6b1e46SJorge Ramirez-Ortiz 6651d6b1e46SJorge Ramirez-Ortiz if (raw) 6661d6b1e46SJorge Ramirez-Ortiz bad_pos += nand->bad_mark.sec * mtk_data_len(chip); 6671d6b1e46SJorge Ramirez-Ortiz else 6681d6b1e46SJorge Ramirez-Ortiz bad_pos += nand->bad_mark.sec * chip->ecc.size; 6691d6b1e46SJorge Ramirez-Ortiz 6701d6b1e46SJorge Ramirez-Ortiz swap(chip->oob_poi[0], buf[bad_pos]); 6711d6b1e46SJorge Ramirez-Ortiz } 6721d6b1e46SJorge Ramirez-Ortiz 6731d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_format_subpage(struct mtd_info *mtd, u32 offset, 6741d6b1e46SJorge Ramirez-Ortiz u32 len, const u8 *buf) 6751d6b1e46SJorge Ramirez-Ortiz { 6761d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 6771d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 6781d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 6791d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; 6801d6b1e46SJorge Ramirez-Ortiz u32 start, end; 6811d6b1e46SJorge Ramirez-Ortiz int i, ret; 6821d6b1e46SJorge Ramirez-Ortiz 6831d6b1e46SJorge Ramirez-Ortiz start = offset / chip->ecc.size; 6841d6b1e46SJorge Ramirez-Ortiz end = DIV_ROUND_UP(offset + len, chip->ecc.size); 6851d6b1e46SJorge Ramirez-Ortiz 6861d6b1e46SJorge Ramirez-Ortiz memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); 6871d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < chip->ecc.steps; i++) { 6881d6b1e46SJorge Ramirez-Ortiz memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), 6891d6b1e46SJorge Ramirez-Ortiz chip->ecc.size); 6901d6b1e46SJorge Ramirez-Ortiz 6911d6b1e46SJorge Ramirez-Ortiz if (start > i || i >= end) 6921d6b1e46SJorge Ramirez-Ortiz continue; 6931d6b1e46SJorge Ramirez-Ortiz 6941d6b1e46SJorge Ramirez-Ortiz if (i == mtk_nand->bad_mark.sec) 6951d6b1e46SJorge Ramirez-Ortiz mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); 6961d6b1e46SJorge Ramirez-Ortiz 6971d6b1e46SJorge Ramirez-Ortiz memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); 6981d6b1e46SJorge Ramirez-Ortiz 6991d6b1e46SJorge Ramirez-Ortiz /* program the CRC back to the OOB */ 7001d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i)); 7011d6b1e46SJorge Ramirez-Ortiz if (ret < 0) 7021d6b1e46SJorge Ramirez-Ortiz return ret; 7031d6b1e46SJorge Ramirez-Ortiz } 7041d6b1e46SJorge Ramirez-Ortiz 7051d6b1e46SJorge Ramirez-Ortiz return 0; 7061d6b1e46SJorge Ramirez-Ortiz } 7071d6b1e46SJorge Ramirez-Ortiz 7081d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_format_page(struct mtd_info *mtd, const u8 *buf) 7091d6b1e46SJorge Ramirez-Ortiz { 7101d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 7111d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 7121d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 7131d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; 7141d6b1e46SJorge Ramirez-Ortiz u32 i; 7151d6b1e46SJorge Ramirez-Ortiz 7161d6b1e46SJorge Ramirez-Ortiz memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); 7171d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < chip->ecc.steps; i++) { 7181d6b1e46SJorge Ramirez-Ortiz if (buf) 7191d6b1e46SJorge Ramirez-Ortiz memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i), 7201d6b1e46SJorge Ramirez-Ortiz chip->ecc.size); 7211d6b1e46SJorge Ramirez-Ortiz 7221d6b1e46SJorge Ramirez-Ortiz if (i == mtk_nand->bad_mark.sec) 7231d6b1e46SJorge Ramirez-Ortiz mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); 7241d6b1e46SJorge Ramirez-Ortiz 7251d6b1e46SJorge Ramirez-Ortiz memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size); 7261d6b1e46SJorge Ramirez-Ortiz } 7271d6b1e46SJorge Ramirez-Ortiz } 7281d6b1e46SJorge Ramirez-Ortiz 7291d6b1e46SJorge Ramirez-Ortiz static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start, 7301d6b1e46SJorge Ramirez-Ortiz u32 sectors) 7311d6b1e46SJorge Ramirez-Ortiz { 7321d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 7331d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 7341d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; 7351d6b1e46SJorge Ramirez-Ortiz u32 vall, valm; 7361d6b1e46SJorge Ramirez-Ortiz u8 *oobptr; 7371d6b1e46SJorge Ramirez-Ortiz int i, j; 7381d6b1e46SJorge Ramirez-Ortiz 7391d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < sectors; i++) { 7401d6b1e46SJorge Ramirez-Ortiz oobptr = oob_ptr(chip, start + i); 7411d6b1e46SJorge Ramirez-Ortiz vall = nfi_readl(nfc, NFI_FDML(i)); 7421d6b1e46SJorge Ramirez-Ortiz valm = nfi_readl(nfc, NFI_FDMM(i)); 7431d6b1e46SJorge Ramirez-Ortiz 7441d6b1e46SJorge Ramirez-Ortiz for (j = 0; j < fdm->reg_size; j++) 7451d6b1e46SJorge Ramirez-Ortiz oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8); 7461d6b1e46SJorge Ramirez-Ortiz } 7471d6b1e46SJorge Ramirez-Ortiz } 7481d6b1e46SJorge Ramirez-Ortiz 7491d6b1e46SJorge Ramirez-Ortiz static inline void mtk_nfc_write_fdm(struct nand_chip *chip) 7501d6b1e46SJorge Ramirez-Ortiz { 7511d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 7521d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 7531d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; 7541d6b1e46SJorge Ramirez-Ortiz u32 vall, valm; 7551d6b1e46SJorge Ramirez-Ortiz u8 *oobptr; 7561d6b1e46SJorge Ramirez-Ortiz int i, j; 7571d6b1e46SJorge Ramirez-Ortiz 7581d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < chip->ecc.steps; i++) { 7591d6b1e46SJorge Ramirez-Ortiz oobptr = oob_ptr(chip, i); 7601d6b1e46SJorge Ramirez-Ortiz vall = 0; 7611d6b1e46SJorge Ramirez-Ortiz valm = 0; 7621d6b1e46SJorge Ramirez-Ortiz for (j = 0; j < 8; j++) { 7631d6b1e46SJorge Ramirez-Ortiz if (j < 4) 7641d6b1e46SJorge Ramirez-Ortiz vall |= (j < fdm->reg_size ? oobptr[j] : 0xff) 7651d6b1e46SJorge Ramirez-Ortiz << (j * 8); 7661d6b1e46SJorge Ramirez-Ortiz else 7671d6b1e46SJorge Ramirez-Ortiz valm |= (j < fdm->reg_size ? oobptr[j] : 0xff) 7681d6b1e46SJorge Ramirez-Ortiz << ((j - 4) * 8); 7691d6b1e46SJorge Ramirez-Ortiz } 7701d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, vall, NFI_FDML(i)); 7711d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, valm, NFI_FDMM(i)); 7721d6b1e46SJorge Ramirez-Ortiz } 7731d6b1e46SJorge Ramirez-Ortiz } 7741d6b1e46SJorge Ramirez-Ortiz 7751d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip, 7761d6b1e46SJorge Ramirez-Ortiz const u8 *buf, int page, int len) 7771d6b1e46SJorge Ramirez-Ortiz { 7781d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 7791d6b1e46SJorge Ramirez-Ortiz struct device *dev = nfc->dev; 7801d6b1e46SJorge Ramirez-Ortiz dma_addr_t addr; 7811d6b1e46SJorge Ramirez-Ortiz u32 reg; 7821d6b1e46SJorge Ramirez-Ortiz int ret; 7831d6b1e46SJorge Ramirez-Ortiz 7841d6b1e46SJorge Ramirez-Ortiz addr = dma_map_single(dev, (void *)buf, len, DMA_TO_DEVICE); 7851d6b1e46SJorge Ramirez-Ortiz ret = dma_mapping_error(nfc->dev, addr); 7861d6b1e46SJorge Ramirez-Ortiz if (ret) { 7871d6b1e46SJorge Ramirez-Ortiz dev_err(nfc->dev, "dma mapping error\n"); 7881d6b1e46SJorge Ramirez-Ortiz return -EINVAL; 7891d6b1e46SJorge Ramirez-Ortiz } 7901d6b1e46SJorge Ramirez-Ortiz 7911d6b1e46SJorge Ramirez-Ortiz reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN; 7921d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 7931d6b1e46SJorge Ramirez-Ortiz 7941d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON); 7951d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); 7961d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); 7971d6b1e46SJorge Ramirez-Ortiz 7981d6b1e46SJorge Ramirez-Ortiz init_completion(&nfc->done); 7991d6b1e46SJorge Ramirez-Ortiz 8001d6b1e46SJorge Ramirez-Ortiz reg = nfi_readl(nfc, NFI_CON) | CON_BWR; 8011d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, reg, NFI_CON); 8021d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, STAR_EN, NFI_STRDATA); 8031d6b1e46SJorge Ramirez-Ortiz 8041d6b1e46SJorge Ramirez-Ortiz ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); 8051d6b1e46SJorge Ramirez-Ortiz if (!ret) { 8061d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "program ahb done timeout\n"); 8071d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, 0, NFI_INTR_EN); 8081d6b1e46SJorge Ramirez-Ortiz ret = -ETIMEDOUT; 8091d6b1e46SJorge Ramirez-Ortiz goto timeout; 8101d6b1e46SJorge Ramirez-Ortiz } 8111d6b1e46SJorge Ramirez-Ortiz 8121d6b1e46SJorge Ramirez-Ortiz ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg, 813559e58e7SRogerCC Lin ADDRCNTR_SEC(reg) >= chip->ecc.steps, 8141d6b1e46SJorge Ramirez-Ortiz 10, MTK_TIMEOUT); 8151d6b1e46SJorge Ramirez-Ortiz if (ret) 8161d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "hwecc write timeout\n"); 8171d6b1e46SJorge Ramirez-Ortiz 8181d6b1e46SJorge Ramirez-Ortiz timeout: 8191d6b1e46SJorge Ramirez-Ortiz 8201d6b1e46SJorge Ramirez-Ortiz dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE); 8211d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, 0, NFI_CON); 8221d6b1e46SJorge Ramirez-Ortiz 8231d6b1e46SJorge Ramirez-Ortiz return ret; 8241d6b1e46SJorge Ramirez-Ortiz } 8251d6b1e46SJorge Ramirez-Ortiz 8261d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, 8271d6b1e46SJorge Ramirez-Ortiz const u8 *buf, int page, int raw) 8281d6b1e46SJorge Ramirez-Ortiz { 8291d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 8301d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 8311d6b1e46SJorge Ramirez-Ortiz size_t len; 8321d6b1e46SJorge Ramirez-Ortiz const u8 *bufpoi; 8331d6b1e46SJorge Ramirez-Ortiz u32 reg; 8341d6b1e46SJorge Ramirez-Ortiz int ret; 8351d6b1e46SJorge Ramirez-Ortiz 8365197360fSBoris Brezillon mtk_nfc_select_target(chip, chip->cur_cs); 83725f815f6SBoris Brezillon nand_prog_page_begin_op(chip, page, 0, NULL, 0); 83825f815f6SBoris Brezillon 8391d6b1e46SJorge Ramirez-Ortiz if (!raw) { 8401d6b1e46SJorge Ramirez-Ortiz /* OOB => FDM: from register, ECC: from HW */ 8411d6b1e46SJorge Ramirez-Ortiz reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN; 8421d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG); 8431d6b1e46SJorge Ramirez-Ortiz 8441d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.op = ECC_ENCODE; 8451d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.mode = ECC_NFI_MODE; 8461d6b1e46SJorge Ramirez-Ortiz ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); 8471d6b1e46SJorge Ramirez-Ortiz if (ret) { 8481d6b1e46SJorge Ramirez-Ortiz /* clear NFI config */ 8491d6b1e46SJorge Ramirez-Ortiz reg = nfi_readw(nfc, NFI_CNFG); 8501d6b1e46SJorge Ramirez-Ortiz reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); 8511d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 8521d6b1e46SJorge Ramirez-Ortiz 8531d6b1e46SJorge Ramirez-Ortiz return ret; 8541d6b1e46SJorge Ramirez-Ortiz } 8551d6b1e46SJorge Ramirez-Ortiz 8561d6b1e46SJorge Ramirez-Ortiz memcpy(nfc->buffer, buf, mtd->writesize); 8571d6b1e46SJorge Ramirez-Ortiz mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw); 8581d6b1e46SJorge Ramirez-Ortiz bufpoi = nfc->buffer; 8591d6b1e46SJorge Ramirez-Ortiz 8601d6b1e46SJorge Ramirez-Ortiz /* write OOB into the FDM registers (OOB area in MTK NAND) */ 8611d6b1e46SJorge Ramirez-Ortiz mtk_nfc_write_fdm(chip); 8621d6b1e46SJorge Ramirez-Ortiz } else { 8631d6b1e46SJorge Ramirez-Ortiz bufpoi = buf; 8641d6b1e46SJorge Ramirez-Ortiz } 8651d6b1e46SJorge Ramirez-Ortiz 8661d6b1e46SJorge Ramirez-Ortiz len = mtd->writesize + (raw ? mtd->oobsize : 0); 8671d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len); 8681d6b1e46SJorge Ramirez-Ortiz 8691d6b1e46SJorge Ramirez-Ortiz if (!raw) 8701d6b1e46SJorge Ramirez-Ortiz mtk_ecc_disable(nfc->ecc); 8711d6b1e46SJorge Ramirez-Ortiz 87225f815f6SBoris Brezillon if (ret) 8731d6b1e46SJorge Ramirez-Ortiz return ret; 87425f815f6SBoris Brezillon 87525f815f6SBoris Brezillon return nand_prog_page_end_op(chip); 8761d6b1e46SJorge Ramirez-Ortiz } 8771d6b1e46SJorge Ramirez-Ortiz 878767eb6fbSBoris Brezillon static int mtk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, 8791d6b1e46SJorge Ramirez-Ortiz int oob_on, int page) 8801d6b1e46SJorge Ramirez-Ortiz { 881767eb6fbSBoris Brezillon return mtk_nfc_write_page(nand_to_mtd(chip), chip, buf, page, 0); 8821d6b1e46SJorge Ramirez-Ortiz } 8831d6b1e46SJorge Ramirez-Ortiz 884767eb6fbSBoris Brezillon static int mtk_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, 885767eb6fbSBoris Brezillon int oob_on, int pg) 8861d6b1e46SJorge Ramirez-Ortiz { 887767eb6fbSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(chip); 8881d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 8891d6b1e46SJorge Ramirez-Ortiz 8901d6b1e46SJorge Ramirez-Ortiz mtk_nfc_format_page(mtd, buf); 8911d6b1e46SJorge Ramirez-Ortiz return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1); 8921d6b1e46SJorge Ramirez-Ortiz } 8931d6b1e46SJorge Ramirez-Ortiz 894767eb6fbSBoris Brezillon static int mtk_nfc_write_subpage_hwecc(struct nand_chip *chip, u32 offset, 8951d6b1e46SJorge Ramirez-Ortiz u32 data_len, const u8 *buf, 8961d6b1e46SJorge Ramirez-Ortiz int oob_on, int page) 8971d6b1e46SJorge Ramirez-Ortiz { 898767eb6fbSBoris Brezillon struct mtd_info *mtd = nand_to_mtd(chip); 8991d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 9001d6b1e46SJorge Ramirez-Ortiz int ret; 9011d6b1e46SJorge Ramirez-Ortiz 9021d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf); 9031d6b1e46SJorge Ramirez-Ortiz if (ret < 0) 9041d6b1e46SJorge Ramirez-Ortiz return ret; 9051d6b1e46SJorge Ramirez-Ortiz 9061d6b1e46SJorge Ramirez-Ortiz /* use the data in the private buffer (now with FDM and CRC) */ 9071d6b1e46SJorge Ramirez-Ortiz return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1); 9081d6b1e46SJorge Ramirez-Ortiz } 9091d6b1e46SJorge Ramirez-Ortiz 910767eb6fbSBoris Brezillon static int mtk_nfc_write_oob_std(struct nand_chip *chip, int page) 9111d6b1e46SJorge Ramirez-Ortiz { 912767eb6fbSBoris Brezillon return mtk_nfc_write_page_raw(chip, NULL, 1, page); 9131d6b1e46SJorge Ramirez-Ortiz } 9141d6b1e46SJorge Ramirez-Ortiz 915336d4b13SXiaolei Li static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 start, 916336d4b13SXiaolei Li u32 sectors) 9171d6b1e46SJorge Ramirez-Ortiz { 9181d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 9191d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 9201d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 9211d6b1e46SJorge Ramirez-Ortiz struct mtk_ecc_stats stats; 922336d4b13SXiaolei Li u32 reg_size = mtk_nand->fdm.reg_size; 9231d6b1e46SJorge Ramirez-Ortiz int rc, i; 9241d6b1e46SJorge Ramirez-Ortiz 9251d6b1e46SJorge Ramirez-Ortiz rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE; 9261d6b1e46SJorge Ramirez-Ortiz if (rc) { 9271d6b1e46SJorge Ramirez-Ortiz memset(buf, 0xff, sectors * chip->ecc.size); 9281d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < sectors; i++) 929336d4b13SXiaolei Li memset(oob_ptr(chip, start + i), 0xff, reg_size); 9301d6b1e46SJorge Ramirez-Ortiz return 0; 9311d6b1e46SJorge Ramirez-Ortiz } 9321d6b1e46SJorge Ramirez-Ortiz 9331d6b1e46SJorge Ramirez-Ortiz mtk_ecc_get_stats(nfc->ecc, &stats, sectors); 9341d6b1e46SJorge Ramirez-Ortiz mtd->ecc_stats.corrected += stats.corrected; 9351d6b1e46SJorge Ramirez-Ortiz mtd->ecc_stats.failed += stats.failed; 9361d6b1e46SJorge Ramirez-Ortiz 9371d6b1e46SJorge Ramirez-Ortiz return stats.bitflips; 9381d6b1e46SJorge Ramirez-Ortiz } 9391d6b1e46SJorge Ramirez-Ortiz 9401d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, 9411d6b1e46SJorge Ramirez-Ortiz u32 data_offs, u32 readlen, 9421d6b1e46SJorge Ramirez-Ortiz u8 *bufpoi, int page, int raw) 9431d6b1e46SJorge Ramirez-Ortiz { 9441d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 9451d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 9461d6b1e46SJorge Ramirez-Ortiz u32 spare = mtk_nand->spare_per_sector; 9471d6b1e46SJorge Ramirez-Ortiz u32 column, sectors, start, end, reg; 9481d6b1e46SJorge Ramirez-Ortiz dma_addr_t addr; 949336d4b13SXiaolei Li int bitflips = 0; 9501d6b1e46SJorge Ramirez-Ortiz size_t len; 9511d6b1e46SJorge Ramirez-Ortiz u8 *buf; 9521d6b1e46SJorge Ramirez-Ortiz int rc; 9531d6b1e46SJorge Ramirez-Ortiz 9545197360fSBoris Brezillon mtk_nfc_select_target(chip, chip->cur_cs); 9551d6b1e46SJorge Ramirez-Ortiz start = data_offs / chip->ecc.size; 9561d6b1e46SJorge Ramirez-Ortiz end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size); 9571d6b1e46SJorge Ramirez-Ortiz 9581d6b1e46SJorge Ramirez-Ortiz sectors = end - start; 9591d6b1e46SJorge Ramirez-Ortiz column = start * (chip->ecc.size + spare); 9601d6b1e46SJorge Ramirez-Ortiz 9611d6b1e46SJorge Ramirez-Ortiz len = sectors * chip->ecc.size + (raw ? sectors * spare : 0); 9621d6b1e46SJorge Ramirez-Ortiz buf = bufpoi + start * chip->ecc.size; 9631d6b1e46SJorge Ramirez-Ortiz 96425f815f6SBoris Brezillon nand_read_page_op(chip, page, column, NULL, 0); 9651d6b1e46SJorge Ramirez-Ortiz 9661d6b1e46SJorge Ramirez-Ortiz addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE); 9671d6b1e46SJorge Ramirez-Ortiz rc = dma_mapping_error(nfc->dev, addr); 9681d6b1e46SJorge Ramirez-Ortiz if (rc) { 9691d6b1e46SJorge Ramirez-Ortiz dev_err(nfc->dev, "dma mapping error\n"); 9701d6b1e46SJorge Ramirez-Ortiz 9711d6b1e46SJorge Ramirez-Ortiz return -EINVAL; 9721d6b1e46SJorge Ramirez-Ortiz } 9731d6b1e46SJorge Ramirez-Ortiz 9741d6b1e46SJorge Ramirez-Ortiz reg = nfi_readw(nfc, NFI_CNFG); 9751d6b1e46SJorge Ramirez-Ortiz reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB; 9761d6b1e46SJorge Ramirez-Ortiz if (!raw) { 9771d6b1e46SJorge Ramirez-Ortiz reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN; 9781d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 9791d6b1e46SJorge Ramirez-Ortiz 9801d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.mode = ECC_NFI_MODE; 9811d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.sectors = sectors; 9821d6b1e46SJorge Ramirez-Ortiz nfc->ecc_cfg.op = ECC_DECODE; 9831d6b1e46SJorge Ramirez-Ortiz rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg); 9841d6b1e46SJorge Ramirez-Ortiz if (rc) { 9851d6b1e46SJorge Ramirez-Ortiz dev_err(nfc->dev, "ecc enable\n"); 9861d6b1e46SJorge Ramirez-Ortiz /* clear NFI_CNFG */ 9871d6b1e46SJorge Ramirez-Ortiz reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN | 9881d6b1e46SJorge Ramirez-Ortiz CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN); 9891d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 9901d6b1e46SJorge Ramirez-Ortiz dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); 9911d6b1e46SJorge Ramirez-Ortiz 9921d6b1e46SJorge Ramirez-Ortiz return rc; 9931d6b1e46SJorge Ramirez-Ortiz } 9941d6b1e46SJorge Ramirez-Ortiz } else { 9951d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, reg, NFI_CNFG); 9961d6b1e46SJorge Ramirez-Ortiz } 9971d6b1e46SJorge Ramirez-Ortiz 9981d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON); 9991d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN); 10001d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR); 10011d6b1e46SJorge Ramirez-Ortiz 10021d6b1e46SJorge Ramirez-Ortiz init_completion(&nfc->done); 10031d6b1e46SJorge Ramirez-Ortiz reg = nfi_readl(nfc, NFI_CON) | CON_BRD; 10041d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, reg, NFI_CON); 10051d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, STAR_EN, NFI_STRDATA); 10061d6b1e46SJorge Ramirez-Ortiz 10071d6b1e46SJorge Ramirez-Ortiz rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500)); 10081d6b1e46SJorge Ramirez-Ortiz if (!rc) 10091d6b1e46SJorge Ramirez-Ortiz dev_warn(nfc->dev, "read ahb/dma done timeout\n"); 10101d6b1e46SJorge Ramirez-Ortiz 10111d6b1e46SJorge Ramirez-Ortiz rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg, 1012559e58e7SRogerCC Lin ADDRCNTR_SEC(reg) >= sectors, 10, 10131d6b1e46SJorge Ramirez-Ortiz MTK_TIMEOUT); 10141d6b1e46SJorge Ramirez-Ortiz if (rc < 0) { 10151d6b1e46SJorge Ramirez-Ortiz dev_err(nfc->dev, "subpage done timeout\n"); 10161d6b1e46SJorge Ramirez-Ortiz bitflips = -EIO; 1017336d4b13SXiaolei Li } else if (!raw) { 10181d6b1e46SJorge Ramirez-Ortiz rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE); 10191d6b1e46SJorge Ramirez-Ortiz bitflips = rc < 0 ? -ETIMEDOUT : 1020336d4b13SXiaolei Li mtk_nfc_update_ecc_stats(mtd, buf, start, sectors); 10211d6b1e46SJorge Ramirez-Ortiz mtk_nfc_read_fdm(chip, start, sectors); 10221d6b1e46SJorge Ramirez-Ortiz } 10231d6b1e46SJorge Ramirez-Ortiz 10241d6b1e46SJorge Ramirez-Ortiz dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE); 10251d6b1e46SJorge Ramirez-Ortiz 10261d6b1e46SJorge Ramirez-Ortiz if (raw) 10271d6b1e46SJorge Ramirez-Ortiz goto done; 10281d6b1e46SJorge Ramirez-Ortiz 10291d6b1e46SJorge Ramirez-Ortiz mtk_ecc_disable(nfc->ecc); 10301d6b1e46SJorge Ramirez-Ortiz 10311d6b1e46SJorge Ramirez-Ortiz if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec) 10321d6b1e46SJorge Ramirez-Ortiz mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw); 10331d6b1e46SJorge Ramirez-Ortiz done: 10341d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, 0, NFI_CON); 10351d6b1e46SJorge Ramirez-Ortiz 10361d6b1e46SJorge Ramirez-Ortiz return bitflips; 10371d6b1e46SJorge Ramirez-Ortiz } 10381d6b1e46SJorge Ramirez-Ortiz 1039b9761687SBoris Brezillon static int mtk_nfc_read_subpage_hwecc(struct nand_chip *chip, u32 off, 10401d6b1e46SJorge Ramirez-Ortiz u32 len, u8 *p, int pg) 10411d6b1e46SJorge Ramirez-Ortiz { 1042b9761687SBoris Brezillon return mtk_nfc_read_subpage(nand_to_mtd(chip), chip, off, len, p, pg, 1043b9761687SBoris Brezillon 0); 10441d6b1e46SJorge Ramirez-Ortiz } 10451d6b1e46SJorge Ramirez-Ortiz 1046b9761687SBoris Brezillon static int mtk_nfc_read_page_hwecc(struct nand_chip *chip, u8 *p, int oob_on, 1047b9761687SBoris Brezillon int pg) 10481d6b1e46SJorge Ramirez-Ortiz { 1049b9761687SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(chip); 1050b9761687SBoris Brezillon 10511d6b1e46SJorge Ramirez-Ortiz return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0); 10521d6b1e46SJorge Ramirez-Ortiz } 10531d6b1e46SJorge Ramirez-Ortiz 1054b9761687SBoris Brezillon static int mtk_nfc_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_on, 1055b9761687SBoris Brezillon int page) 10561d6b1e46SJorge Ramirez-Ortiz { 1057b9761687SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(chip); 10581d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 10591d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = nand_get_controller_data(chip); 10601d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; 10611d6b1e46SJorge Ramirez-Ortiz int i, ret; 10621d6b1e46SJorge Ramirez-Ortiz 10631d6b1e46SJorge Ramirez-Ortiz memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize); 10641d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer, 10651d6b1e46SJorge Ramirez-Ortiz page, 1); 10661d6b1e46SJorge Ramirez-Ortiz if (ret < 0) 10671d6b1e46SJorge Ramirez-Ortiz return ret; 10681d6b1e46SJorge Ramirez-Ortiz 10691d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < chip->ecc.steps; i++) { 10701d6b1e46SJorge Ramirez-Ortiz memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size); 10711d6b1e46SJorge Ramirez-Ortiz 10721d6b1e46SJorge Ramirez-Ortiz if (i == mtk_nand->bad_mark.sec) 10731d6b1e46SJorge Ramirez-Ortiz mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1); 10741d6b1e46SJorge Ramirez-Ortiz 10751d6b1e46SJorge Ramirez-Ortiz if (buf) 10761d6b1e46SJorge Ramirez-Ortiz memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i), 10771d6b1e46SJorge Ramirez-Ortiz chip->ecc.size); 10781d6b1e46SJorge Ramirez-Ortiz } 10791d6b1e46SJorge Ramirez-Ortiz 10801d6b1e46SJorge Ramirez-Ortiz return ret; 10811d6b1e46SJorge Ramirez-Ortiz } 10821d6b1e46SJorge Ramirez-Ortiz 1083b9761687SBoris Brezillon static int mtk_nfc_read_oob_std(struct nand_chip *chip, int page) 10841d6b1e46SJorge Ramirez-Ortiz { 1085b9761687SBoris Brezillon return mtk_nfc_read_page_raw(chip, NULL, 1, page); 10861d6b1e46SJorge Ramirez-Ortiz } 10871d6b1e46SJorge Ramirez-Ortiz 10881d6b1e46SJorge Ramirez-Ortiz static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc) 10891d6b1e46SJorge Ramirez-Ortiz { 10901d6b1e46SJorge Ramirez-Ortiz /* 10911d6b1e46SJorge Ramirez-Ortiz * CNRNB: nand ready/busy register 10921d6b1e46SJorge Ramirez-Ortiz * ------------------------------- 10931d6b1e46SJorge Ramirez-Ortiz * 7:4: timeout register for polling the NAND busy/ready signal 10941d6b1e46SJorge Ramirez-Ortiz * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles. 10951d6b1e46SJorge Ramirez-Ortiz */ 10961d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, 0xf1, NFI_CNRNB); 1097582212ceSXiaolei Li nfi_writel(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT); 10981d6b1e46SJorge Ramirez-Ortiz 10991d6b1e46SJorge Ramirez-Ortiz mtk_nfc_hw_reset(nfc); 11001d6b1e46SJorge Ramirez-Ortiz 11011d6b1e46SJorge Ramirez-Ortiz nfi_readl(nfc, NFI_INTR_STA); 11021d6b1e46SJorge Ramirez-Ortiz nfi_writel(nfc, 0, NFI_INTR_EN); 11031d6b1e46SJorge Ramirez-Ortiz } 11041d6b1e46SJorge Ramirez-Ortiz 11051d6b1e46SJorge Ramirez-Ortiz static irqreturn_t mtk_nfc_irq(int irq, void *id) 11061d6b1e46SJorge Ramirez-Ortiz { 11071d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = id; 11081d6b1e46SJorge Ramirez-Ortiz u16 sta, ien; 11091d6b1e46SJorge Ramirez-Ortiz 11101d6b1e46SJorge Ramirez-Ortiz sta = nfi_readw(nfc, NFI_INTR_STA); 11111d6b1e46SJorge Ramirez-Ortiz ien = nfi_readw(nfc, NFI_INTR_EN); 11121d6b1e46SJorge Ramirez-Ortiz 11131d6b1e46SJorge Ramirez-Ortiz if (!(sta & ien)) 11141d6b1e46SJorge Ramirez-Ortiz return IRQ_NONE; 11151d6b1e46SJorge Ramirez-Ortiz 11161d6b1e46SJorge Ramirez-Ortiz nfi_writew(nfc, ~sta & ien, NFI_INTR_EN); 11171d6b1e46SJorge Ramirez-Ortiz complete(&nfc->done); 11181d6b1e46SJorge Ramirez-Ortiz 11191d6b1e46SJorge Ramirez-Ortiz return IRQ_HANDLED; 11201d6b1e46SJorge Ramirez-Ortiz } 11211d6b1e46SJorge Ramirez-Ortiz 11221d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk) 11231d6b1e46SJorge Ramirez-Ortiz { 11241d6b1e46SJorge Ramirez-Ortiz int ret; 11251d6b1e46SJorge Ramirez-Ortiz 11261d6b1e46SJorge Ramirez-Ortiz ret = clk_prepare_enable(clk->nfi_clk); 11271d6b1e46SJorge Ramirez-Ortiz if (ret) { 11281d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "failed to enable nfi clk\n"); 11291d6b1e46SJorge Ramirez-Ortiz return ret; 11301d6b1e46SJorge Ramirez-Ortiz } 11311d6b1e46SJorge Ramirez-Ortiz 11321d6b1e46SJorge Ramirez-Ortiz ret = clk_prepare_enable(clk->pad_clk); 11331d6b1e46SJorge Ramirez-Ortiz if (ret) { 11341d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "failed to enable pad clk\n"); 11351d6b1e46SJorge Ramirez-Ortiz clk_disable_unprepare(clk->nfi_clk); 11361d6b1e46SJorge Ramirez-Ortiz return ret; 11371d6b1e46SJorge Ramirez-Ortiz } 11381d6b1e46SJorge Ramirez-Ortiz 11391d6b1e46SJorge Ramirez-Ortiz return 0; 11401d6b1e46SJorge Ramirez-Ortiz } 11411d6b1e46SJorge Ramirez-Ortiz 11421d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk) 11431d6b1e46SJorge Ramirez-Ortiz { 11441d6b1e46SJorge Ramirez-Ortiz clk_disable_unprepare(clk->nfi_clk); 11451d6b1e46SJorge Ramirez-Ortiz clk_disable_unprepare(clk->pad_clk); 11461d6b1e46SJorge Ramirez-Ortiz } 11471d6b1e46SJorge Ramirez-Ortiz 11481d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, 11491d6b1e46SJorge Ramirez-Ortiz struct mtd_oob_region *oob_region) 11501d6b1e46SJorge Ramirez-Ortiz { 11511d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 11521d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 11531d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_fdm *fdm = &mtk_nand->fdm; 11541d6b1e46SJorge Ramirez-Ortiz u32 eccsteps; 11551d6b1e46SJorge Ramirez-Ortiz 11561d6b1e46SJorge Ramirez-Ortiz eccsteps = mtd->writesize / chip->ecc.size; 11571d6b1e46SJorge Ramirez-Ortiz 11581d6b1e46SJorge Ramirez-Ortiz if (section >= eccsteps) 11591d6b1e46SJorge Ramirez-Ortiz return -ERANGE; 11601d6b1e46SJorge Ramirez-Ortiz 11611d6b1e46SJorge Ramirez-Ortiz oob_region->length = fdm->reg_size - fdm->ecc_size; 11621d6b1e46SJorge Ramirez-Ortiz oob_region->offset = section * fdm->reg_size + fdm->ecc_size; 11631d6b1e46SJorge Ramirez-Ortiz 11641d6b1e46SJorge Ramirez-Ortiz return 0; 11651d6b1e46SJorge Ramirez-Ortiz } 11661d6b1e46SJorge Ramirez-Ortiz 11671d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section, 11681d6b1e46SJorge Ramirez-Ortiz struct mtd_oob_region *oob_region) 11691d6b1e46SJorge Ramirez-Ortiz { 11701d6b1e46SJorge Ramirez-Ortiz struct nand_chip *chip = mtd_to_nand(mtd); 11711d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 11721d6b1e46SJorge Ramirez-Ortiz u32 eccsteps; 11731d6b1e46SJorge Ramirez-Ortiz 11741d6b1e46SJorge Ramirez-Ortiz if (section) 11751d6b1e46SJorge Ramirez-Ortiz return -ERANGE; 11761d6b1e46SJorge Ramirez-Ortiz 11771d6b1e46SJorge Ramirez-Ortiz eccsteps = mtd->writesize / chip->ecc.size; 11781d6b1e46SJorge Ramirez-Ortiz oob_region->offset = mtk_nand->fdm.reg_size * eccsteps; 11791d6b1e46SJorge Ramirez-Ortiz oob_region->length = mtd->oobsize - oob_region->offset; 11801d6b1e46SJorge Ramirez-Ortiz 11811d6b1e46SJorge Ramirez-Ortiz return 0; 11821d6b1e46SJorge Ramirez-Ortiz } 11831d6b1e46SJorge Ramirez-Ortiz 11841d6b1e46SJorge Ramirez-Ortiz static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = { 11851d6b1e46SJorge Ramirez-Ortiz .free = mtk_nfc_ooblayout_free, 11861d6b1e46SJorge Ramirez-Ortiz .ecc = mtk_nfc_ooblayout_ecc, 11871d6b1e46SJorge Ramirez-Ortiz }; 11881d6b1e46SJorge Ramirez-Ortiz 11891d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd) 11901d6b1e46SJorge Ramirez-Ortiz { 11911d6b1e46SJorge Ramirez-Ortiz struct nand_chip *nand = mtd_to_nand(mtd); 11921d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand); 1193b45ee550SRogerCC Lin struct mtk_nfc *nfc = nand_get_controller_data(nand); 11941d6b1e46SJorge Ramirez-Ortiz u32 ecc_bytes; 11951d6b1e46SJorge Ramirez-Ortiz 1196b45ee550SRogerCC Lin ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * 1197b45ee550SRogerCC Lin mtk_ecc_get_parity_bits(nfc->ecc), 8); 11981d6b1e46SJorge Ramirez-Ortiz 11991d6b1e46SJorge Ramirez-Ortiz fdm->reg_size = chip->spare_per_sector - ecc_bytes; 12001d6b1e46SJorge Ramirez-Ortiz if (fdm->reg_size > NFI_FDM_MAX_SIZE) 12011d6b1e46SJorge Ramirez-Ortiz fdm->reg_size = NFI_FDM_MAX_SIZE; 12021d6b1e46SJorge Ramirez-Ortiz 12031d6b1e46SJorge Ramirez-Ortiz /* bad block mark storage */ 12041d6b1e46SJorge Ramirez-Ortiz fdm->ecc_size = 1; 12051d6b1e46SJorge Ramirez-Ortiz } 12061d6b1e46SJorge Ramirez-Ortiz 12071d6b1e46SJorge Ramirez-Ortiz static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl, 12081d6b1e46SJorge Ramirez-Ortiz struct mtd_info *mtd) 12091d6b1e46SJorge Ramirez-Ortiz { 12101d6b1e46SJorge Ramirez-Ortiz struct nand_chip *nand = mtd_to_nand(mtd); 12111d6b1e46SJorge Ramirez-Ortiz 12121d6b1e46SJorge Ramirez-Ortiz if (mtd->writesize == 512) { 12131d6b1e46SJorge Ramirez-Ortiz bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap; 12141d6b1e46SJorge Ramirez-Ortiz } else { 12151d6b1e46SJorge Ramirez-Ortiz bm_ctl->bm_swap = mtk_nfc_bad_mark_swap; 12161d6b1e46SJorge Ramirez-Ortiz bm_ctl->sec = mtd->writesize / mtk_data_len(nand); 12171d6b1e46SJorge Ramirez-Ortiz bm_ctl->pos = mtd->writesize % mtk_data_len(nand); 12181d6b1e46SJorge Ramirez-Ortiz } 12191d6b1e46SJorge Ramirez-Ortiz } 12201d6b1e46SJorge Ramirez-Ortiz 12217ec4a37cSXiaolei Li static int mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd) 12221d6b1e46SJorge Ramirez-Ortiz { 12231d6b1e46SJorge Ramirez-Ortiz struct nand_chip *nand = mtd_to_nand(mtd); 12247ec4a37cSXiaolei Li struct mtk_nfc *nfc = nand_get_controller_data(nand); 12257ec4a37cSXiaolei Li const u8 *spare = nfc->caps->spare_size; 12267ec4a37cSXiaolei Li u32 eccsteps, i, closest_spare = 0; 12271d6b1e46SJorge Ramirez-Ortiz 12281d6b1e46SJorge Ramirez-Ortiz eccsteps = mtd->writesize / nand->ecc.size; 12291d6b1e46SJorge Ramirez-Ortiz *sps = mtd->oobsize / eccsteps; 12301d6b1e46SJorge Ramirez-Ortiz 12311d6b1e46SJorge Ramirez-Ortiz if (nand->ecc.size == 1024) 12321d6b1e46SJorge Ramirez-Ortiz *sps >>= 1; 12331d6b1e46SJorge Ramirez-Ortiz 12347ec4a37cSXiaolei Li if (*sps < MTK_NFC_MIN_SPARE) 12357ec4a37cSXiaolei Li return -EINVAL; 12367ec4a37cSXiaolei Li 12377ec4a37cSXiaolei Li for (i = 0; i < nfc->caps->num_spare_size; i++) { 12387ec4a37cSXiaolei Li if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) { 12397ec4a37cSXiaolei Li closest_spare = i; 12407ec4a37cSXiaolei Li if (*sps == spare[i]) 12411d6b1e46SJorge Ramirez-Ortiz break; 12421d6b1e46SJorge Ramirez-Ortiz } 12431d6b1e46SJorge Ramirez-Ortiz } 12441d6b1e46SJorge Ramirez-Ortiz 12457ec4a37cSXiaolei Li *sps = spare[closest_spare]; 12461d6b1e46SJorge Ramirez-Ortiz 12471d6b1e46SJorge Ramirez-Ortiz if (nand->ecc.size == 1024) 12481d6b1e46SJorge Ramirez-Ortiz *sps <<= 1; 12497ec4a37cSXiaolei Li 12507ec4a37cSXiaolei Li return 0; 12511d6b1e46SJorge Ramirez-Ortiz } 12521d6b1e46SJorge Ramirez-Ortiz 12531d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd) 12541d6b1e46SJorge Ramirez-Ortiz { 12551d6b1e46SJorge Ramirez-Ortiz struct nand_chip *nand = mtd_to_nand(mtd); 125653576c7bSMiquel Raynal const struct nand_ecc_props *requirements = 125753576c7bSMiquel Raynal nanddev_get_ecc_requirements(&nand->base); 12587ec4a37cSXiaolei Li struct mtk_nfc *nfc = nand_get_controller_data(nand); 12591d6b1e46SJorge Ramirez-Ortiz u32 spare; 12607ec4a37cSXiaolei Li int free, ret; 12611d6b1e46SJorge Ramirez-Ortiz 12621d6b1e46SJorge Ramirez-Ortiz /* support only ecc hw mode */ 1263bace41f8SMiquel Raynal if (nand->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) { 1264bace41f8SMiquel Raynal dev_err(dev, "ecc.engine_type not supported\n"); 12651d6b1e46SJorge Ramirez-Ortiz return -EINVAL; 12661d6b1e46SJorge Ramirez-Ortiz } 12671d6b1e46SJorge Ramirez-Ortiz 12681d6b1e46SJorge Ramirez-Ortiz /* if optional dt settings not present */ 12691d6b1e46SJorge Ramirez-Ortiz if (!nand->ecc.size || !nand->ecc.strength) { 12701d6b1e46SJorge Ramirez-Ortiz /* use datasheet requirements */ 127153576c7bSMiquel Raynal nand->ecc.strength = requirements->strength; 127253576c7bSMiquel Raynal nand->ecc.size = requirements->step_size; 12731d6b1e46SJorge Ramirez-Ortiz 12741d6b1e46SJorge Ramirez-Ortiz /* 12751d6b1e46SJorge Ramirez-Ortiz * align eccstrength and eccsize 12761d6b1e46SJorge Ramirez-Ortiz * this controller only supports 512 and 1024 sizes 12771d6b1e46SJorge Ramirez-Ortiz */ 12781d6b1e46SJorge Ramirez-Ortiz if (nand->ecc.size < 1024) { 1279b45ee550SRogerCC Lin if (mtd->writesize > 512 && 1280b45ee550SRogerCC Lin nfc->caps->max_sector_size > 512) { 12811d6b1e46SJorge Ramirez-Ortiz nand->ecc.size = 1024; 12821d6b1e46SJorge Ramirez-Ortiz nand->ecc.strength <<= 1; 12831d6b1e46SJorge Ramirez-Ortiz } else { 12841d6b1e46SJorge Ramirez-Ortiz nand->ecc.size = 512; 12851d6b1e46SJorge Ramirez-Ortiz } 12861d6b1e46SJorge Ramirez-Ortiz } else { 12871d6b1e46SJorge Ramirez-Ortiz nand->ecc.size = 1024; 12881d6b1e46SJorge Ramirez-Ortiz } 12891d6b1e46SJorge Ramirez-Ortiz 12907ec4a37cSXiaolei Li ret = mtk_nfc_set_spare_per_sector(&spare, mtd); 12917ec4a37cSXiaolei Li if (ret) 12927ec4a37cSXiaolei Li return ret; 12931d6b1e46SJorge Ramirez-Ortiz 12941d6b1e46SJorge Ramirez-Ortiz /* calculate oob bytes except ecc parity data */ 1295b45ee550SRogerCC Lin free = (nand->ecc.strength * mtk_ecc_get_parity_bits(nfc->ecc) 1296b45ee550SRogerCC Lin + 7) >> 3; 12971d6b1e46SJorge Ramirez-Ortiz free = spare - free; 12981d6b1e46SJorge Ramirez-Ortiz 12991d6b1e46SJorge Ramirez-Ortiz /* 13001d6b1e46SJorge Ramirez-Ortiz * enhance ecc strength if oob left is bigger than max FDM size 13011d6b1e46SJorge Ramirez-Ortiz * or reduce ecc strength if oob size is not enough for ecc 13021d6b1e46SJorge Ramirez-Ortiz * parity data. 13031d6b1e46SJorge Ramirez-Ortiz */ 13041d6b1e46SJorge Ramirez-Ortiz if (free > NFI_FDM_MAX_SIZE) { 13051d6b1e46SJorge Ramirez-Ortiz spare -= NFI_FDM_MAX_SIZE; 1306b45ee550SRogerCC Lin nand->ecc.strength = (spare << 3) / 1307b45ee550SRogerCC Lin mtk_ecc_get_parity_bits(nfc->ecc); 13081d6b1e46SJorge Ramirez-Ortiz } else if (free < 0) { 13091d6b1e46SJorge Ramirez-Ortiz spare -= NFI_FDM_MIN_SIZE; 1310b45ee550SRogerCC Lin nand->ecc.strength = (spare << 3) / 1311b45ee550SRogerCC Lin mtk_ecc_get_parity_bits(nfc->ecc); 13121d6b1e46SJorge Ramirez-Ortiz } 13131d6b1e46SJorge Ramirez-Ortiz } 13141d6b1e46SJorge Ramirez-Ortiz 13157ec4a37cSXiaolei Li mtk_ecc_adjust_strength(nfc->ecc, &nand->ecc.strength); 13161d6b1e46SJorge Ramirez-Ortiz 13171d6b1e46SJorge Ramirez-Ortiz dev_info(dev, "eccsize %d eccstrength %d\n", 13181d6b1e46SJorge Ramirez-Ortiz nand->ecc.size, nand->ecc.strength); 13191d6b1e46SJorge Ramirez-Ortiz 13201d6b1e46SJorge Ramirez-Ortiz return 0; 13211d6b1e46SJorge Ramirez-Ortiz } 13221d6b1e46SJorge Ramirez-Ortiz 13231ce7826dSMiquel Raynal static int mtk_nfc_attach_chip(struct nand_chip *chip) 13241ce7826dSMiquel Raynal { 13251ce7826dSMiquel Raynal struct mtd_info *mtd = nand_to_mtd(chip); 13261ce7826dSMiquel Raynal struct device *dev = mtd->dev.parent; 13271ce7826dSMiquel Raynal struct mtk_nfc *nfc = nand_get_controller_data(chip); 13281ce7826dSMiquel Raynal struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip); 13291ce7826dSMiquel Raynal int len; 13301ce7826dSMiquel Raynal int ret; 13311ce7826dSMiquel Raynal 13321ce7826dSMiquel Raynal if (chip->options & NAND_BUSWIDTH_16) { 13331ce7826dSMiquel Raynal dev_err(dev, "16bits buswidth not supported"); 13341ce7826dSMiquel Raynal return -EINVAL; 13351ce7826dSMiquel Raynal } 13361ce7826dSMiquel Raynal 13371ce7826dSMiquel Raynal /* store bbt magic in page, cause OOB is not protected */ 13381ce7826dSMiquel Raynal if (chip->bbt_options & NAND_BBT_USE_FLASH) 13391ce7826dSMiquel Raynal chip->bbt_options |= NAND_BBT_NO_OOB; 13401ce7826dSMiquel Raynal 13411ce7826dSMiquel Raynal ret = mtk_nfc_ecc_init(dev, mtd); 13421ce7826dSMiquel Raynal if (ret) 13431ce7826dSMiquel Raynal return ret; 13441ce7826dSMiquel Raynal 13451ce7826dSMiquel Raynal ret = mtk_nfc_set_spare_per_sector(&mtk_nand->spare_per_sector, mtd); 13461ce7826dSMiquel Raynal if (ret) 13471ce7826dSMiquel Raynal return ret; 13481ce7826dSMiquel Raynal 13491ce7826dSMiquel Raynal mtk_nfc_set_fdm(&mtk_nand->fdm, mtd); 13501ce7826dSMiquel Raynal mtk_nfc_set_bad_mark_ctl(&mtk_nand->bad_mark, mtd); 13511ce7826dSMiquel Raynal 13521ce7826dSMiquel Raynal len = mtd->writesize + mtd->oobsize; 13531ce7826dSMiquel Raynal nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL); 13541ce7826dSMiquel Raynal if (!nfc->buffer) 13551ce7826dSMiquel Raynal return -ENOMEM; 13561ce7826dSMiquel Raynal 13571ce7826dSMiquel Raynal return 0; 13581ce7826dSMiquel Raynal } 13591ce7826dSMiquel Raynal 13601ce7826dSMiquel Raynal static const struct nand_controller_ops mtk_nfc_controller_ops = { 13611ce7826dSMiquel Raynal .attach_chip = mtk_nfc_attach_chip, 13624c46667bSMiquel Raynal .setup_interface = mtk_nfc_setup_interface, 13635197360fSBoris Brezillon .exec_op = mtk_nfc_exec_op, 13641ce7826dSMiquel Raynal }; 13651ce7826dSMiquel Raynal 13661d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc, 13671d6b1e46SJorge Ramirez-Ortiz struct device_node *np) 13681d6b1e46SJorge Ramirez-Ortiz { 13691d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *chip; 13701d6b1e46SJorge Ramirez-Ortiz struct nand_chip *nand; 13711d6b1e46SJorge Ramirez-Ortiz struct mtd_info *mtd; 13721ce7826dSMiquel Raynal int nsels; 13731d6b1e46SJorge Ramirez-Ortiz u32 tmp; 13741d6b1e46SJorge Ramirez-Ortiz int ret; 13751d6b1e46SJorge Ramirez-Ortiz int i; 13761d6b1e46SJorge Ramirez-Ortiz 13771d6b1e46SJorge Ramirez-Ortiz if (!of_get_property(np, "reg", &nsels)) 13781d6b1e46SJorge Ramirez-Ortiz return -ENODEV; 13791d6b1e46SJorge Ramirez-Ortiz 13801d6b1e46SJorge Ramirez-Ortiz nsels /= sizeof(u32); 13811d6b1e46SJorge Ramirez-Ortiz if (!nsels || nsels > MTK_NAND_MAX_NSELS) { 13821d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "invalid reg property size %d\n", nsels); 13831d6b1e46SJorge Ramirez-Ortiz return -EINVAL; 13841d6b1e46SJorge Ramirez-Ortiz } 13851d6b1e46SJorge Ramirez-Ortiz 13861d6b1e46SJorge Ramirez-Ortiz chip = devm_kzalloc(dev, sizeof(*chip) + nsels * sizeof(u8), 13871d6b1e46SJorge Ramirez-Ortiz GFP_KERNEL); 13881d6b1e46SJorge Ramirez-Ortiz if (!chip) 13891d6b1e46SJorge Ramirez-Ortiz return -ENOMEM; 13901d6b1e46SJorge Ramirez-Ortiz 13911d6b1e46SJorge Ramirez-Ortiz chip->nsels = nsels; 13921d6b1e46SJorge Ramirez-Ortiz for (i = 0; i < nsels; i++) { 13931d6b1e46SJorge Ramirez-Ortiz ret = of_property_read_u32_index(np, "reg", i, &tmp); 13941d6b1e46SJorge Ramirez-Ortiz if (ret) { 13951d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "reg property failure : %d\n", ret); 13961d6b1e46SJorge Ramirez-Ortiz return ret; 13971d6b1e46SJorge Ramirez-Ortiz } 13988dbd7b10SXiaolei Li 13998dbd7b10SXiaolei Li if (tmp >= MTK_NAND_MAX_NSELS) { 14008dbd7b10SXiaolei Li dev_err(dev, "invalid CS: %u\n", tmp); 14018dbd7b10SXiaolei Li return -EINVAL; 14028dbd7b10SXiaolei Li } 14038dbd7b10SXiaolei Li 14048dbd7b10SXiaolei Li if (test_and_set_bit(tmp, &nfc->assigned_cs)) { 14058dbd7b10SXiaolei Li dev_err(dev, "CS %u already assigned\n", tmp); 14068dbd7b10SXiaolei Li return -EINVAL; 14078dbd7b10SXiaolei Li } 14088dbd7b10SXiaolei Li 14091d6b1e46SJorge Ramirez-Ortiz chip->sels[i] = tmp; 14101d6b1e46SJorge Ramirez-Ortiz } 14111d6b1e46SJorge Ramirez-Ortiz 14121d6b1e46SJorge Ramirez-Ortiz nand = &chip->nand; 14131d6b1e46SJorge Ramirez-Ortiz nand->controller = &nfc->controller; 14141d6b1e46SJorge Ramirez-Ortiz 14151d6b1e46SJorge Ramirez-Ortiz nand_set_flash_node(nand, np); 14161d6b1e46SJorge Ramirez-Ortiz nand_set_controller_data(nand, nfc); 14171d6b1e46SJorge Ramirez-Ortiz 1418ce8148d7SMiquel Raynal nand->options |= NAND_USES_DMA | NAND_SUBPAGE_READ; 14191d6b1e46SJorge Ramirez-Ortiz 14201d6b1e46SJorge Ramirez-Ortiz /* set default mode in case dt entry is missing */ 1421bace41f8SMiquel Raynal nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 14221d6b1e46SJorge Ramirez-Ortiz 14231d6b1e46SJorge Ramirez-Ortiz nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc; 14241d6b1e46SJorge Ramirez-Ortiz nand->ecc.write_page_raw = mtk_nfc_write_page_raw; 14251d6b1e46SJorge Ramirez-Ortiz nand->ecc.write_page = mtk_nfc_write_page_hwecc; 14261d6b1e46SJorge Ramirez-Ortiz nand->ecc.write_oob_raw = mtk_nfc_write_oob_std; 14271d6b1e46SJorge Ramirez-Ortiz nand->ecc.write_oob = mtk_nfc_write_oob_std; 14281d6b1e46SJorge Ramirez-Ortiz 14291d6b1e46SJorge Ramirez-Ortiz nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc; 14301d6b1e46SJorge Ramirez-Ortiz nand->ecc.read_page_raw = mtk_nfc_read_page_raw; 14311d6b1e46SJorge Ramirez-Ortiz nand->ecc.read_page = mtk_nfc_read_page_hwecc; 14321d6b1e46SJorge Ramirez-Ortiz nand->ecc.read_oob_raw = mtk_nfc_read_oob_std; 14331d6b1e46SJorge Ramirez-Ortiz nand->ecc.read_oob = mtk_nfc_read_oob_std; 14341d6b1e46SJorge Ramirez-Ortiz 14351d6b1e46SJorge Ramirez-Ortiz mtd = nand_to_mtd(nand); 14361d6b1e46SJorge Ramirez-Ortiz mtd->owner = THIS_MODULE; 14371d6b1e46SJorge Ramirez-Ortiz mtd->dev.parent = dev; 14381d6b1e46SJorge Ramirez-Ortiz mtd->name = MTK_NAME; 14391d6b1e46SJorge Ramirez-Ortiz mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops); 14401d6b1e46SJorge Ramirez-Ortiz 14411d6b1e46SJorge Ramirez-Ortiz mtk_nfc_hw_init(nfc); 14421d6b1e46SJorge Ramirez-Ortiz 144300ad378fSBoris Brezillon ret = nand_scan(nand, nsels); 14441d6b1e46SJorge Ramirez-Ortiz if (ret) 1445f0dbe4aaSMasahiro Yamada return ret; 14461d6b1e46SJorge Ramirez-Ortiz 144729597ca1SRafał Miłecki ret = mtd_device_register(mtd, NULL, 0); 14481d6b1e46SJorge Ramirez-Ortiz if (ret) { 14491d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "mtd parse partition error\n"); 14508a82bbcaSMiquel Raynal nand_cleanup(nand); 14511d6b1e46SJorge Ramirez-Ortiz return ret; 14521d6b1e46SJorge Ramirez-Ortiz } 14531d6b1e46SJorge Ramirez-Ortiz 14541d6b1e46SJorge Ramirez-Ortiz list_add_tail(&chip->node, &nfc->chips); 14551d6b1e46SJorge Ramirez-Ortiz 14561d6b1e46SJorge Ramirez-Ortiz return 0; 14571d6b1e46SJorge Ramirez-Ortiz } 14581d6b1e46SJorge Ramirez-Ortiz 14591d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc) 14601d6b1e46SJorge Ramirez-Ortiz { 14611d6b1e46SJorge Ramirez-Ortiz struct device_node *np = dev->of_node; 14621d6b1e46SJorge Ramirez-Ortiz struct device_node *nand_np; 14631d6b1e46SJorge Ramirez-Ortiz int ret; 14641d6b1e46SJorge Ramirez-Ortiz 14651d6b1e46SJorge Ramirez-Ortiz for_each_child_of_node(np, nand_np) { 14661d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np); 14671d6b1e46SJorge Ramirez-Ortiz if (ret) { 14681d6b1e46SJorge Ramirez-Ortiz of_node_put(nand_np); 14691d6b1e46SJorge Ramirez-Ortiz return ret; 14701d6b1e46SJorge Ramirez-Ortiz } 14711d6b1e46SJorge Ramirez-Ortiz } 14721d6b1e46SJorge Ramirez-Ortiz 14731d6b1e46SJorge Ramirez-Ortiz return 0; 14741d6b1e46SJorge Ramirez-Ortiz } 14751d6b1e46SJorge Ramirez-Ortiz 14767ec4a37cSXiaolei Li static const struct mtk_nfc_caps mtk_nfc_caps_mt2701 = { 14777ec4a37cSXiaolei Li .spare_size = spare_size_mt2701, 14787ec4a37cSXiaolei Li .num_spare_size = 16, 14797ec4a37cSXiaolei Li .pageformat_spare_shift = 4, 1480edfee361SXiaolei Li .nfi_clk_div = 1, 1481b45ee550SRogerCC Lin .max_sector = 16, 1482b45ee550SRogerCC Lin .max_sector_size = 1024, 14837ec4a37cSXiaolei Li }; 14847ec4a37cSXiaolei Li 148530ee809eSXiaolei Li static const struct mtk_nfc_caps mtk_nfc_caps_mt2712 = { 148630ee809eSXiaolei Li .spare_size = spare_size_mt2712, 148730ee809eSXiaolei Li .num_spare_size = 19, 148830ee809eSXiaolei Li .pageformat_spare_shift = 16, 1489edfee361SXiaolei Li .nfi_clk_div = 2, 1490b45ee550SRogerCC Lin .max_sector = 16, 1491b45ee550SRogerCC Lin .max_sector_size = 1024, 149230ee809eSXiaolei Li }; 149330ee809eSXiaolei Li 149498dea8d7SRogerCC Lin static const struct mtk_nfc_caps mtk_nfc_caps_mt7622 = { 149598dea8d7SRogerCC Lin .spare_size = spare_size_mt7622, 149698dea8d7SRogerCC Lin .num_spare_size = 4, 149798dea8d7SRogerCC Lin .pageformat_spare_shift = 4, 149898dea8d7SRogerCC Lin .nfi_clk_div = 1, 149998dea8d7SRogerCC Lin .max_sector = 8, 150098dea8d7SRogerCC Lin .max_sector_size = 512, 150198dea8d7SRogerCC Lin }; 150298dea8d7SRogerCC Lin 15037ec4a37cSXiaolei Li static const struct of_device_id mtk_nfc_id_table[] = { 15047ec4a37cSXiaolei Li { 15057ec4a37cSXiaolei Li .compatible = "mediatek,mt2701-nfc", 15067ec4a37cSXiaolei Li .data = &mtk_nfc_caps_mt2701, 150730ee809eSXiaolei Li }, { 150830ee809eSXiaolei Li .compatible = "mediatek,mt2712-nfc", 150930ee809eSXiaolei Li .data = &mtk_nfc_caps_mt2712, 151098dea8d7SRogerCC Lin }, { 151198dea8d7SRogerCC Lin .compatible = "mediatek,mt7622-nfc", 151298dea8d7SRogerCC Lin .data = &mtk_nfc_caps_mt7622, 15137ec4a37cSXiaolei Li }, 15147ec4a37cSXiaolei Li {} 15157ec4a37cSXiaolei Li }; 15167ec4a37cSXiaolei Li MODULE_DEVICE_TABLE(of, mtk_nfc_id_table); 15177ec4a37cSXiaolei Li 15181d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_probe(struct platform_device *pdev) 15191d6b1e46SJorge Ramirez-Ortiz { 15201d6b1e46SJorge Ramirez-Ortiz struct device *dev = &pdev->dev; 15211d6b1e46SJorge Ramirez-Ortiz struct device_node *np = dev->of_node; 15221d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc; 15231d6b1e46SJorge Ramirez-Ortiz struct resource *res; 15241d6b1e46SJorge Ramirez-Ortiz int ret, irq; 15251d6b1e46SJorge Ramirez-Ortiz 15261d6b1e46SJorge Ramirez-Ortiz nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); 15271d6b1e46SJorge Ramirez-Ortiz if (!nfc) 15281d6b1e46SJorge Ramirez-Ortiz return -ENOMEM; 15291d6b1e46SJorge Ramirez-Ortiz 1530b5c2defcSBoris Brezillon nand_controller_init(&nfc->controller); 15311d6b1e46SJorge Ramirez-Ortiz INIT_LIST_HEAD(&nfc->chips); 15321ce7826dSMiquel Raynal nfc->controller.ops = &mtk_nfc_controller_ops; 15331d6b1e46SJorge Ramirez-Ortiz 15341d6b1e46SJorge Ramirez-Ortiz /* probe defer if not ready */ 15351d6b1e46SJorge Ramirez-Ortiz nfc->ecc = of_mtk_ecc_get(np); 15361d6b1e46SJorge Ramirez-Ortiz if (IS_ERR(nfc->ecc)) 15371d6b1e46SJorge Ramirez-Ortiz return PTR_ERR(nfc->ecc); 15381d6b1e46SJorge Ramirez-Ortiz else if (!nfc->ecc) 15391d6b1e46SJorge Ramirez-Ortiz return -ENODEV; 15401d6b1e46SJorge Ramirez-Ortiz 154136bf2eb9SRyder Lee nfc->caps = of_device_get_match_data(dev); 15421d6b1e46SJorge Ramirez-Ortiz nfc->dev = dev; 15431d6b1e46SJorge Ramirez-Ortiz 15441d6b1e46SJorge Ramirez-Ortiz res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 15451d6b1e46SJorge Ramirez-Ortiz nfc->regs = devm_ioremap_resource(dev, res); 15461d6b1e46SJorge Ramirez-Ortiz if (IS_ERR(nfc->regs)) { 15471d6b1e46SJorge Ramirez-Ortiz ret = PTR_ERR(nfc->regs); 15481d6b1e46SJorge Ramirez-Ortiz goto release_ecc; 15491d6b1e46SJorge Ramirez-Ortiz } 15501d6b1e46SJorge Ramirez-Ortiz 15511d6b1e46SJorge Ramirez-Ortiz nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk"); 15521d6b1e46SJorge Ramirez-Ortiz if (IS_ERR(nfc->clk.nfi_clk)) { 15531d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "no clk\n"); 15541d6b1e46SJorge Ramirez-Ortiz ret = PTR_ERR(nfc->clk.nfi_clk); 15551d6b1e46SJorge Ramirez-Ortiz goto release_ecc; 15561d6b1e46SJorge Ramirez-Ortiz } 15571d6b1e46SJorge Ramirez-Ortiz 15581d6b1e46SJorge Ramirez-Ortiz nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk"); 15591d6b1e46SJorge Ramirez-Ortiz if (IS_ERR(nfc->clk.pad_clk)) { 15601d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "no pad clk\n"); 15611d6b1e46SJorge Ramirez-Ortiz ret = PTR_ERR(nfc->clk.pad_clk); 15621d6b1e46SJorge Ramirez-Ortiz goto release_ecc; 15631d6b1e46SJorge Ramirez-Ortiz } 15641d6b1e46SJorge Ramirez-Ortiz 15651d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_enable_clk(dev, &nfc->clk); 15661d6b1e46SJorge Ramirez-Ortiz if (ret) 15671d6b1e46SJorge Ramirez-Ortiz goto release_ecc; 15681d6b1e46SJorge Ramirez-Ortiz 15691d6b1e46SJorge Ramirez-Ortiz irq = platform_get_irq(pdev, 0); 15701d6b1e46SJorge Ramirez-Ortiz if (irq < 0) { 15711d6b1e46SJorge Ramirez-Ortiz ret = -EINVAL; 15721d6b1e46SJorge Ramirez-Ortiz goto clk_disable; 15731d6b1e46SJorge Ramirez-Ortiz } 15741d6b1e46SJorge Ramirez-Ortiz 15751d6b1e46SJorge Ramirez-Ortiz ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc); 15761d6b1e46SJorge Ramirez-Ortiz if (ret) { 15771d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "failed to request nfi irq\n"); 15781d6b1e46SJorge Ramirez-Ortiz goto clk_disable; 15791d6b1e46SJorge Ramirez-Ortiz } 15801d6b1e46SJorge Ramirez-Ortiz 15811d6b1e46SJorge Ramirez-Ortiz ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 15821d6b1e46SJorge Ramirez-Ortiz if (ret) { 15831d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "failed to set dma mask\n"); 15841d6b1e46SJorge Ramirez-Ortiz goto clk_disable; 15851d6b1e46SJorge Ramirez-Ortiz } 15861d6b1e46SJorge Ramirez-Ortiz 15871d6b1e46SJorge Ramirez-Ortiz platform_set_drvdata(pdev, nfc); 15881d6b1e46SJorge Ramirez-Ortiz 15891d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_nand_chips_init(dev, nfc); 15901d6b1e46SJorge Ramirez-Ortiz if (ret) { 15911d6b1e46SJorge Ramirez-Ortiz dev_err(dev, "failed to init nand chips\n"); 15921d6b1e46SJorge Ramirez-Ortiz goto clk_disable; 15931d6b1e46SJorge Ramirez-Ortiz } 15941d6b1e46SJorge Ramirez-Ortiz 15951d6b1e46SJorge Ramirez-Ortiz return 0; 15961d6b1e46SJorge Ramirez-Ortiz 15971d6b1e46SJorge Ramirez-Ortiz clk_disable: 15981d6b1e46SJorge Ramirez-Ortiz mtk_nfc_disable_clk(&nfc->clk); 15991d6b1e46SJorge Ramirez-Ortiz 16001d6b1e46SJorge Ramirez-Ortiz release_ecc: 16011d6b1e46SJorge Ramirez-Ortiz mtk_ecc_release(nfc->ecc); 16021d6b1e46SJorge Ramirez-Ortiz 16031d6b1e46SJorge Ramirez-Ortiz return ret; 16041d6b1e46SJorge Ramirez-Ortiz } 16051d6b1e46SJorge Ramirez-Ortiz 16061d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_remove(struct platform_device *pdev) 16071d6b1e46SJorge Ramirez-Ortiz { 16081d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = platform_get_drvdata(pdev); 16091fec333aSMiquel Raynal struct mtk_nfc_nand_chip *mtk_chip; 16101fec333aSMiquel Raynal struct nand_chip *chip; 16111fec333aSMiquel Raynal int ret; 16121d6b1e46SJorge Ramirez-Ortiz 16131d6b1e46SJorge Ramirez-Ortiz while (!list_empty(&nfc->chips)) { 16141fec333aSMiquel Raynal mtk_chip = list_first_entry(&nfc->chips, 16151fec333aSMiquel Raynal struct mtk_nfc_nand_chip, node); 16161fec333aSMiquel Raynal chip = &mtk_chip->nand; 16171fec333aSMiquel Raynal ret = mtd_device_unregister(nand_to_mtd(chip)); 16181fec333aSMiquel Raynal WARN_ON(ret); 16191fec333aSMiquel Raynal nand_cleanup(chip); 16201fec333aSMiquel Raynal list_del(&mtk_chip->node); 16211d6b1e46SJorge Ramirez-Ortiz } 16221d6b1e46SJorge Ramirez-Ortiz 16231d6b1e46SJorge Ramirez-Ortiz mtk_ecc_release(nfc->ecc); 16241d6b1e46SJorge Ramirez-Ortiz mtk_nfc_disable_clk(&nfc->clk); 16251d6b1e46SJorge Ramirez-Ortiz 16261d6b1e46SJorge Ramirez-Ortiz return 0; 16271d6b1e46SJorge Ramirez-Ortiz } 16281d6b1e46SJorge Ramirez-Ortiz 16291d6b1e46SJorge Ramirez-Ortiz #ifdef CONFIG_PM_SLEEP 16301d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_suspend(struct device *dev) 16311d6b1e46SJorge Ramirez-Ortiz { 16321d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = dev_get_drvdata(dev); 16331d6b1e46SJorge Ramirez-Ortiz 16341d6b1e46SJorge Ramirez-Ortiz mtk_nfc_disable_clk(&nfc->clk); 16351d6b1e46SJorge Ramirez-Ortiz 16361d6b1e46SJorge Ramirez-Ortiz return 0; 16371d6b1e46SJorge Ramirez-Ortiz } 16381d6b1e46SJorge Ramirez-Ortiz 16391d6b1e46SJorge Ramirez-Ortiz static int mtk_nfc_resume(struct device *dev) 16401d6b1e46SJorge Ramirez-Ortiz { 16411d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc *nfc = dev_get_drvdata(dev); 16421d6b1e46SJorge Ramirez-Ortiz struct mtk_nfc_nand_chip *chip; 16431d6b1e46SJorge Ramirez-Ortiz struct nand_chip *nand; 16441d6b1e46SJorge Ramirez-Ortiz int ret; 16451d6b1e46SJorge Ramirez-Ortiz u32 i; 16461d6b1e46SJorge Ramirez-Ortiz 16471d6b1e46SJorge Ramirez-Ortiz udelay(200); 16481d6b1e46SJorge Ramirez-Ortiz 16491d6b1e46SJorge Ramirez-Ortiz ret = mtk_nfc_enable_clk(dev, &nfc->clk); 16501d6b1e46SJorge Ramirez-Ortiz if (ret) 16511d6b1e46SJorge Ramirez-Ortiz return ret; 16521d6b1e46SJorge Ramirez-Ortiz 16531d6b1e46SJorge Ramirez-Ortiz /* reset NAND chip if VCC was powered off */ 16541d6b1e46SJorge Ramirez-Ortiz list_for_each_entry(chip, &nfc->chips, node) { 16551d6b1e46SJorge Ramirez-Ortiz nand = &chip->nand; 1656f883199dSXiaolei Li for (i = 0; i < chip->nsels; i++) 1657f883199dSXiaolei Li nand_reset(nand, i); 16581d6b1e46SJorge Ramirez-Ortiz } 16591d6b1e46SJorge Ramirez-Ortiz 16601d6b1e46SJorge Ramirez-Ortiz return 0; 16611d6b1e46SJorge Ramirez-Ortiz } 16621d6b1e46SJorge Ramirez-Ortiz 16631d6b1e46SJorge Ramirez-Ortiz static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume); 16641d6b1e46SJorge Ramirez-Ortiz #endif 16651d6b1e46SJorge Ramirez-Ortiz 16661d6b1e46SJorge Ramirez-Ortiz static struct platform_driver mtk_nfc_driver = { 16671d6b1e46SJorge Ramirez-Ortiz .probe = mtk_nfc_probe, 16681d6b1e46SJorge Ramirez-Ortiz .remove = mtk_nfc_remove, 16691d6b1e46SJorge Ramirez-Ortiz .driver = { 16701d6b1e46SJorge Ramirez-Ortiz .name = MTK_NAME, 16711d6b1e46SJorge Ramirez-Ortiz .of_match_table = mtk_nfc_id_table, 16721d6b1e46SJorge Ramirez-Ortiz #ifdef CONFIG_PM_SLEEP 16731d6b1e46SJorge Ramirez-Ortiz .pm = &mtk_nfc_pm_ops, 16741d6b1e46SJorge Ramirez-Ortiz #endif 16751d6b1e46SJorge Ramirez-Ortiz }, 16761d6b1e46SJorge Ramirez-Ortiz }; 16771d6b1e46SJorge Ramirez-Ortiz 16781d6b1e46SJorge Ramirez-Ortiz module_platform_driver(mtk_nfc_driver); 16791d6b1e46SJorge Ramirez-Ortiz 1680b74e6985SXiaolei Li MODULE_LICENSE("Dual MIT/GPL"); 16811d6b1e46SJorge Ramirez-Ortiz MODULE_AUTHOR("Xiaolei Li <xiaolei.li@mediatek.com>"); 16821d6b1e46SJorge Ramirez-Ortiz MODULE_DESCRIPTION("MTK Nand Flash Controller Driver"); 1683