1f5f88871SBoris Brezillon // SPDX-License-Identifier: GPL-2.0+ 21fef62c1SBoris BREZILLON /* 31fef62c1SBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com> 41fef62c1SBoris BREZILLON * 51fef62c1SBoris BREZILLON * Derived from: 61fef62c1SBoris BREZILLON * https://github.com/yuq/sunxi-nfc-mtd 71fef62c1SBoris BREZILLON * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com> 81fef62c1SBoris BREZILLON * 91fef62c1SBoris BREZILLON * https://github.com/hno/Allwinner-Info 101fef62c1SBoris BREZILLON * Copyright (C) 2013 Henrik Nordström <Henrik Nordström> 111fef62c1SBoris BREZILLON * 121fef62c1SBoris BREZILLON * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com> 131fef62c1SBoris BREZILLON * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org> 141fef62c1SBoris BREZILLON */ 151fef62c1SBoris BREZILLON 161fef62c1SBoris BREZILLON #include <linux/dma-mapping.h> 171fef62c1SBoris BREZILLON #include <linux/slab.h> 181fef62c1SBoris BREZILLON #include <linux/module.h> 191fef62c1SBoris BREZILLON #include <linux/moduleparam.h> 201fef62c1SBoris BREZILLON #include <linux/platform_device.h> 211fef62c1SBoris BREZILLON #include <linux/of.h> 221fef62c1SBoris BREZILLON #include <linux/of_device.h> 231fef62c1SBoris BREZILLON #include <linux/mtd/mtd.h> 24d4092d76SBoris Brezillon #include <linux/mtd/rawnand.h> 251fef62c1SBoris BREZILLON #include <linux/mtd/partitions.h> 261fef62c1SBoris BREZILLON #include <linux/clk.h> 271fef62c1SBoris BREZILLON #include <linux/delay.h> 281fef62c1SBoris BREZILLON #include <linux/dmaengine.h> 291fef62c1SBoris BREZILLON #include <linux/interrupt.h> 30166f08c7SBoris Brezillon #include <linux/iopoll.h> 31ab9d6a78SIcenowy Zheng #include <linux/reset.h> 321fef62c1SBoris BREZILLON 331fef62c1SBoris BREZILLON #define NFC_REG_CTL 0x0000 341fef62c1SBoris BREZILLON #define NFC_REG_ST 0x0004 351fef62c1SBoris BREZILLON #define NFC_REG_INT 0x0008 361fef62c1SBoris BREZILLON #define NFC_REG_TIMING_CTL 0x000C 371fef62c1SBoris BREZILLON #define NFC_REG_TIMING_CFG 0x0010 381fef62c1SBoris BREZILLON #define NFC_REG_ADDR_LOW 0x0014 391fef62c1SBoris BREZILLON #define NFC_REG_ADDR_HIGH 0x0018 401fef62c1SBoris BREZILLON #define NFC_REG_SECTOR_NUM 0x001C 411fef62c1SBoris BREZILLON #define NFC_REG_CNT 0x0020 421fef62c1SBoris BREZILLON #define NFC_REG_CMD 0x0024 431fef62c1SBoris BREZILLON #define NFC_REG_RCMD_SET 0x0028 441fef62c1SBoris BREZILLON #define NFC_REG_WCMD_SET 0x002C 45a760e77dSMiquel Raynal #define NFC_REG_A10_IO_DATA 0x0030 46c7a87cebSMiquel Raynal #define NFC_REG_A23_IO_DATA 0x0300 471fef62c1SBoris BREZILLON #define NFC_REG_ECC_CTL 0x0034 481fef62c1SBoris BREZILLON #define NFC_REG_ECC_ST 0x0038 491fef62c1SBoris BREZILLON #define NFC_REG_DEBUG 0x003C 50b6a02c08SBoris BREZILLON #define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) 51b6a02c08SBoris BREZILLON #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) 521fef62c1SBoris BREZILLON #define NFC_REG_SPARE_AREA 0x00A0 534be4e03eSBoris BREZILLON #define NFC_REG_PAT_ID 0x00A4 54910ef7a4SManuel Dipolt #define NFC_REG_MDMA_ADDR 0x00C0 55c7a87cebSMiquel Raynal #define NFC_REG_MDMA_CNT 0x00C4 561fef62c1SBoris BREZILLON #define NFC_RAM0_BASE 0x0400 571fef62c1SBoris BREZILLON #define NFC_RAM1_BASE 0x0800 581fef62c1SBoris BREZILLON 591fef62c1SBoris BREZILLON /* define bit use in NFC_CTL */ 601fef62c1SBoris BREZILLON #define NFC_EN BIT(0) 611fef62c1SBoris BREZILLON #define NFC_RESET BIT(1) 62b6a02c08SBoris BREZILLON #define NFC_BUS_WIDTH_MSK BIT(2) 63b6a02c08SBoris BREZILLON #define NFC_BUS_WIDTH_8 (0 << 2) 64b6a02c08SBoris BREZILLON #define NFC_BUS_WIDTH_16 (1 << 2) 65b6a02c08SBoris BREZILLON #define NFC_RB_SEL_MSK BIT(3) 66b6a02c08SBoris BREZILLON #define NFC_RB_SEL(x) ((x) << 3) 67b6a02c08SBoris BREZILLON #define NFC_CE_SEL_MSK GENMASK(26, 24) 68b6a02c08SBoris BREZILLON #define NFC_CE_SEL(x) ((x) << 24) 691fef62c1SBoris BREZILLON #define NFC_CE_CTL BIT(6) 70b6a02c08SBoris BREZILLON #define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) 71b6a02c08SBoris BREZILLON #define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) 721fef62c1SBoris BREZILLON #define NFC_SAM BIT(12) 731fef62c1SBoris BREZILLON #define NFC_RAM_METHOD BIT(14) 74c7a87cebSMiquel Raynal #define NFC_DMA_TYPE_NORMAL BIT(15) 751fef62c1SBoris BREZILLON #define NFC_DEBUG_CTL BIT(31) 761fef62c1SBoris BREZILLON 771fef62c1SBoris BREZILLON /* define bit use in NFC_ST */ 781fef62c1SBoris BREZILLON #define NFC_RB_B2R BIT(0) 791fef62c1SBoris BREZILLON #define NFC_CMD_INT_FLAG BIT(1) 801fef62c1SBoris BREZILLON #define NFC_DMA_INT_FLAG BIT(2) 811fef62c1SBoris BREZILLON #define NFC_CMD_FIFO_STATUS BIT(3) 821fef62c1SBoris BREZILLON #define NFC_STA BIT(4) 831fef62c1SBoris BREZILLON #define NFC_NATCH_INT_FLAG BIT(5) 84b6a02c08SBoris BREZILLON #define NFC_RB_STATE(x) BIT(x + 8) 851fef62c1SBoris BREZILLON 861fef62c1SBoris BREZILLON /* define bit use in NFC_INT */ 871fef62c1SBoris BREZILLON #define NFC_B2R_INT_ENABLE BIT(0) 881fef62c1SBoris BREZILLON #define NFC_CMD_INT_ENABLE BIT(1) 891fef62c1SBoris BREZILLON #define NFC_DMA_INT_ENABLE BIT(2) 901fef62c1SBoris BREZILLON #define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ 911fef62c1SBoris BREZILLON NFC_CMD_INT_ENABLE | \ 921fef62c1SBoris BREZILLON NFC_DMA_INT_ENABLE) 931fef62c1SBoris BREZILLON 94d052e508SRoy Spliet /* define bit use in NFC_TIMING_CTL */ 95d052e508SRoy Spliet #define NFC_TIMING_CTL_EDO BIT(8) 96d052e508SRoy Spliet 979c618292SRoy Spliet /* define NFC_TIMING_CFG register layout */ 989c618292SRoy Spliet #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ 999c618292SRoy Spliet (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ 1009c618292SRoy Spliet (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ 1019c618292SRoy Spliet (((tCAD) & 0x7) << 8)) 1029c618292SRoy Spliet 1031fef62c1SBoris BREZILLON /* define bit use in NFC_CMD */ 104b6a02c08SBoris BREZILLON #define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) 105b6a02c08SBoris BREZILLON #define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) 106b6a02c08SBoris BREZILLON #define NFC_CMD(x) (x) 107b6a02c08SBoris BREZILLON #define NFC_ADR_NUM_MSK GENMASK(18, 16) 108b6a02c08SBoris BREZILLON #define NFC_ADR_NUM(x) (((x) - 1) << 16) 1091fef62c1SBoris BREZILLON #define NFC_SEND_ADR BIT(19) 1101fef62c1SBoris BREZILLON #define NFC_ACCESS_DIR BIT(20) 1111fef62c1SBoris BREZILLON #define NFC_DATA_TRANS BIT(21) 1121fef62c1SBoris BREZILLON #define NFC_SEND_CMD1 BIT(22) 1131fef62c1SBoris BREZILLON #define NFC_WAIT_FLAG BIT(23) 1141fef62c1SBoris BREZILLON #define NFC_SEND_CMD2 BIT(24) 1151fef62c1SBoris BREZILLON #define NFC_SEQ BIT(25) 1161fef62c1SBoris BREZILLON #define NFC_DATA_SWAP_METHOD BIT(26) 1171fef62c1SBoris BREZILLON #define NFC_ROW_AUTO_INC BIT(27) 1181fef62c1SBoris BREZILLON #define NFC_SEND_CMD3 BIT(28) 1191fef62c1SBoris BREZILLON #define NFC_SEND_CMD4 BIT(29) 120b6a02c08SBoris BREZILLON #define NFC_CMD_TYPE_MSK GENMASK(31, 30) 121b6a02c08SBoris BREZILLON #define NFC_NORMAL_OP (0 << 30) 122b6a02c08SBoris BREZILLON #define NFC_ECC_OP (1 << 30) 123cf3e3fd2SBoris Brezillon #define NFC_PAGE_OP (2U << 30) 1241fef62c1SBoris BREZILLON 1251fef62c1SBoris BREZILLON /* define bit use in NFC_RCMD_SET */ 126b6a02c08SBoris BREZILLON #define NFC_READ_CMD_MSK GENMASK(7, 0) 127b6a02c08SBoris BREZILLON #define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) 128b6a02c08SBoris BREZILLON #define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) 1291fef62c1SBoris BREZILLON 1301fef62c1SBoris BREZILLON /* define bit use in NFC_WCMD_SET */ 131b6a02c08SBoris BREZILLON #define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) 132b6a02c08SBoris BREZILLON #define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) 133b6a02c08SBoris BREZILLON #define NFC_READ_CMD0_MSK GENMASK(23, 16) 134b6a02c08SBoris BREZILLON #define NFC_READ_CMD1_MSK GENMASK(31, 24) 1351fef62c1SBoris BREZILLON 1361fef62c1SBoris BREZILLON /* define bit use in NFC_ECC_CTL */ 1371fef62c1SBoris BREZILLON #define NFC_ECC_EN BIT(0) 1381fef62c1SBoris BREZILLON #define NFC_ECC_PIPELINE BIT(3) 1391fef62c1SBoris BREZILLON #define NFC_ECC_EXCEPTION BIT(4) 140b6a02c08SBoris BREZILLON #define NFC_ECC_BLOCK_SIZE_MSK BIT(5) 141f59dab8dSBoris Brezillon #define NFC_ECC_BLOCK_512 BIT(5) 1421fef62c1SBoris BREZILLON #define NFC_RANDOM_EN BIT(9) 1431fef62c1SBoris BREZILLON #define NFC_RANDOM_DIRECTION BIT(10) 144b6a02c08SBoris BREZILLON #define NFC_ECC_MODE_MSK GENMASK(15, 12) 145b6a02c08SBoris BREZILLON #define NFC_ECC_MODE(x) ((x) << 12) 146b6a02c08SBoris BREZILLON #define NFC_RANDOM_SEED_MSK GENMASK(30, 16) 147b6a02c08SBoris BREZILLON #define NFC_RANDOM_SEED(x) ((x) << 16) 148b6a02c08SBoris BREZILLON 149b6a02c08SBoris BREZILLON /* define bit use in NFC_ECC_ST */ 150b6a02c08SBoris BREZILLON #define NFC_ECC_ERR(x) BIT(x) 151614049a8SBoris Brezillon #define NFC_ECC_ERR_MSK GENMASK(15, 0) 152b6a02c08SBoris BREZILLON #define NFC_ECC_PAT_FOUND(x) BIT(x + 16) 153f8b04746SBoris Brezillon #define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) 1541fef62c1SBoris BREZILLON 1551fef62c1SBoris BREZILLON #define NFC_DEFAULT_TIMEOUT_MS 1000 1561fef62c1SBoris BREZILLON 1571fef62c1SBoris BREZILLON #define NFC_SRAM_SIZE 1024 1581fef62c1SBoris BREZILLON 1591fef62c1SBoris BREZILLON #define NFC_MAX_CS 7 1601fef62c1SBoris BREZILLON 16167c88008SBoris Brezillon /** 16267c88008SBoris Brezillon * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select 1631fef62c1SBoris BREZILLON * 1641fef62c1SBoris BREZILLON * @cs: the NAND CS id used to communicate with a NAND Chip 16567c88008SBoris Brezillon * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the NFC 1661fef62c1SBoris BREZILLON */ 1671fef62c1SBoris BREZILLON struct sunxi_nand_chip_sel { 1681fef62c1SBoris BREZILLON u8 cs; 169ddd5ed3aSBoris Brezillon s8 rb; 1701fef62c1SBoris BREZILLON }; 1711fef62c1SBoris BREZILLON 17267c88008SBoris Brezillon /** 17367c88008SBoris Brezillon * struct sunxi_nand_hw_ecc - stores information related to HW ECC support 1741fef62c1SBoris BREZILLON * 1751fef62c1SBoris BREZILLON * @mode: the sunxi ECC mode field deduced from ECC requirements 1761fef62c1SBoris BREZILLON */ 1771fef62c1SBoris BREZILLON struct sunxi_nand_hw_ecc { 1781fef62c1SBoris BREZILLON int mode; 1791fef62c1SBoris BREZILLON }; 1801fef62c1SBoris BREZILLON 18167c88008SBoris Brezillon /** 18267c88008SBoris Brezillon * struct sunxi_nand_chip - stores NAND chip device related information 1831fef62c1SBoris BREZILLON * 1841fef62c1SBoris BREZILLON * @node: used to store NAND chips into a list 1851fef62c1SBoris BREZILLON * @nand: base NAND chip structure 186cbd87780SMiquel Raynal * @ecc: ECC controller structure 1871fef62c1SBoris BREZILLON * @clk_rate: clk_rate required for this NAND chip 18867c88008SBoris Brezillon * @timing_cfg: TIMING_CFG register value for this NAND chip 18967c88008SBoris Brezillon * @timing_ctl: TIMING_CTL register value for this NAND chip 1901fef62c1SBoris BREZILLON * @nsels: number of CS lines required by the NAND chip 1911fef62c1SBoris BREZILLON * @sels: array of CS lines descriptions 1921fef62c1SBoris BREZILLON */ 1931fef62c1SBoris BREZILLON struct sunxi_nand_chip { 1941fef62c1SBoris BREZILLON struct list_head node; 1951fef62c1SBoris BREZILLON struct nand_chip nand; 196cbd87780SMiquel Raynal struct sunxi_nand_hw_ecc *ecc; 1971fef62c1SBoris BREZILLON unsigned long clk_rate; 1989c618292SRoy Spliet u32 timing_cfg; 199d052e508SRoy Spliet u32 timing_ctl; 2001fef62c1SBoris BREZILLON int nsels; 20149f1c330SGustavo A. R. Silva struct sunxi_nand_chip_sel sels[]; 2021fef62c1SBoris BREZILLON }; 2031fef62c1SBoris BREZILLON 2041fef62c1SBoris BREZILLON static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) 2051fef62c1SBoris BREZILLON { 2061fef62c1SBoris BREZILLON return container_of(nand, struct sunxi_nand_chip, nand); 2071fef62c1SBoris BREZILLON } 2081fef62c1SBoris BREZILLON 209a760e77dSMiquel Raynal /* 210a760e77dSMiquel Raynal * NAND Controller capabilities structure: stores NAND controller capabilities 211a760e77dSMiquel Raynal * for distinction between compatible strings. 212a760e77dSMiquel Raynal * 213910ef7a4SManuel Dipolt * @has_mdma: Use mbus dma mode, otherwise general dma 214c7a87cebSMiquel Raynal * through MBUS on A23/A33 needs extra configuration. 215a760e77dSMiquel Raynal * @reg_io_data: I/O data register 216a760e77dSMiquel Raynal * @dma_maxburst: DMA maxburst 217a760e77dSMiquel Raynal */ 218a760e77dSMiquel Raynal struct sunxi_nfc_caps { 219910ef7a4SManuel Dipolt bool has_mdma; 220a760e77dSMiquel Raynal unsigned int reg_io_data; 221a760e77dSMiquel Raynal unsigned int dma_maxburst; 222a760e77dSMiquel Raynal }; 223a760e77dSMiquel Raynal 22467c88008SBoris Brezillon /** 22567c88008SBoris Brezillon * struct sunxi_nfc - stores sunxi NAND controller information 2261fef62c1SBoris BREZILLON * 2271fef62c1SBoris BREZILLON * @controller: base controller structure 2281fef62c1SBoris BREZILLON * @dev: parent device (used to print error messages) 2291fef62c1SBoris BREZILLON * @regs: NAND controller registers 23067c88008SBoris Brezillon * @ahb_clk: NAND controller AHB clock 23167c88008SBoris Brezillon * @mod_clk: NAND controller mod clock 23267c88008SBoris Brezillon * @reset: NAND controller reset line 2331fef62c1SBoris BREZILLON * @assigned_cs: bitmask describing already assigned CS lines 2341fef62c1SBoris BREZILLON * @clk_rate: NAND controller current clock rate 23567c88008SBoris Brezillon * @chips: a list containing all the NAND chips attached to this NAND 23667c88008SBoris Brezillon * controller 23767c88008SBoris Brezillon * @complete: a completion object used to wait for NAND controller events 23867c88008SBoris Brezillon * @dmac: the DMA channel attached to the NAND controller 2390d5c506dSLee Jones * @caps: NAND Controller capabilities 2401fef62c1SBoris BREZILLON */ 2411fef62c1SBoris BREZILLON struct sunxi_nfc { 2427da45139SMiquel Raynal struct nand_controller controller; 2431fef62c1SBoris BREZILLON struct device *dev; 2441fef62c1SBoris BREZILLON void __iomem *regs; 2451fef62c1SBoris BREZILLON struct clk *ahb_clk; 2461fef62c1SBoris BREZILLON struct clk *mod_clk; 247ab9d6a78SIcenowy Zheng struct reset_control *reset; 2481fef62c1SBoris BREZILLON unsigned long assigned_cs; 2491fef62c1SBoris BREZILLON unsigned long clk_rate; 2501fef62c1SBoris BREZILLON struct list_head chips; 2511fef62c1SBoris BREZILLON struct completion complete; 252614049a8SBoris Brezillon struct dma_chan *dmac; 253a760e77dSMiquel Raynal const struct sunxi_nfc_caps *caps; 2541fef62c1SBoris BREZILLON }; 2551fef62c1SBoris BREZILLON 2567da45139SMiquel Raynal static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl) 2571fef62c1SBoris BREZILLON { 2581fef62c1SBoris BREZILLON return container_of(ctrl, struct sunxi_nfc, controller); 2591fef62c1SBoris BREZILLON } 2601fef62c1SBoris BREZILLON 2611fef62c1SBoris BREZILLON static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) 2621fef62c1SBoris BREZILLON { 2631fef62c1SBoris BREZILLON struct sunxi_nfc *nfc = dev_id; 2641fef62c1SBoris BREZILLON u32 st = readl(nfc->regs + NFC_REG_ST); 2651fef62c1SBoris BREZILLON u32 ien = readl(nfc->regs + NFC_REG_INT); 2661fef62c1SBoris BREZILLON 2671fef62c1SBoris BREZILLON if (!(ien & st)) 2681fef62c1SBoris BREZILLON return IRQ_NONE; 2691fef62c1SBoris BREZILLON 2701fef62c1SBoris BREZILLON if ((ien & st) == ien) 2711fef62c1SBoris BREZILLON complete(&nfc->complete); 2721fef62c1SBoris BREZILLON 2731fef62c1SBoris BREZILLON writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); 2741fef62c1SBoris BREZILLON writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); 2751fef62c1SBoris BREZILLON 2761fef62c1SBoris BREZILLON return IRQ_HANDLED; 2771fef62c1SBoris BREZILLON } 2781fef62c1SBoris BREZILLON 279c0c9dfa8SBoris Brezillon static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, 280c0c9dfa8SBoris Brezillon bool use_polling, unsigned int timeout_ms) 2811fef62c1SBoris BREZILLON { 282c0c9dfa8SBoris Brezillon int ret; 2831fef62c1SBoris BREZILLON 284c0c9dfa8SBoris Brezillon if (events & ~NFC_INT_MASK) 285c0c9dfa8SBoris Brezillon return -EINVAL; 2861fef62c1SBoris BREZILLON 2871fef62c1SBoris BREZILLON if (!timeout_ms) 2881fef62c1SBoris BREZILLON timeout_ms = NFC_DEFAULT_TIMEOUT_MS; 2891fef62c1SBoris BREZILLON 290c0c9dfa8SBoris Brezillon if (!use_polling) { 291c0c9dfa8SBoris Brezillon init_completion(&nfc->complete); 292c0c9dfa8SBoris Brezillon 293c0c9dfa8SBoris Brezillon writel(events, nfc->regs + NFC_REG_INT); 294c0c9dfa8SBoris Brezillon 295c0c9dfa8SBoris Brezillon ret = wait_for_completion_timeout(&nfc->complete, 296c0c9dfa8SBoris Brezillon msecs_to_jiffies(timeout_ms)); 29719649e2cSBoris Brezillon if (!ret) 29819649e2cSBoris Brezillon ret = -ETIMEDOUT; 29919649e2cSBoris Brezillon else 30019649e2cSBoris Brezillon ret = 0; 301c0c9dfa8SBoris Brezillon 302c0c9dfa8SBoris Brezillon writel(0, nfc->regs + NFC_REG_INT); 303c0c9dfa8SBoris Brezillon } else { 304c0c9dfa8SBoris Brezillon u32 status; 305c0c9dfa8SBoris Brezillon 306c0c9dfa8SBoris Brezillon ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, 307c0c9dfa8SBoris Brezillon (status & events) == events, 1, 308c0c9dfa8SBoris Brezillon timeout_ms * 1000); 3091fef62c1SBoris BREZILLON } 3101fef62c1SBoris BREZILLON 311c0c9dfa8SBoris Brezillon writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); 312c0c9dfa8SBoris Brezillon 313c0c9dfa8SBoris Brezillon if (ret) 314c0c9dfa8SBoris Brezillon dev_err(nfc->dev, "wait interrupt timedout\n"); 315c0c9dfa8SBoris Brezillon 316c0c9dfa8SBoris Brezillon return ret; 3171fef62c1SBoris BREZILLON } 3181fef62c1SBoris BREZILLON 3191fef62c1SBoris BREZILLON static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) 3201fef62c1SBoris BREZILLON { 321166f08c7SBoris Brezillon u32 status; 322166f08c7SBoris Brezillon int ret; 3231fef62c1SBoris BREZILLON 324166f08c7SBoris Brezillon ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, 325166f08c7SBoris Brezillon !(status & NFC_CMD_FIFO_STATUS), 1, 326166f08c7SBoris Brezillon NFC_DEFAULT_TIMEOUT_MS * 1000); 327166f08c7SBoris Brezillon if (ret) 3281fef62c1SBoris BREZILLON dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); 329166f08c7SBoris Brezillon 330166f08c7SBoris Brezillon return ret; 3311fef62c1SBoris BREZILLON } 3321fef62c1SBoris BREZILLON 3331fef62c1SBoris BREZILLON static int sunxi_nfc_rst(struct sunxi_nfc *nfc) 3341fef62c1SBoris BREZILLON { 335166f08c7SBoris Brezillon u32 ctl; 336166f08c7SBoris Brezillon int ret; 3371fef62c1SBoris BREZILLON 3381fef62c1SBoris BREZILLON writel(0, nfc->regs + NFC_REG_ECC_CTL); 3391fef62c1SBoris BREZILLON writel(NFC_RESET, nfc->regs + NFC_REG_CTL); 3401fef62c1SBoris BREZILLON 341166f08c7SBoris Brezillon ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, 342166f08c7SBoris Brezillon !(ctl & NFC_RESET), 1, 343166f08c7SBoris Brezillon NFC_DEFAULT_TIMEOUT_MS * 1000); 344166f08c7SBoris Brezillon if (ret) 3451fef62c1SBoris BREZILLON dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); 346166f08c7SBoris Brezillon 347166f08c7SBoris Brezillon return ret; 3481fef62c1SBoris BREZILLON } 3491fef62c1SBoris BREZILLON 350cde567e3SBoris Brezillon static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, 351614049a8SBoris Brezillon int chunksize, int nchunks, 352614049a8SBoris Brezillon enum dma_data_direction ddir, 353614049a8SBoris Brezillon struct scatterlist *sg) 354614049a8SBoris Brezillon { 355614049a8SBoris Brezillon struct dma_async_tx_descriptor *dmad; 356614049a8SBoris Brezillon enum dma_transfer_direction tdir; 357614049a8SBoris Brezillon dma_cookie_t dmat; 358614049a8SBoris Brezillon int ret; 359614049a8SBoris Brezillon 360614049a8SBoris Brezillon if (ddir == DMA_FROM_DEVICE) 361614049a8SBoris Brezillon tdir = DMA_DEV_TO_MEM; 362614049a8SBoris Brezillon else 363614049a8SBoris Brezillon tdir = DMA_MEM_TO_DEV; 364614049a8SBoris Brezillon 365614049a8SBoris Brezillon sg_init_one(sg, buf, nchunks * chunksize); 366614049a8SBoris Brezillon ret = dma_map_sg(nfc->dev, sg, 1, ddir); 367614049a8SBoris Brezillon if (!ret) 368614049a8SBoris Brezillon return -ENOMEM; 369614049a8SBoris Brezillon 370910ef7a4SManuel Dipolt if (!nfc->caps->has_mdma) { 371614049a8SBoris Brezillon dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); 37228f3d01eSWei Yongjun if (!dmad) { 37328f3d01eSWei Yongjun ret = -EINVAL; 374614049a8SBoris Brezillon goto err_unmap_buf; 375614049a8SBoris Brezillon } 376910ef7a4SManuel Dipolt } 377614049a8SBoris Brezillon 378614049a8SBoris Brezillon writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, 379614049a8SBoris Brezillon nfc->regs + NFC_REG_CTL); 380614049a8SBoris Brezillon writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); 381614049a8SBoris Brezillon writel(chunksize, nfc->regs + NFC_REG_CNT); 382c7a87cebSMiquel Raynal 383910ef7a4SManuel Dipolt if (nfc->caps->has_mdma) { 384910ef7a4SManuel Dipolt writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL, 385910ef7a4SManuel Dipolt nfc->regs + NFC_REG_CTL); 386910ef7a4SManuel Dipolt writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); 387910ef7a4SManuel Dipolt writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR); 388910ef7a4SManuel Dipolt } else { 389614049a8SBoris Brezillon dmat = dmaengine_submit(dmad); 390614049a8SBoris Brezillon 391614049a8SBoris Brezillon ret = dma_submit_error(dmat); 392614049a8SBoris Brezillon if (ret) 393614049a8SBoris Brezillon goto err_clr_dma_flag; 394910ef7a4SManuel Dipolt } 395614049a8SBoris Brezillon 396614049a8SBoris Brezillon return 0; 397614049a8SBoris Brezillon 398614049a8SBoris Brezillon err_clr_dma_flag: 399614049a8SBoris Brezillon writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, 400614049a8SBoris Brezillon nfc->regs + NFC_REG_CTL); 401614049a8SBoris Brezillon 402614049a8SBoris Brezillon err_unmap_buf: 403614049a8SBoris Brezillon dma_unmap_sg(nfc->dev, sg, 1, ddir); 404614049a8SBoris Brezillon return ret; 405614049a8SBoris Brezillon } 406614049a8SBoris Brezillon 407cde567e3SBoris Brezillon static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc, 408614049a8SBoris Brezillon enum dma_data_direction ddir, 409614049a8SBoris Brezillon struct scatterlist *sg) 410614049a8SBoris Brezillon { 411614049a8SBoris Brezillon dma_unmap_sg(nfc->dev, sg, 1, ddir); 412614049a8SBoris Brezillon writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, 413614049a8SBoris Brezillon nfc->regs + NFC_REG_CTL); 414614049a8SBoris Brezillon } 415614049a8SBoris Brezillon 416df505799SBoris Brezillon static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) 4171fef62c1SBoris BREZILLON { 418758b56f5SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 4191fef62c1SBoris BREZILLON struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 4201fef62c1SBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 4211fef62c1SBoris BREZILLON struct sunxi_nand_chip_sel *sel; 4221fef62c1SBoris BREZILLON u32 ctl; 4231fef62c1SBoris BREZILLON 424df505799SBoris Brezillon if (cs > 0 && cs >= sunxi_nand->nsels) 4251fef62c1SBoris BREZILLON return; 4261fef62c1SBoris BREZILLON 4271fef62c1SBoris BREZILLON ctl = readl(nfc->regs + NFC_REG_CTL) & 428b6a02c08SBoris BREZILLON ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); 4291fef62c1SBoris BREZILLON 430df505799SBoris Brezillon sel = &sunxi_nand->sels[cs]; 431df505799SBoris Brezillon ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift); 432df505799SBoris Brezillon if (sel->rb >= 0) 433ddd5ed3aSBoris Brezillon ctl |= NFC_RB_SEL(sel->rb); 4341fef62c1SBoris BREZILLON 4351fef62c1SBoris BREZILLON writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); 4361fef62c1SBoris BREZILLON 4371fef62c1SBoris BREZILLON if (nfc->clk_rate != sunxi_nand->clk_rate) { 4381fef62c1SBoris BREZILLON clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); 4391fef62c1SBoris BREZILLON nfc->clk_rate = sunxi_nand->clk_rate; 4401fef62c1SBoris BREZILLON } 4411fef62c1SBoris BREZILLON 442d052e508SRoy Spliet writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); 4439c618292SRoy Spliet writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); 4441fef62c1SBoris BREZILLON writel(ctl, nfc->regs + NFC_REG_CTL); 4451fef62c1SBoris BREZILLON } 4461fef62c1SBoris BREZILLON 4477e534323SBoris Brezillon static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len) 4481fef62c1SBoris BREZILLON { 4491fef62c1SBoris BREZILLON struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 4501fef62c1SBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 4511fef62c1SBoris BREZILLON int ret; 4521fef62c1SBoris BREZILLON int cnt; 4531fef62c1SBoris BREZILLON int offs = 0; 4541fef62c1SBoris BREZILLON u32 tmp; 4551fef62c1SBoris BREZILLON 4561fef62c1SBoris BREZILLON while (len > offs) { 4578de15e1fSBoris Brezillon bool poll = false; 4588de15e1fSBoris Brezillon 4591fef62c1SBoris BREZILLON cnt = min(len - offs, NFC_SRAM_SIZE); 4601fef62c1SBoris BREZILLON 4611fef62c1SBoris BREZILLON ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 4621fef62c1SBoris BREZILLON if (ret) 4631fef62c1SBoris BREZILLON break; 4641fef62c1SBoris BREZILLON 4651fef62c1SBoris BREZILLON writel(cnt, nfc->regs + NFC_REG_CNT); 4661fef62c1SBoris BREZILLON tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; 4671fef62c1SBoris BREZILLON writel(tmp, nfc->regs + NFC_REG_CMD); 4681fef62c1SBoris BREZILLON 4698de15e1fSBoris Brezillon /* Arbitrary limit for polling mode */ 4708de15e1fSBoris Brezillon if (cnt < 64) 4718de15e1fSBoris Brezillon poll = true; 4728de15e1fSBoris Brezillon 4738de15e1fSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); 4741fef62c1SBoris BREZILLON if (ret) 4751fef62c1SBoris BREZILLON break; 4761fef62c1SBoris BREZILLON 4771fef62c1SBoris BREZILLON if (buf) 4781fef62c1SBoris BREZILLON memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, 4791fef62c1SBoris BREZILLON cnt); 4801fef62c1SBoris BREZILLON offs += cnt; 4811fef62c1SBoris BREZILLON } 4821fef62c1SBoris BREZILLON } 4831fef62c1SBoris BREZILLON 484c0739d85SBoris Brezillon static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf, 4851fef62c1SBoris BREZILLON int len) 4861fef62c1SBoris BREZILLON { 4871fef62c1SBoris BREZILLON struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 4881fef62c1SBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 4891fef62c1SBoris BREZILLON int ret; 4901fef62c1SBoris BREZILLON int cnt; 4911fef62c1SBoris BREZILLON int offs = 0; 4921fef62c1SBoris BREZILLON u32 tmp; 4931fef62c1SBoris BREZILLON 4941fef62c1SBoris BREZILLON while (len > offs) { 4958de15e1fSBoris Brezillon bool poll = false; 4968de15e1fSBoris Brezillon 4971fef62c1SBoris BREZILLON cnt = min(len - offs, NFC_SRAM_SIZE); 4981fef62c1SBoris BREZILLON 4991fef62c1SBoris BREZILLON ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 5001fef62c1SBoris BREZILLON if (ret) 5011fef62c1SBoris BREZILLON break; 5021fef62c1SBoris BREZILLON 5031fef62c1SBoris BREZILLON writel(cnt, nfc->regs + NFC_REG_CNT); 5041fef62c1SBoris BREZILLON memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); 5051fef62c1SBoris BREZILLON tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | 5061fef62c1SBoris BREZILLON NFC_ACCESS_DIR; 5071fef62c1SBoris BREZILLON writel(tmp, nfc->regs + NFC_REG_CMD); 5081fef62c1SBoris BREZILLON 5098de15e1fSBoris Brezillon /* Arbitrary limit for polling mode */ 5108de15e1fSBoris Brezillon if (cnt < 64) 5118de15e1fSBoris Brezillon poll = true; 5128de15e1fSBoris Brezillon 5138de15e1fSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); 5141fef62c1SBoris BREZILLON if (ret) 5151fef62c1SBoris BREZILLON break; 5161fef62c1SBoris BREZILLON 5171fef62c1SBoris BREZILLON offs += cnt; 5181fef62c1SBoris BREZILLON } 5191fef62c1SBoris BREZILLON } 5201fef62c1SBoris BREZILLON 5214be4e03eSBoris BREZILLON /* These seed values have been extracted from Allwinner's BSP */ 5224be4e03eSBoris BREZILLON static const u16 sunxi_nfc_randomizer_page_seeds[] = { 5234be4e03eSBoris BREZILLON 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, 5244be4e03eSBoris BREZILLON 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, 5254be4e03eSBoris BREZILLON 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, 5264be4e03eSBoris BREZILLON 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, 5274be4e03eSBoris BREZILLON 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, 5284be4e03eSBoris BREZILLON 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, 5294be4e03eSBoris BREZILLON 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, 5304be4e03eSBoris BREZILLON 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, 5314be4e03eSBoris BREZILLON 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, 5324be4e03eSBoris BREZILLON 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, 5334be4e03eSBoris BREZILLON 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, 5344be4e03eSBoris BREZILLON 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, 5354be4e03eSBoris BREZILLON 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, 5364be4e03eSBoris BREZILLON 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, 5374be4e03eSBoris BREZILLON 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, 5384be4e03eSBoris BREZILLON 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, 5394be4e03eSBoris BREZILLON }; 5404be4e03eSBoris BREZILLON 5414be4e03eSBoris BREZILLON /* 5424be4e03eSBoris BREZILLON * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds 5434be4e03eSBoris BREZILLON * have been generated using 5444be4e03eSBoris BREZILLON * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what 5454be4e03eSBoris BREZILLON * the randomizer engine does internally before de/scrambling OOB data. 5464be4e03eSBoris BREZILLON * 5474be4e03eSBoris BREZILLON * Those tables are statically defined to avoid calculating randomizer state 5484be4e03eSBoris BREZILLON * at runtime. 5494be4e03eSBoris BREZILLON */ 5504be4e03eSBoris BREZILLON static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { 5514be4e03eSBoris BREZILLON 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, 5524be4e03eSBoris BREZILLON 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, 5534be4e03eSBoris BREZILLON 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, 5544be4e03eSBoris BREZILLON 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, 5554be4e03eSBoris BREZILLON 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, 5564be4e03eSBoris BREZILLON 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, 5574be4e03eSBoris BREZILLON 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, 5584be4e03eSBoris BREZILLON 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, 5594be4e03eSBoris BREZILLON 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, 5604be4e03eSBoris BREZILLON 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, 5614be4e03eSBoris BREZILLON 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, 5624be4e03eSBoris BREZILLON 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, 5634be4e03eSBoris BREZILLON 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, 5644be4e03eSBoris BREZILLON 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, 5654be4e03eSBoris BREZILLON 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, 5664be4e03eSBoris BREZILLON 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, 5674be4e03eSBoris BREZILLON }; 5684be4e03eSBoris BREZILLON 5694be4e03eSBoris BREZILLON static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { 5704be4e03eSBoris BREZILLON 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, 5714be4e03eSBoris BREZILLON 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, 5724be4e03eSBoris BREZILLON 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, 5734be4e03eSBoris BREZILLON 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, 5744be4e03eSBoris BREZILLON 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, 5754be4e03eSBoris BREZILLON 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, 5764be4e03eSBoris BREZILLON 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, 5774be4e03eSBoris BREZILLON 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, 5784be4e03eSBoris BREZILLON 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, 5794be4e03eSBoris BREZILLON 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, 5804be4e03eSBoris BREZILLON 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, 5814be4e03eSBoris BREZILLON 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, 5824be4e03eSBoris BREZILLON 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, 5834be4e03eSBoris BREZILLON 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, 5844be4e03eSBoris BREZILLON 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, 5854be4e03eSBoris BREZILLON 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, 5864be4e03eSBoris BREZILLON }; 5874be4e03eSBoris BREZILLON 5884be4e03eSBoris BREZILLON static u16 sunxi_nfc_randomizer_step(u16 state, int count) 5894be4e03eSBoris BREZILLON { 5904be4e03eSBoris BREZILLON state &= 0x7fff; 5914be4e03eSBoris BREZILLON 5924be4e03eSBoris BREZILLON /* 5934be4e03eSBoris BREZILLON * This loop is just a simple implementation of a Fibonacci LFSR using 5944be4e03eSBoris BREZILLON * the x16 + x15 + 1 polynomial. 5954be4e03eSBoris BREZILLON */ 5964be4e03eSBoris BREZILLON while (count--) 5974be4e03eSBoris BREZILLON state = ((state >> 1) | 5984be4e03eSBoris BREZILLON (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; 5994be4e03eSBoris BREZILLON 6004be4e03eSBoris BREZILLON return state; 6014be4e03eSBoris BREZILLON } 6024be4e03eSBoris BREZILLON 603cde567e3SBoris Brezillon static u16 sunxi_nfc_randomizer_state(struct nand_chip *nand, int page, 604cde567e3SBoris Brezillon bool ecc) 6054be4e03eSBoris BREZILLON { 606cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 6074be4e03eSBoris BREZILLON const u16 *seeds = sunxi_nfc_randomizer_page_seeds; 60846c135c2SBrian Norris int mod = mtd_div_by_ws(mtd->erasesize, mtd); 6094be4e03eSBoris BREZILLON 6104be4e03eSBoris BREZILLON if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) 6114be4e03eSBoris BREZILLON mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); 6124be4e03eSBoris BREZILLON 6134be4e03eSBoris BREZILLON if (ecc) { 6144be4e03eSBoris BREZILLON if (mtd->ecc_step_size == 512) 6154be4e03eSBoris BREZILLON seeds = sunxi_nfc_randomizer_ecc512_seeds; 6164be4e03eSBoris BREZILLON else 6174be4e03eSBoris BREZILLON seeds = sunxi_nfc_randomizer_ecc1024_seeds; 6184be4e03eSBoris BREZILLON } 6194be4e03eSBoris BREZILLON 6204be4e03eSBoris BREZILLON return seeds[page % mod]; 6214be4e03eSBoris BREZILLON } 6224be4e03eSBoris BREZILLON 623cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page, 624cde567e3SBoris Brezillon bool ecc) 6254be4e03eSBoris BREZILLON { 6264be4e03eSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6274be4e03eSBoris BREZILLON u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 6284be4e03eSBoris BREZILLON u16 state; 6294be4e03eSBoris BREZILLON 6304be4e03eSBoris BREZILLON if (!(nand->options & NAND_NEED_SCRAMBLING)) 6314be4e03eSBoris BREZILLON return; 6324be4e03eSBoris BREZILLON 6334be4e03eSBoris BREZILLON ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 634cde567e3SBoris Brezillon state = sunxi_nfc_randomizer_state(nand, page, ecc); 6354be4e03eSBoris BREZILLON ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; 6364be4e03eSBoris BREZILLON writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); 6374be4e03eSBoris BREZILLON } 6384be4e03eSBoris BREZILLON 639cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_enable(struct nand_chip *nand) 6404be4e03eSBoris BREZILLON { 6414be4e03eSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6424be4e03eSBoris BREZILLON 6434be4e03eSBoris BREZILLON if (!(nand->options & NAND_NEED_SCRAMBLING)) 6444be4e03eSBoris BREZILLON return; 6454be4e03eSBoris BREZILLON 6464be4e03eSBoris BREZILLON writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, 6474be4e03eSBoris BREZILLON nfc->regs + NFC_REG_ECC_CTL); 6484be4e03eSBoris BREZILLON } 6494be4e03eSBoris BREZILLON 650cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_disable(struct nand_chip *nand) 6514be4e03eSBoris BREZILLON { 6524be4e03eSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6534be4e03eSBoris BREZILLON 6544be4e03eSBoris BREZILLON if (!(nand->options & NAND_NEED_SCRAMBLING)) 6554be4e03eSBoris BREZILLON return; 6564be4e03eSBoris BREZILLON 6574be4e03eSBoris BREZILLON writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, 6584be4e03eSBoris BREZILLON nfc->regs + NFC_REG_ECC_CTL); 6594be4e03eSBoris BREZILLON } 6604be4e03eSBoris BREZILLON 661cde567e3SBoris Brezillon static void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm) 6624be4e03eSBoris BREZILLON { 663cde567e3SBoris Brezillon u16 state = sunxi_nfc_randomizer_state(nand, page, true); 6644be4e03eSBoris BREZILLON 6654be4e03eSBoris BREZILLON bbm[0] ^= state; 6664be4e03eSBoris BREZILLON bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); 6674be4e03eSBoris BREZILLON } 6684be4e03eSBoris BREZILLON 669cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand, 6704be4e03eSBoris BREZILLON const uint8_t *buf, int len, 6714be4e03eSBoris BREZILLON bool ecc, int page) 6724be4e03eSBoris BREZILLON { 673cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, ecc); 674cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand); 675cde567e3SBoris Brezillon sunxi_nfc_write_buf(nand, buf, len); 676cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand); 6774be4e03eSBoris BREZILLON } 6784be4e03eSBoris BREZILLON 679cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf, 6804be4e03eSBoris BREZILLON int len, bool ecc, int page) 6814be4e03eSBoris BREZILLON { 682cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, ecc); 683cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand); 684cde567e3SBoris Brezillon sunxi_nfc_read_buf(nand, buf, len); 685cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand); 6864be4e03eSBoris BREZILLON } 6874be4e03eSBoris BREZILLON 688cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand) 689c9118eceSBoris BREZILLON { 690cbd87780SMiquel Raynal struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 691c9118eceSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 692c9118eceSBoris BREZILLON u32 ecc_ctl; 693c9118eceSBoris BREZILLON 694c9118eceSBoris BREZILLON ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 695c9118eceSBoris BREZILLON ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | 696c9118eceSBoris BREZILLON NFC_ECC_BLOCK_SIZE_MSK); 697cbd87780SMiquel Raynal ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(sunxi_nand->ecc->mode) | 698cbd87780SMiquel Raynal NFC_ECC_EXCEPTION | NFC_ECC_PIPELINE; 699c9118eceSBoris BREZILLON 700f59dab8dSBoris Brezillon if (nand->ecc.size == 512) 701f59dab8dSBoris Brezillon ecc_ctl |= NFC_ECC_BLOCK_512; 702f59dab8dSBoris Brezillon 703c9118eceSBoris BREZILLON writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); 704c9118eceSBoris BREZILLON } 705c9118eceSBoris BREZILLON 706cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand) 707c9118eceSBoris BREZILLON { 708c9118eceSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 709c9118eceSBoris BREZILLON 710c9118eceSBoris BREZILLON writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, 711c9118eceSBoris BREZILLON nfc->regs + NFC_REG_ECC_CTL); 712c9118eceSBoris BREZILLON } 713c9118eceSBoris BREZILLON 714f363e0faSBoris BREZILLON static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) 715f363e0faSBoris BREZILLON { 716f363e0faSBoris BREZILLON buf[0] = user_data; 717f363e0faSBoris BREZILLON buf[1] = user_data >> 8; 718f363e0faSBoris BREZILLON buf[2] = user_data >> 16; 719f363e0faSBoris BREZILLON buf[3] = user_data >> 24; 720f363e0faSBoris BREZILLON } 721f363e0faSBoris BREZILLON 722cc6822fbSBoris Brezillon static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) 723cc6822fbSBoris Brezillon { 724cc6822fbSBoris Brezillon return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 725cc6822fbSBoris Brezillon } 726cc6822fbSBoris Brezillon 727cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, 728cc6822fbSBoris Brezillon int step, bool bbm, int page) 729cc6822fbSBoris Brezillon { 730cc6822fbSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 731cc6822fbSBoris Brezillon 732cc6822fbSBoris Brezillon sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), 733cc6822fbSBoris Brezillon oob); 734cc6822fbSBoris Brezillon 735cc6822fbSBoris Brezillon /* De-randomize the Bad Block Marker. */ 736cc6822fbSBoris Brezillon if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) 737cde567e3SBoris Brezillon sunxi_nfc_randomize_bbm(nand, page, oob); 738cc6822fbSBoris Brezillon } 739cc6822fbSBoris Brezillon 740cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, 741cc6822fbSBoris Brezillon const u8 *oob, int step, 742cc6822fbSBoris Brezillon bool bbm, int page) 743cc6822fbSBoris Brezillon { 744cc6822fbSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 745cc6822fbSBoris Brezillon u8 user_data[4]; 746cc6822fbSBoris Brezillon 747cc6822fbSBoris Brezillon /* Randomize the Bad Block Marker. */ 748cc6822fbSBoris Brezillon if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { 749cc6822fbSBoris Brezillon memcpy(user_data, oob, sizeof(user_data)); 750cde567e3SBoris Brezillon sunxi_nfc_randomize_bbm(nand, page, user_data); 751cc6822fbSBoris Brezillon oob = user_data; 752cc6822fbSBoris Brezillon } 753cc6822fbSBoris Brezillon 754cc6822fbSBoris Brezillon writel(sunxi_nfc_buf_to_user_data(oob), 755cc6822fbSBoris Brezillon nfc->regs + NFC_REG_USER_DATA(step)); 756cc6822fbSBoris Brezillon } 757cc6822fbSBoris Brezillon 758cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, 759cc6822fbSBoris Brezillon unsigned int *max_bitflips, int ret) 760cc6822fbSBoris Brezillon { 761cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 762cde567e3SBoris Brezillon 763cc6822fbSBoris Brezillon if (ret < 0) { 764cc6822fbSBoris Brezillon mtd->ecc_stats.failed++; 765cc6822fbSBoris Brezillon } else { 766cc6822fbSBoris Brezillon mtd->ecc_stats.corrected += ret; 767cc6822fbSBoris Brezillon *max_bitflips = max_t(unsigned int, *max_bitflips, ret); 768cc6822fbSBoris Brezillon } 769cc6822fbSBoris Brezillon } 770cc6822fbSBoris Brezillon 771cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, 772614049a8SBoris Brezillon int step, u32 status, bool *erased) 773cc6822fbSBoris Brezillon { 774cc6822fbSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 775cc6822fbSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 776614049a8SBoris Brezillon u32 tmp; 777cc6822fbSBoris Brezillon 778cc6822fbSBoris Brezillon *erased = false; 779cc6822fbSBoris Brezillon 780cc6822fbSBoris Brezillon if (status & NFC_ECC_ERR(step)) 781cc6822fbSBoris Brezillon return -EBADMSG; 782cc6822fbSBoris Brezillon 783cc6822fbSBoris Brezillon if (status & NFC_ECC_PAT_FOUND(step)) { 784cc6822fbSBoris Brezillon u8 pattern; 785cc6822fbSBoris Brezillon 786cc6822fbSBoris Brezillon if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { 787cc6822fbSBoris Brezillon pattern = 0x0; 788cc6822fbSBoris Brezillon } else { 789cc6822fbSBoris Brezillon pattern = 0xff; 790cc6822fbSBoris Brezillon *erased = true; 791cc6822fbSBoris Brezillon } 792cc6822fbSBoris Brezillon 793cc6822fbSBoris Brezillon if (data) 794cc6822fbSBoris Brezillon memset(data, pattern, ecc->size); 795cc6822fbSBoris Brezillon 796cc6822fbSBoris Brezillon if (oob) 797cc6822fbSBoris Brezillon memset(oob, pattern, ecc->bytes + 4); 798cc6822fbSBoris Brezillon 799cc6822fbSBoris Brezillon return 0; 800cc6822fbSBoris Brezillon } 801cc6822fbSBoris Brezillon 802cc6822fbSBoris Brezillon tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); 803cc6822fbSBoris Brezillon 804cc6822fbSBoris Brezillon return NFC_ECC_ERR_CNT(step, tmp); 805cc6822fbSBoris Brezillon } 806cc6822fbSBoris Brezillon 807cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, 808913821bdSBoris BREZILLON u8 *data, int data_off, 809913821bdSBoris BREZILLON u8 *oob, int oob_off, 810913821bdSBoris BREZILLON int *cur_off, 8114be4e03eSBoris BREZILLON unsigned int *max_bitflips, 812828dec15SBoris Brezillon bool bbm, bool oob_required, int page) 813913821bdSBoris BREZILLON { 814913821bdSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 815913821bdSBoris BREZILLON struct nand_ecc_ctrl *ecc = &nand->ecc; 8164be4e03eSBoris BREZILLON int raw_mode = 0; 817cc6822fbSBoris Brezillon bool erased; 818913821bdSBoris BREZILLON int ret; 819913821bdSBoris BREZILLON 820913821bdSBoris BREZILLON if (*cur_off != data_off) 82197d90da8SBoris Brezillon nand_change_read_column_op(nand, data_off, NULL, 0, false); 822913821bdSBoris BREZILLON 823cde567e3SBoris Brezillon sunxi_nfc_randomizer_read_buf(nand, NULL, ecc->size, false, page); 824913821bdSBoris BREZILLON 82574eb9ff5SBoris BREZILLON if (data_off + ecc->size != oob_off) 82697d90da8SBoris Brezillon nand_change_read_column_op(nand, oob_off, NULL, 0, false); 827913821bdSBoris BREZILLON 828913821bdSBoris BREZILLON ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 829913821bdSBoris BREZILLON if (ret) 830913821bdSBoris BREZILLON return ret; 831913821bdSBoris BREZILLON 832cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand); 833913821bdSBoris BREZILLON writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, 834913821bdSBoris BREZILLON nfc->regs + NFC_REG_CMD); 835913821bdSBoris BREZILLON 8368de15e1fSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 837cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand); 838913821bdSBoris BREZILLON if (ret) 839913821bdSBoris BREZILLON return ret; 840913821bdSBoris BREZILLON 8414be4e03eSBoris BREZILLON *cur_off = oob_off + ecc->bytes + 4; 8424be4e03eSBoris BREZILLON 843cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0, 844614049a8SBoris Brezillon readl(nfc->regs + NFC_REG_ECC_ST), 845828dec15SBoris Brezillon &erased); 846cc6822fbSBoris Brezillon if (erased) 8474be4e03eSBoris BREZILLON return 1; 8484be4e03eSBoris BREZILLON 849cc6822fbSBoris Brezillon if (ret < 0) { 8504be4e03eSBoris BREZILLON /* 8514be4e03eSBoris BREZILLON * Re-read the data with the randomizer disabled to identify 8524be4e03eSBoris BREZILLON * bitflips in erased pages. 8534be4e03eSBoris BREZILLON */ 85497d90da8SBoris Brezillon if (nand->options & NAND_NEED_SCRAMBLING) 85597d90da8SBoris Brezillon nand_change_read_column_op(nand, data_off, data, 85697d90da8SBoris Brezillon ecc->size, false); 85797d90da8SBoris Brezillon else 858cc6822fbSBoris Brezillon memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, 859cc6822fbSBoris Brezillon ecc->size); 860cc6822fbSBoris Brezillon 86197d90da8SBoris Brezillon nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, 86297d90da8SBoris Brezillon false); 8634be4e03eSBoris BREZILLON 864146b503eSBoris BREZILLON ret = nand_check_erased_ecc_chunk(data, ecc->size, 865146b503eSBoris BREZILLON oob, ecc->bytes + 4, 866146b503eSBoris BREZILLON NULL, 0, ecc->strength); 8674be4e03eSBoris BREZILLON if (ret >= 0) 8684be4e03eSBoris BREZILLON raw_mode = 1; 869f363e0faSBoris BREZILLON } else { 870cc6822fbSBoris Brezillon memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); 8714be4e03eSBoris BREZILLON 872828dec15SBoris Brezillon if (oob_required) { 87397d90da8SBoris Brezillon nand_change_read_column_op(nand, oob_off, NULL, 0, 87497d90da8SBoris Brezillon false); 875cde567e3SBoris Brezillon sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4, 876cc6822fbSBoris Brezillon true, page); 877cc6822fbSBoris Brezillon 878cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0, 879cc6822fbSBoris Brezillon bbm, page); 880f363e0faSBoris BREZILLON } 881828dec15SBoris Brezillon } 882913821bdSBoris BREZILLON 883cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_update_stats(nand, max_bitflips, ret); 884913821bdSBoris BREZILLON 8854be4e03eSBoris BREZILLON return raw_mode; 886913821bdSBoris BREZILLON } 887913821bdSBoris BREZILLON 888cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand, 8894be4e03eSBoris BREZILLON u8 *oob, int *cur_off, 8904be4e03eSBoris BREZILLON bool randomize, int page) 89135d0e24fSBoris BREZILLON { 892cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 89335d0e24fSBoris BREZILLON struct nand_ecc_ctrl *ecc = &nand->ecc; 89435d0e24fSBoris BREZILLON int offset = ((ecc->bytes + 4) * ecc->steps); 89535d0e24fSBoris BREZILLON int len = mtd->oobsize - offset; 89635d0e24fSBoris BREZILLON 89735d0e24fSBoris BREZILLON if (len <= 0) 89835d0e24fSBoris BREZILLON return; 89935d0e24fSBoris BREZILLON 900c4f3ef2cSBoris Brezillon if (!cur_off || *cur_off != offset) 90197d90da8SBoris Brezillon nand_change_read_column_op(nand, mtd->writesize, NULL, 0, 90297d90da8SBoris Brezillon false); 90335d0e24fSBoris BREZILLON 9044be4e03eSBoris BREZILLON if (!randomize) 9057e534323SBoris Brezillon sunxi_nfc_read_buf(nand, oob + offset, len); 9064be4e03eSBoris BREZILLON else 907cde567e3SBoris Brezillon sunxi_nfc_randomizer_read_buf(nand, oob + offset, len, 9084be4e03eSBoris BREZILLON false, page); 90935d0e24fSBoris BREZILLON 910c4f3ef2cSBoris Brezillon if (cur_off) 91135d0e24fSBoris BREZILLON *cur_off = mtd->oobsize + mtd->writesize; 91235d0e24fSBoris BREZILLON } 91335d0e24fSBoris BREZILLON 914cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf, 915614049a8SBoris Brezillon int oob_required, int page, 916614049a8SBoris Brezillon int nchunks) 917614049a8SBoris Brezillon { 918614049a8SBoris Brezillon bool randomized = nand->options & NAND_NEED_SCRAMBLING; 919614049a8SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 920cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 921614049a8SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 922614049a8SBoris Brezillon unsigned int max_bitflips = 0; 923614049a8SBoris Brezillon int ret, i, raw_mode = 0; 924614049a8SBoris Brezillon struct scatterlist sg; 925910ef7a4SManuel Dipolt u32 status, wait; 926614049a8SBoris Brezillon 927614049a8SBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 928614049a8SBoris Brezillon if (ret) 929614049a8SBoris Brezillon return ret; 930614049a8SBoris Brezillon 931cde567e3SBoris Brezillon ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks, 932614049a8SBoris Brezillon DMA_FROM_DEVICE, &sg); 933614049a8SBoris Brezillon if (ret) 934614049a8SBoris Brezillon return ret; 935614049a8SBoris Brezillon 936cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand); 937cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, false); 938cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand); 939614049a8SBoris Brezillon 940614049a8SBoris Brezillon writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | 941614049a8SBoris Brezillon NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); 942614049a8SBoris Brezillon 943910ef7a4SManuel Dipolt wait = NFC_CMD_INT_FLAG; 944910ef7a4SManuel Dipolt 945910ef7a4SManuel Dipolt if (nfc->caps->has_mdma) 946910ef7a4SManuel Dipolt wait |= NFC_DMA_INT_FLAG; 947910ef7a4SManuel Dipolt else 948614049a8SBoris Brezillon dma_async_issue_pending(nfc->dmac); 949614049a8SBoris Brezillon 950614049a8SBoris Brezillon writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, 951614049a8SBoris Brezillon nfc->regs + NFC_REG_CMD); 952614049a8SBoris Brezillon 953910ef7a4SManuel Dipolt ret = sunxi_nfc_wait_events(nfc, wait, false, 0); 954910ef7a4SManuel Dipolt if (ret && !nfc->caps->has_mdma) 955614049a8SBoris Brezillon dmaengine_terminate_all(nfc->dmac); 956614049a8SBoris Brezillon 957cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand); 958cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand); 959614049a8SBoris Brezillon 960cde567e3SBoris Brezillon sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg); 961614049a8SBoris Brezillon 962614049a8SBoris Brezillon if (ret) 963614049a8SBoris Brezillon return ret; 964614049a8SBoris Brezillon 965614049a8SBoris Brezillon status = readl(nfc->regs + NFC_REG_ECC_ST); 966614049a8SBoris Brezillon 967614049a8SBoris Brezillon for (i = 0; i < nchunks; i++) { 968614049a8SBoris Brezillon int data_off = i * ecc->size; 969614049a8SBoris Brezillon int oob_off = i * (ecc->bytes + 4); 970614049a8SBoris Brezillon u8 *data = buf + data_off; 971614049a8SBoris Brezillon u8 *oob = nand->oob_poi + oob_off; 972614049a8SBoris Brezillon bool erased; 973614049a8SBoris Brezillon 974cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL, 975614049a8SBoris Brezillon oob_required ? oob : NULL, 976614049a8SBoris Brezillon i, status, &erased); 977614049a8SBoris Brezillon 978614049a8SBoris Brezillon /* ECC errors are handled in the second loop. */ 979614049a8SBoris Brezillon if (ret < 0) 980614049a8SBoris Brezillon continue; 981614049a8SBoris Brezillon 982614049a8SBoris Brezillon if (oob_required && !erased) { 983614049a8SBoris Brezillon /* TODO: use DMA to retrieve OOB */ 98497d90da8SBoris Brezillon nand_change_read_column_op(nand, 98597d90da8SBoris Brezillon mtd->writesize + oob_off, 98697d90da8SBoris Brezillon oob, ecc->bytes + 4, false); 987614049a8SBoris Brezillon 988cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, 989614049a8SBoris Brezillon !i, page); 990614049a8SBoris Brezillon } 991614049a8SBoris Brezillon 992614049a8SBoris Brezillon if (erased) 993614049a8SBoris Brezillon raw_mode = 1; 994614049a8SBoris Brezillon 995cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); 996614049a8SBoris Brezillon } 997614049a8SBoris Brezillon 998614049a8SBoris Brezillon if (status & NFC_ECC_ERR_MSK) { 999614049a8SBoris Brezillon for (i = 0; i < nchunks; i++) { 1000614049a8SBoris Brezillon int data_off = i * ecc->size; 1001614049a8SBoris Brezillon int oob_off = i * (ecc->bytes + 4); 1002614049a8SBoris Brezillon u8 *data = buf + data_off; 1003614049a8SBoris Brezillon u8 *oob = nand->oob_poi + oob_off; 1004614049a8SBoris Brezillon 1005614049a8SBoris Brezillon if (!(status & NFC_ECC_ERR(i))) 1006614049a8SBoris Brezillon continue; 1007614049a8SBoris Brezillon 1008614049a8SBoris Brezillon /* 1009614049a8SBoris Brezillon * Re-read the data with the randomizer disabled to 1010614049a8SBoris Brezillon * identify bitflips in erased pages. 101197d90da8SBoris Brezillon * TODO: use DMA to read page in raw mode 1012614049a8SBoris Brezillon */ 101397d90da8SBoris Brezillon if (randomized) 101497d90da8SBoris Brezillon nand_change_read_column_op(nand, data_off, 101597d90da8SBoris Brezillon data, ecc->size, 101697d90da8SBoris Brezillon false); 1017614049a8SBoris Brezillon 1018614049a8SBoris Brezillon /* TODO: use DMA to retrieve OOB */ 101997d90da8SBoris Brezillon nand_change_read_column_op(nand, 102097d90da8SBoris Brezillon mtd->writesize + oob_off, 102197d90da8SBoris Brezillon oob, ecc->bytes + 4, false); 1022614049a8SBoris Brezillon 1023614049a8SBoris Brezillon ret = nand_check_erased_ecc_chunk(data, ecc->size, 1024614049a8SBoris Brezillon oob, ecc->bytes + 4, 1025614049a8SBoris Brezillon NULL, 0, 1026614049a8SBoris Brezillon ecc->strength); 1027614049a8SBoris Brezillon if (ret >= 0) 1028614049a8SBoris Brezillon raw_mode = 1; 1029614049a8SBoris Brezillon 1030cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); 1031614049a8SBoris Brezillon } 1032614049a8SBoris Brezillon } 1033614049a8SBoris Brezillon 1034614049a8SBoris Brezillon if (oob_required) 1035cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, 1036614049a8SBoris Brezillon NULL, !raw_mode, 1037614049a8SBoris Brezillon page); 1038614049a8SBoris Brezillon 1039614049a8SBoris Brezillon return max_bitflips; 1040614049a8SBoris Brezillon } 1041614049a8SBoris Brezillon 1042cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, 1043913821bdSBoris BREZILLON const u8 *data, int data_off, 1044913821bdSBoris BREZILLON const u8 *oob, int oob_off, 10454be4e03eSBoris BREZILLON int *cur_off, bool bbm, 10464be4e03eSBoris BREZILLON int page) 1047913821bdSBoris BREZILLON { 1048913821bdSBoris BREZILLON struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 1049913821bdSBoris BREZILLON struct nand_ecc_ctrl *ecc = &nand->ecc; 1050913821bdSBoris BREZILLON int ret; 1051913821bdSBoris BREZILLON 1052913821bdSBoris BREZILLON if (data_off != *cur_off) 105397d90da8SBoris Brezillon nand_change_write_column_op(nand, data_off, NULL, 0, false); 1054913821bdSBoris BREZILLON 1055cde567e3SBoris Brezillon sunxi_nfc_randomizer_write_buf(nand, data, ecc->size, false, page); 1056913821bdSBoris BREZILLON 105774eb9ff5SBoris BREZILLON if (data_off + ecc->size != oob_off) 105897d90da8SBoris Brezillon nand_change_write_column_op(nand, oob_off, NULL, 0, false); 1059913821bdSBoris BREZILLON 1060913821bdSBoris BREZILLON ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 1061913821bdSBoris BREZILLON if (ret) 1062913821bdSBoris BREZILLON return ret; 1063913821bdSBoris BREZILLON 1064cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand); 1065cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page); 1066cc6822fbSBoris Brezillon 1067913821bdSBoris BREZILLON writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | 1068913821bdSBoris BREZILLON NFC_ACCESS_DIR | NFC_ECC_OP, 1069913821bdSBoris BREZILLON nfc->regs + NFC_REG_CMD); 1070913821bdSBoris BREZILLON 10718de15e1fSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 1072cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand); 1073913821bdSBoris BREZILLON if (ret) 1074913821bdSBoris BREZILLON return ret; 1075913821bdSBoris BREZILLON 1076913821bdSBoris BREZILLON *cur_off = oob_off + ecc->bytes + 4; 1077913821bdSBoris BREZILLON 1078913821bdSBoris BREZILLON return 0; 1079913821bdSBoris BREZILLON } 1080913821bdSBoris BREZILLON 1081cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, 10824be4e03eSBoris BREZILLON u8 *oob, int *cur_off, 10834be4e03eSBoris BREZILLON int page) 108435d0e24fSBoris BREZILLON { 1085cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 108635d0e24fSBoris BREZILLON struct nand_ecc_ctrl *ecc = &nand->ecc; 108735d0e24fSBoris BREZILLON int offset = ((ecc->bytes + 4) * ecc->steps); 108835d0e24fSBoris BREZILLON int len = mtd->oobsize - offset; 108935d0e24fSBoris BREZILLON 109035d0e24fSBoris BREZILLON if (len <= 0) 109135d0e24fSBoris BREZILLON return; 109235d0e24fSBoris BREZILLON 1093c4f3ef2cSBoris Brezillon if (!cur_off || *cur_off != offset) 109497d90da8SBoris Brezillon nand_change_write_column_op(nand, offset + mtd->writesize, 109597d90da8SBoris Brezillon NULL, 0, false); 109635d0e24fSBoris BREZILLON 1097cde567e3SBoris Brezillon sunxi_nfc_randomizer_write_buf(nand, oob + offset, len, false, page); 109835d0e24fSBoris BREZILLON 1099c4f3ef2cSBoris Brezillon if (cur_off) 110035d0e24fSBoris BREZILLON *cur_off = mtd->oobsize + mtd->writesize; 110135d0e24fSBoris BREZILLON } 110235d0e24fSBoris BREZILLON 1103a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, 11041fef62c1SBoris BREZILLON int oob_required, int page) 11051fef62c1SBoris BREZILLON { 1106a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 1107a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 11081fef62c1SBoris BREZILLON unsigned int max_bitflips = 0; 1109b462551cSBoris BREZILLON int ret, i, cur_off = 0; 11104be4e03eSBoris BREZILLON bool raw_mode = false; 11111fef62c1SBoris BREZILLON 1112df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1113df505799SBoris Brezillon 1114a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0); 111525f815f6SBoris Brezillon 1116cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand); 11171fef62c1SBoris BREZILLON 11181fef62c1SBoris BREZILLON for (i = 0; i < ecc->steps; i++) { 1119b462551cSBoris BREZILLON int data_off = i * ecc->size; 1120b462551cSBoris BREZILLON int oob_off = i * (ecc->bytes + 4); 1121b462551cSBoris BREZILLON u8 *data = buf + data_off; 1122a55abb36SBoris Brezillon u8 *oob = nand->oob_poi + oob_off; 11231fef62c1SBoris BREZILLON 1124cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob, 1125b462551cSBoris BREZILLON oob_off + mtd->writesize, 11264be4e03eSBoris BREZILLON &cur_off, &max_bitflips, 1127828dec15SBoris Brezillon !i, oob_required, page); 11284be4e03eSBoris BREZILLON if (ret < 0) 11291fef62c1SBoris BREZILLON return ret; 11304be4e03eSBoris BREZILLON else if (ret) 11314be4e03eSBoris BREZILLON raw_mode = true; 11321fef62c1SBoris BREZILLON } 11331fef62c1SBoris BREZILLON 113435d0e24fSBoris BREZILLON if (oob_required) 1135cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, &cur_off, 11364be4e03eSBoris BREZILLON !raw_mode, page); 11371fef62c1SBoris BREZILLON 1138cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand); 11391fef62c1SBoris BREZILLON 11401fef62c1SBoris BREZILLON return max_bitflips; 11411fef62c1SBoris BREZILLON } 11421fef62c1SBoris BREZILLON 1143a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf, 1144614049a8SBoris Brezillon int oob_required, int page) 1145614049a8SBoris Brezillon { 1146614049a8SBoris Brezillon int ret; 1147614049a8SBoris Brezillon 1148df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1149df505799SBoris Brezillon 1150a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0); 115125f815f6SBoris Brezillon 1152cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page, 1153a55abb36SBoris Brezillon nand->ecc.steps); 1154614049a8SBoris Brezillon if (ret >= 0) 1155614049a8SBoris Brezillon return ret; 1156614049a8SBoris Brezillon 1157614049a8SBoris Brezillon /* Fallback to PIO mode */ 1158a55abb36SBoris Brezillon return sunxi_nfc_hw_ecc_read_page(nand, buf, oob_required, page); 1159614049a8SBoris Brezillon } 1160614049a8SBoris Brezillon 1161a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, 1162fe82ccefSBoris Brezillon u32 data_offs, u32 readlen, 1163fe82ccefSBoris Brezillon u8 *bufpoi, int page) 1164fe82ccefSBoris Brezillon { 1165a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 1166a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 1167fe82ccefSBoris Brezillon int ret, i, cur_off = 0; 1168fe82ccefSBoris Brezillon unsigned int max_bitflips = 0; 1169fe82ccefSBoris Brezillon 1170df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1171df505799SBoris Brezillon 1172a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0); 117325f815f6SBoris Brezillon 1174cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand); 1175fe82ccefSBoris Brezillon 1176fe82ccefSBoris Brezillon for (i = data_offs / ecc->size; 1177fe82ccefSBoris Brezillon i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { 1178fe82ccefSBoris Brezillon int data_off = i * ecc->size; 1179fe82ccefSBoris Brezillon int oob_off = i * (ecc->bytes + 4); 1180fe82ccefSBoris Brezillon u8 *data = bufpoi + data_off; 1181a55abb36SBoris Brezillon u8 *oob = nand->oob_poi + oob_off; 1182fe82ccefSBoris Brezillon 1183cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, 1184fe82ccefSBoris Brezillon oob, 1185fe82ccefSBoris Brezillon oob_off + mtd->writesize, 1186828dec15SBoris Brezillon &cur_off, &max_bitflips, !i, 1187828dec15SBoris Brezillon false, page); 1188fe82ccefSBoris Brezillon if (ret < 0) 1189fe82ccefSBoris Brezillon return ret; 1190fe82ccefSBoris Brezillon } 1191fe82ccefSBoris Brezillon 1192cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand); 1193fe82ccefSBoris Brezillon 1194fe82ccefSBoris Brezillon return max_bitflips; 1195fe82ccefSBoris Brezillon } 1196fe82ccefSBoris Brezillon 1197a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand, 1198614049a8SBoris Brezillon u32 data_offs, u32 readlen, 1199614049a8SBoris Brezillon u8 *buf, int page) 1200614049a8SBoris Brezillon { 1201a55abb36SBoris Brezillon int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size); 1202614049a8SBoris Brezillon int ret; 1203614049a8SBoris Brezillon 1204df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1205df505799SBoris Brezillon 1206a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0); 120725f815f6SBoris Brezillon 1208cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks); 1209614049a8SBoris Brezillon if (ret >= 0) 1210614049a8SBoris Brezillon return ret; 1211614049a8SBoris Brezillon 1212614049a8SBoris Brezillon /* Fallback to PIO mode */ 1213a55abb36SBoris Brezillon return sunxi_nfc_hw_ecc_read_subpage(nand, data_offs, readlen, 1214614049a8SBoris Brezillon buf, page); 1215614049a8SBoris Brezillon } 1216614049a8SBoris Brezillon 1217a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, 121845aaeff9SBoris BREZILLON const uint8_t *buf, int oob_required, 121945aaeff9SBoris BREZILLON int page) 12201fef62c1SBoris BREZILLON { 1221a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 1222a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 1223b462551cSBoris BREZILLON int ret, i, cur_off = 0; 12241fef62c1SBoris BREZILLON 1225df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1226df505799SBoris Brezillon 1227a55abb36SBoris Brezillon nand_prog_page_begin_op(nand, page, 0, NULL, 0); 122825f815f6SBoris Brezillon 1229cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand); 12301fef62c1SBoris BREZILLON 12311fef62c1SBoris BREZILLON for (i = 0; i < ecc->steps; i++) { 1232b462551cSBoris BREZILLON int data_off = i * ecc->size; 1233b462551cSBoris BREZILLON int oob_off = i * (ecc->bytes + 4); 1234b462551cSBoris BREZILLON const u8 *data = buf + data_off; 1235a55abb36SBoris Brezillon const u8 *oob = nand->oob_poi + oob_off; 12361fef62c1SBoris BREZILLON 1237cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, 1238b462551cSBoris BREZILLON oob_off + mtd->writesize, 12394be4e03eSBoris BREZILLON &cur_off, !i, page); 12401fef62c1SBoris BREZILLON if (ret) 12411fef62c1SBoris BREZILLON return ret; 12421fef62c1SBoris BREZILLON } 12431fef62c1SBoris BREZILLON 1244a55abb36SBoris Brezillon if (oob_required || (nand->options & NAND_NEED_SCRAMBLING)) 1245cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi, 12464be4e03eSBoris BREZILLON &cur_off, page); 12471fef62c1SBoris BREZILLON 1248cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand); 12491fef62c1SBoris BREZILLON 1250a55abb36SBoris Brezillon return nand_prog_page_end_op(nand); 12511fef62c1SBoris BREZILLON } 12521fef62c1SBoris BREZILLON 1253a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, 125403b1d11aSBoris Brezillon u32 data_offs, u32 data_len, 125503b1d11aSBoris Brezillon const u8 *buf, int oob_required, 125603b1d11aSBoris Brezillon int page) 125703b1d11aSBoris Brezillon { 1258a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 1259a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 126003b1d11aSBoris Brezillon int ret, i, cur_off = 0; 126103b1d11aSBoris Brezillon 1262df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1263df505799SBoris Brezillon 1264a55abb36SBoris Brezillon nand_prog_page_begin_op(nand, page, 0, NULL, 0); 126525f815f6SBoris Brezillon 1266cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand); 126703b1d11aSBoris Brezillon 126803b1d11aSBoris Brezillon for (i = data_offs / ecc->size; 126903b1d11aSBoris Brezillon i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { 127003b1d11aSBoris Brezillon int data_off = i * ecc->size; 127103b1d11aSBoris Brezillon int oob_off = i * (ecc->bytes + 4); 127203b1d11aSBoris Brezillon const u8 *data = buf + data_off; 1273a55abb36SBoris Brezillon const u8 *oob = nand->oob_poi + oob_off; 127403b1d11aSBoris Brezillon 1275cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, 127603b1d11aSBoris Brezillon oob_off + mtd->writesize, 127703b1d11aSBoris Brezillon &cur_off, !i, page); 127803b1d11aSBoris Brezillon if (ret) 127903b1d11aSBoris Brezillon return ret; 128003b1d11aSBoris Brezillon } 128103b1d11aSBoris Brezillon 1282cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand); 128303b1d11aSBoris Brezillon 1284a55abb36SBoris Brezillon return nand_prog_page_end_op(nand); 128503b1d11aSBoris Brezillon } 128603b1d11aSBoris Brezillon 1287a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, 1288614049a8SBoris Brezillon const u8 *buf, 1289614049a8SBoris Brezillon int oob_required, 1290614049a8SBoris Brezillon int page) 1291614049a8SBoris Brezillon { 1292614049a8SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 1293614049a8SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 1294614049a8SBoris Brezillon struct scatterlist sg; 1295910ef7a4SManuel Dipolt u32 wait; 1296614049a8SBoris Brezillon int ret, i; 1297614049a8SBoris Brezillon 1298df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs); 1299df505799SBoris Brezillon 1300614049a8SBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 1301614049a8SBoris Brezillon if (ret) 1302614049a8SBoris Brezillon return ret; 1303614049a8SBoris Brezillon 1304cde567e3SBoris Brezillon ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps, 1305614049a8SBoris Brezillon DMA_TO_DEVICE, &sg); 1306614049a8SBoris Brezillon if (ret) 1307614049a8SBoris Brezillon goto pio_fallback; 1308614049a8SBoris Brezillon 1309614049a8SBoris Brezillon for (i = 0; i < ecc->steps; i++) { 1310614049a8SBoris Brezillon const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); 1311614049a8SBoris Brezillon 1312cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page); 1313614049a8SBoris Brezillon } 1314614049a8SBoris Brezillon 1315a55abb36SBoris Brezillon nand_prog_page_begin_op(nand, page, 0, NULL, 0); 131625f815f6SBoris Brezillon 1317cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand); 1318cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, false); 1319cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand); 1320614049a8SBoris Brezillon 1321614049a8SBoris Brezillon writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, 132273277443SBoris Brezillon nfc->regs + NFC_REG_WCMD_SET); 1323614049a8SBoris Brezillon 1324910ef7a4SManuel Dipolt wait = NFC_CMD_INT_FLAG; 1325910ef7a4SManuel Dipolt 1326910ef7a4SManuel Dipolt if (nfc->caps->has_mdma) 1327910ef7a4SManuel Dipolt wait |= NFC_DMA_INT_FLAG; 1328910ef7a4SManuel Dipolt else 1329614049a8SBoris Brezillon dma_async_issue_pending(nfc->dmac); 1330614049a8SBoris Brezillon 1331614049a8SBoris Brezillon writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | 1332614049a8SBoris Brezillon NFC_DATA_TRANS | NFC_ACCESS_DIR, 1333614049a8SBoris Brezillon nfc->regs + NFC_REG_CMD); 1334614049a8SBoris Brezillon 1335910ef7a4SManuel Dipolt ret = sunxi_nfc_wait_events(nfc, wait, false, 0); 1336910ef7a4SManuel Dipolt if (ret && !nfc->caps->has_mdma) 1337614049a8SBoris Brezillon dmaengine_terminate_all(nfc->dmac); 1338614049a8SBoris Brezillon 1339cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand); 1340cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand); 1341614049a8SBoris Brezillon 1342cde567e3SBoris Brezillon sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg); 1343614049a8SBoris Brezillon 1344614049a8SBoris Brezillon if (ret) 1345614049a8SBoris Brezillon return ret; 1346614049a8SBoris Brezillon 1347a55abb36SBoris Brezillon if (oob_required || (nand->options & NAND_NEED_SCRAMBLING)) 1348614049a8SBoris Brezillon /* TODO: use DMA to transfer extra OOB bytes ? */ 1349cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi, 1350614049a8SBoris Brezillon NULL, page); 1351614049a8SBoris Brezillon 1352a55abb36SBoris Brezillon return nand_prog_page_end_op(nand); 1353614049a8SBoris Brezillon 1354614049a8SBoris Brezillon pio_fallback: 1355a55abb36SBoris Brezillon return sunxi_nfc_hw_ecc_write_page(nand, buf, oob_required, page); 1356614049a8SBoris Brezillon } 1357614049a8SBoris Brezillon 1358a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page) 13591c1bdd6fSBoris Brezillon { 1360eeab7174SBoris Brezillon u8 *buf = nand_get_data_buf(nand); 13611c1bdd6fSBoris Brezillon 1362eeab7174SBoris Brezillon return nand->ecc.read_page(nand, buf, 1, page); 13631c1bdd6fSBoris Brezillon } 13641c1bdd6fSBoris Brezillon 1365a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page) 13661c1bdd6fSBoris Brezillon { 1367a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 1368eeab7174SBoris Brezillon u8 *buf = nand_get_data_buf(nand); 136997d90da8SBoris Brezillon int ret; 13701c1bdd6fSBoris Brezillon 1371eeab7174SBoris Brezillon memset(buf, 0xff, mtd->writesize); 1372eeab7174SBoris Brezillon ret = nand->ecc.write_page(nand, buf, 1, page); 13731c1bdd6fSBoris Brezillon if (ret) 13741c1bdd6fSBoris Brezillon return ret; 13751c1bdd6fSBoris Brezillon 13761c1bdd6fSBoris Brezillon /* Send command to program the OOB data */ 1377a55abb36SBoris Brezillon return nand_prog_page_end_op(nand); 13781c1bdd6fSBoris Brezillon } 13791c1bdd6fSBoris Brezillon 13809c618292SRoy Spliet static const s32 tWB_lut[] = {6, 12, 16, 20}; 13819c618292SRoy Spliet static const s32 tRHW_lut[] = {4, 8, 12, 20}; 13829c618292SRoy Spliet 13839c618292SRoy Spliet static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, 13849c618292SRoy Spliet u32 clk_period) 13859c618292SRoy Spliet { 13869c618292SRoy Spliet u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); 13879c618292SRoy Spliet int i; 13889c618292SRoy Spliet 13899c618292SRoy Spliet for (i = 0; i < lut_size; i++) { 13909c618292SRoy Spliet if (clk_cycles <= lut[i]) 13919c618292SRoy Spliet return i; 13929c618292SRoy Spliet } 13939c618292SRoy Spliet 13949c618292SRoy Spliet /* Doesn't fit */ 13959c618292SRoy Spliet return -EINVAL; 13969c618292SRoy Spliet } 13979c618292SRoy Spliet 13989c618292SRoy Spliet #define sunxi_nand_lookup_timing(l, p, c) \ 13999c618292SRoy Spliet _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) 14009c618292SRoy Spliet 14014c46667bSMiquel Raynal static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, 14024c46667bSMiquel Raynal const struct nand_interface_config *conf) 14031fef62c1SBoris BREZILLON { 1404f385ebf0SBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 1405f385ebf0SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 1406907f45fbSSascha Hauer const struct nand_sdr_timings *timings; 14071fef62c1SBoris BREZILLON u32 min_clk_period = 0; 14089c618292SRoy Spliet s32 tWB, tADL, tWHR, tRHW, tCAD; 14092d43457fSBoris Brezillon long real_clk_rate; 14101fef62c1SBoris BREZILLON 1411907f45fbSSascha Hauer timings = nand_get_sdr_timings(conf); 1412907f45fbSSascha Hauer if (IS_ERR(timings)) 1413907f45fbSSascha Hauer return -ENOTSUPP; 1414907f45fbSSascha Hauer 14151fef62c1SBoris BREZILLON /* T1 <=> tCLS */ 14161fef62c1SBoris BREZILLON if (timings->tCLS_min > min_clk_period) 14171fef62c1SBoris BREZILLON min_clk_period = timings->tCLS_min; 14181fef62c1SBoris BREZILLON 14191fef62c1SBoris BREZILLON /* T2 <=> tCLH */ 14201fef62c1SBoris BREZILLON if (timings->tCLH_min > min_clk_period) 14211fef62c1SBoris BREZILLON min_clk_period = timings->tCLH_min; 14221fef62c1SBoris BREZILLON 14231fef62c1SBoris BREZILLON /* T3 <=> tCS */ 14241fef62c1SBoris BREZILLON if (timings->tCS_min > min_clk_period) 14251fef62c1SBoris BREZILLON min_clk_period = timings->tCS_min; 14261fef62c1SBoris BREZILLON 14271fef62c1SBoris BREZILLON /* T4 <=> tCH */ 14281fef62c1SBoris BREZILLON if (timings->tCH_min > min_clk_period) 14291fef62c1SBoris BREZILLON min_clk_period = timings->tCH_min; 14301fef62c1SBoris BREZILLON 14311fef62c1SBoris BREZILLON /* T5 <=> tWP */ 14321fef62c1SBoris BREZILLON if (timings->tWP_min > min_clk_period) 14331fef62c1SBoris BREZILLON min_clk_period = timings->tWP_min; 14341fef62c1SBoris BREZILLON 14351fef62c1SBoris BREZILLON /* T6 <=> tWH */ 14361fef62c1SBoris BREZILLON if (timings->tWH_min > min_clk_period) 14371fef62c1SBoris BREZILLON min_clk_period = timings->tWH_min; 14381fef62c1SBoris BREZILLON 14391fef62c1SBoris BREZILLON /* T7 <=> tALS */ 14401fef62c1SBoris BREZILLON if (timings->tALS_min > min_clk_period) 14411fef62c1SBoris BREZILLON min_clk_period = timings->tALS_min; 14421fef62c1SBoris BREZILLON 14431fef62c1SBoris BREZILLON /* T8 <=> tDS */ 14441fef62c1SBoris BREZILLON if (timings->tDS_min > min_clk_period) 14451fef62c1SBoris BREZILLON min_clk_period = timings->tDS_min; 14461fef62c1SBoris BREZILLON 14471fef62c1SBoris BREZILLON /* T9 <=> tDH */ 14481fef62c1SBoris BREZILLON if (timings->tDH_min > min_clk_period) 14491fef62c1SBoris BREZILLON min_clk_period = timings->tDH_min; 14501fef62c1SBoris BREZILLON 14511fef62c1SBoris BREZILLON /* T10 <=> tRR */ 14521fef62c1SBoris BREZILLON if (timings->tRR_min > (min_clk_period * 3)) 14531fef62c1SBoris BREZILLON min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); 14541fef62c1SBoris BREZILLON 14551fef62c1SBoris BREZILLON /* T11 <=> tALH */ 14561fef62c1SBoris BREZILLON if (timings->tALH_min > min_clk_period) 14571fef62c1SBoris BREZILLON min_clk_period = timings->tALH_min; 14581fef62c1SBoris BREZILLON 14591fef62c1SBoris BREZILLON /* T12 <=> tRP */ 14601fef62c1SBoris BREZILLON if (timings->tRP_min > min_clk_period) 14611fef62c1SBoris BREZILLON min_clk_period = timings->tRP_min; 14621fef62c1SBoris BREZILLON 14631fef62c1SBoris BREZILLON /* T13 <=> tREH */ 14641fef62c1SBoris BREZILLON if (timings->tREH_min > min_clk_period) 14651fef62c1SBoris BREZILLON min_clk_period = timings->tREH_min; 14661fef62c1SBoris BREZILLON 14671fef62c1SBoris BREZILLON /* T14 <=> tRC */ 14681fef62c1SBoris BREZILLON if (timings->tRC_min > (min_clk_period * 2)) 14691fef62c1SBoris BREZILLON min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); 14701fef62c1SBoris BREZILLON 14711fef62c1SBoris BREZILLON /* T15 <=> tWC */ 14721fef62c1SBoris BREZILLON if (timings->tWC_min > (min_clk_period * 2)) 14731fef62c1SBoris BREZILLON min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); 14741fef62c1SBoris BREZILLON 14759c618292SRoy Spliet /* T16 - T19 + tCAD */ 14765abcd95dSBoris Brezillon if (timings->tWB_max > (min_clk_period * 20)) 14775abcd95dSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); 14785abcd95dSBoris Brezillon 14795abcd95dSBoris Brezillon if (timings->tADL_min > (min_clk_period * 32)) 14805abcd95dSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); 14815abcd95dSBoris Brezillon 14825abcd95dSBoris Brezillon if (timings->tWHR_min > (min_clk_period * 32)) 14835abcd95dSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); 14845abcd95dSBoris Brezillon 14855abcd95dSBoris Brezillon if (timings->tRHW_min > (min_clk_period * 20)) 14865abcd95dSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); 14875abcd95dSBoris Brezillon 1488511d05e0SBoris Brezillon /* 1489511d05e0SBoris Brezillon * In non-EDO, tREA should be less than tRP to guarantee that the 1490511d05e0SBoris Brezillon * controller does not sample the IO lines too early. Unfortunately, 1491511d05e0SBoris Brezillon * the sunxi NAND controller does not allow us to have different 1492511d05e0SBoris Brezillon * values for tRP and tREH (tRP = tREH = tRW / 2). 1493511d05e0SBoris Brezillon * 1494511d05e0SBoris Brezillon * We have 2 options to overcome this limitation: 1495511d05e0SBoris Brezillon * 1496511d05e0SBoris Brezillon * 1/ Extend tRC to fulfil the tREA <= tRC / 2 constraint 1497511d05e0SBoris Brezillon * 2/ Use EDO mode (only works if timings->tRLOH > 0) 1498511d05e0SBoris Brezillon */ 1499511d05e0SBoris Brezillon if (timings->tREA_max > min_clk_period && !timings->tRLOH_min) 1500511d05e0SBoris Brezillon min_clk_period = timings->tREA_max; 1501511d05e0SBoris Brezillon 15029c618292SRoy Spliet tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, 15039c618292SRoy Spliet min_clk_period); 15049c618292SRoy Spliet if (tWB < 0) { 15059c618292SRoy Spliet dev_err(nfc->dev, "unsupported tWB\n"); 15069c618292SRoy Spliet return tWB; 15079c618292SRoy Spliet } 15089c618292SRoy Spliet 15099c618292SRoy Spliet tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; 15109c618292SRoy Spliet if (tADL > 3) { 15119c618292SRoy Spliet dev_err(nfc->dev, "unsupported tADL\n"); 15129c618292SRoy Spliet return -EINVAL; 15139c618292SRoy Spliet } 15149c618292SRoy Spliet 15159c618292SRoy Spliet tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; 15169c618292SRoy Spliet if (tWHR > 3) { 15179c618292SRoy Spliet dev_err(nfc->dev, "unsupported tWHR\n"); 15189c618292SRoy Spliet return -EINVAL; 15199c618292SRoy Spliet } 15209c618292SRoy Spliet 15219c618292SRoy Spliet tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, 15229c618292SRoy Spliet min_clk_period); 15239c618292SRoy Spliet if (tRHW < 0) { 15249c618292SRoy Spliet dev_err(nfc->dev, "unsupported tRHW\n"); 15259c618292SRoy Spliet return tRHW; 15269c618292SRoy Spliet } 15279c618292SRoy Spliet 1528104e442aSBoris Brezillon if (csline == NAND_DATA_IFACE_CHECK_ONLY) 1529907f45fbSSascha Hauer return 0; 1530907f45fbSSascha Hauer 15319c618292SRoy Spliet /* 15329c618292SRoy Spliet * TODO: according to ONFI specs this value only applies for DDR NAND, 15339c618292SRoy Spliet * but Allwinner seems to set this to 0x7. Mimic them for now. 15349c618292SRoy Spliet */ 15359c618292SRoy Spliet tCAD = 0x7; 15369c618292SRoy Spliet 15379c618292SRoy Spliet /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ 1538f385ebf0SBoris Brezillon sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); 15391fef62c1SBoris BREZILLON 15401fef62c1SBoris BREZILLON /* Convert min_clk_period from picoseconds to nanoseconds */ 15411fef62c1SBoris BREZILLON min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); 15421fef62c1SBoris BREZILLON 15431fef62c1SBoris BREZILLON /* 15442f9992e0SBoris Brezillon * Unlike what is stated in Allwinner datasheet, the clk_rate should 15452f9992e0SBoris Brezillon * be set to (1 / min_clk_period), and not (2 / min_clk_period). 15462f9992e0SBoris Brezillon * This new formula was verified with a scope and validated by 15472f9992e0SBoris Brezillon * Allwinner engineers. 15481fef62c1SBoris BREZILLON */ 1549f385ebf0SBoris Brezillon sunxi_nand->clk_rate = NSEC_PER_SEC / min_clk_period; 1550f385ebf0SBoris Brezillon real_clk_rate = clk_round_rate(nfc->mod_clk, sunxi_nand->clk_rate); 1551791eccd9SBryan O'Donoghue if (real_clk_rate <= 0) { 1552f385ebf0SBoris Brezillon dev_err(nfc->dev, "Unable to round clk %lu\n", 1553f385ebf0SBoris Brezillon sunxi_nand->clk_rate); 1554791eccd9SBryan O'Donoghue return -EINVAL; 1555791eccd9SBryan O'Donoghue } 15562d43457fSBoris Brezillon 1557511d05e0SBoris Brezillon sunxi_nand->timing_ctl = 0; 1558511d05e0SBoris Brezillon 15592d43457fSBoris Brezillon /* 15602d43457fSBoris Brezillon * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data 15612d43457fSBoris Brezillon * output cycle timings shall be used if the host drives tRC less than 1562511d05e0SBoris Brezillon * 30 ns. We should also use EDO mode if tREA is bigger than tRP. 15632d43457fSBoris Brezillon */ 15642d43457fSBoris Brezillon min_clk_period = NSEC_PER_SEC / real_clk_rate; 1565511d05e0SBoris Brezillon if (min_clk_period * 2 < 30 || min_clk_period * 1000 < timings->tREA_max) 1566511d05e0SBoris Brezillon sunxi_nand->timing_ctl = NFC_TIMING_CTL_EDO; 15671fef62c1SBoris BREZILLON 15681fef62c1SBoris BREZILLON return 0; 15691fef62c1SBoris BREZILLON } 15701fef62c1SBoris BREZILLON 1571c66811e6SBoris Brezillon static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, 1572c66811e6SBoris Brezillon struct mtd_oob_region *oobregion) 1573c66811e6SBoris Brezillon { 1574c66811e6SBoris Brezillon struct nand_chip *nand = mtd_to_nand(mtd); 1575c66811e6SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 1576c66811e6SBoris Brezillon 1577c66811e6SBoris Brezillon if (section >= ecc->steps) 1578c66811e6SBoris Brezillon return -ERANGE; 1579c66811e6SBoris Brezillon 1580c66811e6SBoris Brezillon oobregion->offset = section * (ecc->bytes + 4) + 4; 1581c66811e6SBoris Brezillon oobregion->length = ecc->bytes; 1582c66811e6SBoris Brezillon 1583c66811e6SBoris Brezillon return 0; 1584c66811e6SBoris Brezillon } 1585c66811e6SBoris Brezillon 1586c66811e6SBoris Brezillon static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, 1587c66811e6SBoris Brezillon struct mtd_oob_region *oobregion) 1588c66811e6SBoris Brezillon { 1589c66811e6SBoris Brezillon struct nand_chip *nand = mtd_to_nand(mtd); 1590c66811e6SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc; 1591c66811e6SBoris Brezillon 1592c66811e6SBoris Brezillon if (section > ecc->steps) 1593c66811e6SBoris Brezillon return -ERANGE; 1594c66811e6SBoris Brezillon 1595c66811e6SBoris Brezillon /* 1596c66811e6SBoris Brezillon * The first 2 bytes are used for BB markers, hence we 1597c66811e6SBoris Brezillon * only have 2 bytes available in the first user data 1598c66811e6SBoris Brezillon * section. 1599c66811e6SBoris Brezillon */ 1600bace41f8SMiquel Raynal if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { 1601c66811e6SBoris Brezillon oobregion->offset = 2; 1602c66811e6SBoris Brezillon oobregion->length = 2; 1603c66811e6SBoris Brezillon 1604c66811e6SBoris Brezillon return 0; 1605c66811e6SBoris Brezillon } 1606c66811e6SBoris Brezillon 1607c66811e6SBoris Brezillon oobregion->offset = section * (ecc->bytes + 4); 1608c66811e6SBoris Brezillon 1609c66811e6SBoris Brezillon if (section < ecc->steps) 1610c66811e6SBoris Brezillon oobregion->length = 4; 1611c66811e6SBoris Brezillon else 1612c66811e6SBoris Brezillon oobregion->offset = mtd->oobsize - oobregion->offset; 1613c66811e6SBoris Brezillon 1614c66811e6SBoris Brezillon return 0; 1615c66811e6SBoris Brezillon } 1616c66811e6SBoris Brezillon 1617c66811e6SBoris Brezillon static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { 1618c66811e6SBoris Brezillon .ecc = sunxi_nand_ooblayout_ecc, 1619c66811e6SBoris Brezillon .free = sunxi_nand_ooblayout_free, 1620c66811e6SBoris Brezillon }; 1621c66811e6SBoris Brezillon 1622cbd87780SMiquel Raynal static void sunxi_nand_hw_ecc_ctrl_cleanup(struct sunxi_nand_chip *sunxi_nand) 162315d6f118SBoris Brezillon { 1624cbd87780SMiquel Raynal kfree(sunxi_nand->ecc); 162515d6f118SBoris Brezillon } 162615d6f118SBoris Brezillon 1627cde567e3SBoris Brezillon static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, 16281fef62c1SBoris BREZILLON struct nand_ecc_ctrl *ecc, 16291fef62c1SBoris BREZILLON struct device_node *np) 16301fef62c1SBoris BREZILLON { 16311fef62c1SBoris BREZILLON static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; 1632cbd87780SMiquel Raynal struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 1633cde567e3SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 1634cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand); 1635b5156335SMiquel Raynal struct nand_device *nanddev = mtd_to_nanddev(mtd); 16361fef62c1SBoris BREZILLON int nsectors; 16371fef62c1SBoris BREZILLON int ret; 16381fef62c1SBoris BREZILLON int i; 16391fef62c1SBoris BREZILLON 1640b5156335SMiquel Raynal if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { 16414796d865SBoris Brezillon int bytes; 16424796d865SBoris Brezillon 16434796d865SBoris Brezillon ecc->size = 1024; 16444796d865SBoris Brezillon nsectors = mtd->writesize / ecc->size; 16454796d865SBoris Brezillon 16464796d865SBoris Brezillon /* Reserve 2 bytes for the BBM */ 16474796d865SBoris Brezillon bytes = (mtd->oobsize - 2) / nsectors; 16484796d865SBoris Brezillon 16494796d865SBoris Brezillon /* 4 non-ECC bytes are added before each ECC bytes section */ 16504796d865SBoris Brezillon bytes -= 4; 16514796d865SBoris Brezillon 16524796d865SBoris Brezillon /* and bytes has to be even. */ 16534796d865SBoris Brezillon if (bytes % 2) 16544796d865SBoris Brezillon bytes--; 16554796d865SBoris Brezillon 16564796d865SBoris Brezillon ecc->strength = bytes * 8 / fls(8 * ecc->size); 16574796d865SBoris Brezillon 16584796d865SBoris Brezillon for (i = 0; i < ARRAY_SIZE(strengths); i++) { 16594796d865SBoris Brezillon if (strengths[i] > ecc->strength) 16604796d865SBoris Brezillon break; 16614796d865SBoris Brezillon } 16624796d865SBoris Brezillon 16634796d865SBoris Brezillon if (!i) 16644796d865SBoris Brezillon ecc->strength = 0; 16654796d865SBoris Brezillon else 16664796d865SBoris Brezillon ecc->strength = strengths[i - 1]; 16674796d865SBoris Brezillon } 16684796d865SBoris Brezillon 166940297e7fSDan Carpenter if (ecc->size != 512 && ecc->size != 1024) 167040297e7fSDan Carpenter return -EINVAL; 167140297e7fSDan Carpenter 1672cbd87780SMiquel Raynal sunxi_nand->ecc = kzalloc(sizeof(*sunxi_nand->ecc), GFP_KERNEL); 1673cbd87780SMiquel Raynal if (!sunxi_nand->ecc) 16741fef62c1SBoris BREZILLON return -ENOMEM; 16751fef62c1SBoris BREZILLON 1676872164e4SBoris Brezillon /* Prefer 1k ECC chunk over 512 ones */ 1677872164e4SBoris Brezillon if (ecc->size == 512 && mtd->writesize > 512) { 1678872164e4SBoris Brezillon ecc->size = 1024; 1679872164e4SBoris Brezillon ecc->strength *= 2; 1680872164e4SBoris Brezillon } 1681872164e4SBoris Brezillon 16821fef62c1SBoris BREZILLON /* Add ECC info retrieval from DT */ 16831fef62c1SBoris BREZILLON for (i = 0; i < ARRAY_SIZE(strengths); i++) { 1684f4c6cd1aSMiquel Raynal if (ecc->strength <= strengths[i]) { 1685f4c6cd1aSMiquel Raynal /* 1686f4c6cd1aSMiquel Raynal * Update ecc->strength value with the actual strength 1687f4c6cd1aSMiquel Raynal * that will be used by the ECC engine. 1688f4c6cd1aSMiquel Raynal */ 1689f4c6cd1aSMiquel Raynal ecc->strength = strengths[i]; 16901fef62c1SBoris BREZILLON break; 16911fef62c1SBoris BREZILLON } 1692f4c6cd1aSMiquel Raynal } 16931fef62c1SBoris BREZILLON 16941fef62c1SBoris BREZILLON if (i >= ARRAY_SIZE(strengths)) { 16951fef62c1SBoris BREZILLON dev_err(nfc->dev, "unsupported strength\n"); 16961fef62c1SBoris BREZILLON ret = -ENOTSUPP; 16971fef62c1SBoris BREZILLON goto err; 16981fef62c1SBoris BREZILLON } 16991fef62c1SBoris BREZILLON 1700cbd87780SMiquel Raynal sunxi_nand->ecc->mode = i; 17011fef62c1SBoris BREZILLON 17021fef62c1SBoris BREZILLON /* HW ECC always request ECC bytes for 1024 bytes blocks */ 17031fef62c1SBoris BREZILLON ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); 17041fef62c1SBoris BREZILLON 17051fef62c1SBoris BREZILLON /* HW ECC always work with even numbers of ECC bytes */ 17061fef62c1SBoris BREZILLON ecc->bytes = ALIGN(ecc->bytes, 2); 17071fef62c1SBoris BREZILLON 17081fef62c1SBoris BREZILLON nsectors = mtd->writesize / ecc->size; 17091fef62c1SBoris BREZILLON 17101fef62c1SBoris BREZILLON if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { 17111fef62c1SBoris BREZILLON ret = -EINVAL; 17121fef62c1SBoris BREZILLON goto err; 17131fef62c1SBoris BREZILLON } 17141fef62c1SBoris BREZILLON 171515d6f118SBoris Brezillon ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; 171615d6f118SBoris Brezillon ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; 1717c66811e6SBoris Brezillon mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); 17181fef62c1SBoris BREZILLON 1719910ef7a4SManuel Dipolt if (nfc->dmac || nfc->caps->has_mdma) { 1720614049a8SBoris Brezillon ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; 1721614049a8SBoris Brezillon ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; 1722614049a8SBoris Brezillon ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; 1723ce8148d7SMiquel Raynal nand->options |= NAND_USES_DMA; 1724614049a8SBoris Brezillon } else { 17251fef62c1SBoris BREZILLON ecc->read_page = sunxi_nfc_hw_ecc_read_page; 1726614049a8SBoris Brezillon ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; 17271fef62c1SBoris BREZILLON ecc->write_page = sunxi_nfc_hw_ecc_write_page; 1728614049a8SBoris Brezillon } 1729614049a8SBoris Brezillon 173003b1d11aSBoris Brezillon /* TODO: support DMA for raw accesses and subpage write */ 173103b1d11aSBoris Brezillon ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; 17321c1bdd6fSBoris Brezillon ecc->read_oob_raw = nand_read_oob_std; 17331c1bdd6fSBoris Brezillon ecc->write_oob_raw = nand_write_oob_std; 17341fef62c1SBoris BREZILLON 17351fef62c1SBoris BREZILLON return 0; 17361fef62c1SBoris BREZILLON 173715d6f118SBoris Brezillon err: 1738cbd87780SMiquel Raynal kfree(sunxi_nand->ecc); 17391fef62c1SBoris BREZILLON 17401fef62c1SBoris BREZILLON return ret; 17411fef62c1SBoris BREZILLON } 17421fef62c1SBoris BREZILLON 1743cbd87780SMiquel Raynal static void sunxi_nand_ecc_cleanup(struct sunxi_nand_chip *sunxi_nand) 17441fef62c1SBoris BREZILLON { 1745cbd87780SMiquel Raynal struct nand_ecc_ctrl *ecc = &sunxi_nand->nand.ecc; 1746cbd87780SMiquel Raynal 1747bace41f8SMiquel Raynal switch (ecc->engine_type) { 1748bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_HOST: 1749cbd87780SMiquel Raynal sunxi_nand_hw_ecc_ctrl_cleanup(sunxi_nand); 17501fef62c1SBoris BREZILLON break; 1751bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_NONE: 17521fef62c1SBoris BREZILLON default: 17531fef62c1SBoris BREZILLON break; 17541fef62c1SBoris BREZILLON } 17551fef62c1SBoris BREZILLON } 17561fef62c1SBoris BREZILLON 17572a4d9c16SMiquel Raynal static int sunxi_nand_attach_chip(struct nand_chip *nand) 17581fef62c1SBoris BREZILLON { 175953576c7bSMiquel Raynal const struct nand_ecc_props *requirements = 176053576c7bSMiquel Raynal nanddev_get_ecc_requirements(&nand->base); 17612a4d9c16SMiquel Raynal struct nand_ecc_ctrl *ecc = &nand->ecc; 17622a4d9c16SMiquel Raynal struct device_node *np = nand_get_flash_node(nand); 17631fef62c1SBoris BREZILLON int ret; 17641fef62c1SBoris BREZILLON 17652a4d9c16SMiquel Raynal if (nand->bbt_options & NAND_BBT_USE_FLASH) 17662a4d9c16SMiquel Raynal nand->bbt_options |= NAND_BBT_NO_OOB; 17672a4d9c16SMiquel Raynal 17682a4d9c16SMiquel Raynal if (nand->options & NAND_NEED_SCRAMBLING) 17692a4d9c16SMiquel Raynal nand->options |= NAND_NO_SUBPAGE_WRITE; 17702a4d9c16SMiquel Raynal 17712a4d9c16SMiquel Raynal nand->options |= NAND_SUBPAGE_READ; 17722a4d9c16SMiquel Raynal 1773a3d22a55SBoris BREZILLON if (!ecc->size) { 177453576c7bSMiquel Raynal ecc->size = requirements->step_size; 177553576c7bSMiquel Raynal ecc->strength = requirements->strength; 17761fef62c1SBoris BREZILLON } 17771fef62c1SBoris BREZILLON 17781fef62c1SBoris BREZILLON if (!ecc->size || !ecc->strength) 17791fef62c1SBoris BREZILLON return -EINVAL; 17801fef62c1SBoris BREZILLON 1781bace41f8SMiquel Raynal switch (ecc->engine_type) { 1782bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_HOST: 1783cde567e3SBoris Brezillon ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np); 17841fef62c1SBoris BREZILLON if (ret) 17851fef62c1SBoris BREZILLON return ret; 17861fef62c1SBoris BREZILLON break; 1787bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_NONE: 1788bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_SOFT: 17891fef62c1SBoris BREZILLON break; 17901fef62c1SBoris BREZILLON default: 17911fef62c1SBoris BREZILLON return -EINVAL; 17921fef62c1SBoris BREZILLON } 17931fef62c1SBoris BREZILLON 17941fef62c1SBoris BREZILLON return 0; 17951fef62c1SBoris BREZILLON } 17961fef62c1SBoris BREZILLON 1797df505799SBoris Brezillon static int sunxi_nfc_exec_subop(struct nand_chip *nand, 1798df505799SBoris Brezillon const struct nand_subop *subop) 1799df505799SBoris Brezillon { 1800df505799SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 1801df505799SBoris Brezillon u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { }; 1802df505799SBoris Brezillon unsigned int i, j, remaining, start; 1803df505799SBoris Brezillon void *inbuf = NULL; 1804df505799SBoris Brezillon int ret; 1805df505799SBoris Brezillon 1806df505799SBoris Brezillon for (i = 0; i < subop->ninstrs; i++) { 1807df505799SBoris Brezillon const struct nand_op_instr *instr = &subop->instrs[i]; 1808df505799SBoris Brezillon 1809df505799SBoris Brezillon switch (instr->type) { 1810df505799SBoris Brezillon case NAND_OP_CMD_INSTR: 1811df505799SBoris Brezillon if (cmd & NFC_SEND_CMD1) { 1812df505799SBoris Brezillon if (WARN_ON(cmd & NFC_SEND_CMD2)) 1813df505799SBoris Brezillon return -EINVAL; 1814df505799SBoris Brezillon 1815df505799SBoris Brezillon cmd |= NFC_SEND_CMD2; 1816df505799SBoris Brezillon extcmd |= instr->ctx.cmd.opcode; 1817df505799SBoris Brezillon } else { 1818df505799SBoris Brezillon cmd |= NFC_SEND_CMD1 | 1819df505799SBoris Brezillon NFC_CMD(instr->ctx.cmd.opcode); 1820df505799SBoris Brezillon } 1821df505799SBoris Brezillon break; 1822df505799SBoris Brezillon 1823df505799SBoris Brezillon case NAND_OP_ADDR_INSTR: 1824df505799SBoris Brezillon remaining = nand_subop_get_num_addr_cyc(subop, i); 1825df505799SBoris Brezillon start = nand_subop_get_addr_start_off(subop, i); 1826df505799SBoris Brezillon for (j = 0; j < 8 && j + start < remaining; j++) { 1827df505799SBoris Brezillon u32 addr = instr->ctx.addr.addrs[j + start]; 1828df505799SBoris Brezillon 1829df505799SBoris Brezillon addrs[j / 4] |= addr << (j % 4) * 8; 1830df505799SBoris Brezillon } 1831df505799SBoris Brezillon 1832df505799SBoris Brezillon if (j) 1833df505799SBoris Brezillon cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j); 1834df505799SBoris Brezillon 1835df505799SBoris Brezillon break; 1836df505799SBoris Brezillon 1837df505799SBoris Brezillon case NAND_OP_DATA_IN_INSTR: 1838df505799SBoris Brezillon case NAND_OP_DATA_OUT_INSTR: 1839df505799SBoris Brezillon start = nand_subop_get_data_start_off(subop, i); 1840df505799SBoris Brezillon remaining = nand_subop_get_data_len(subop, i); 1841df505799SBoris Brezillon cnt = min_t(u32, remaining, NFC_SRAM_SIZE); 1842df505799SBoris Brezillon cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; 1843df505799SBoris Brezillon 1844df505799SBoris Brezillon if (instr->type == NAND_OP_DATA_OUT_INSTR) { 1845df505799SBoris Brezillon cmd |= NFC_ACCESS_DIR; 1846df505799SBoris Brezillon memcpy_toio(nfc->regs + NFC_RAM0_BASE, 1847df505799SBoris Brezillon instr->ctx.data.buf.out + start, 1848df505799SBoris Brezillon cnt); 1849df505799SBoris Brezillon } else { 1850df505799SBoris Brezillon inbuf = instr->ctx.data.buf.in + start; 1851df505799SBoris Brezillon } 1852df505799SBoris Brezillon 1853df505799SBoris Brezillon break; 1854df505799SBoris Brezillon 1855df505799SBoris Brezillon case NAND_OP_WAITRDY_INSTR: 1856df505799SBoris Brezillon cmd |= NFC_WAIT_FLAG; 1857df505799SBoris Brezillon break; 1858df505799SBoris Brezillon } 1859df505799SBoris Brezillon } 1860df505799SBoris Brezillon 1861df505799SBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 1862df505799SBoris Brezillon if (ret) 1863df505799SBoris Brezillon return ret; 1864df505799SBoris Brezillon 1865df505799SBoris Brezillon if (cmd & NFC_SEND_ADR) { 1866df505799SBoris Brezillon writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW); 1867df505799SBoris Brezillon writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH); 1868df505799SBoris Brezillon } 1869df505799SBoris Brezillon 1870df505799SBoris Brezillon if (cmd & NFC_SEND_CMD2) 1871df505799SBoris Brezillon writel(extcmd, 1872df505799SBoris Brezillon nfc->regs + 1873df505799SBoris Brezillon (cmd & NFC_ACCESS_DIR ? 1874df505799SBoris Brezillon NFC_REG_WCMD_SET : NFC_REG_RCMD_SET)); 1875df505799SBoris Brezillon 1876df505799SBoris Brezillon if (cmd & NFC_DATA_TRANS) 1877df505799SBoris Brezillon writel(cnt, nfc->regs + NFC_REG_CNT); 1878df505799SBoris Brezillon 1879df505799SBoris Brezillon writel(cmd, nfc->regs + NFC_REG_CMD); 1880df505799SBoris Brezillon 1881df505799SBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, 1882df505799SBoris Brezillon !(cmd & NFC_WAIT_FLAG) && cnt < 64, 1883df505799SBoris Brezillon 0); 1884df505799SBoris Brezillon if (ret) 1885df505799SBoris Brezillon return ret; 1886df505799SBoris Brezillon 1887df505799SBoris Brezillon if (inbuf) 1888df505799SBoris Brezillon memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt); 1889df505799SBoris Brezillon 1890df505799SBoris Brezillon return 0; 1891df505799SBoris Brezillon } 1892df505799SBoris Brezillon 1893df505799SBoris Brezillon static int sunxi_nfc_soft_waitrdy(struct nand_chip *nand, 1894df505799SBoris Brezillon const struct nand_subop *subop) 1895df505799SBoris Brezillon { 1896df505799SBoris Brezillon return nand_soft_waitrdy(nand, 1897df505799SBoris Brezillon subop->instrs[0].ctx.waitrdy.timeout_ms); 1898df505799SBoris Brezillon } 1899df505799SBoris Brezillon 1900df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER( 1901df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 1902df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1903df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 1904df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1905df505799SBoris Brezillon NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), 1906df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)), 1907df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 1908df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1909df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 1910df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024), 1911df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1912df505799SBoris Brezillon NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), 1913df505799SBoris Brezillon ); 1914df505799SBoris Brezillon 1915df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER( 1916df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 1917df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1918df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 1919df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1920df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)), 1921df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 1922df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true), 1923df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 1924df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024), 1925df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true)), 1926df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy, 1927df505799SBoris Brezillon NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 1928df505799SBoris Brezillon ); 1929df505799SBoris Brezillon 1930df505799SBoris Brezillon static int sunxi_nfc_exec_op(struct nand_chip *nand, 1931df505799SBoris Brezillon const struct nand_operation *op, bool check_only) 1932df505799SBoris Brezillon { 1933df505799SBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 1934df505799SBoris Brezillon const struct nand_op_parser *parser; 1935df505799SBoris Brezillon 1936ce446b4bSBoris Brezillon if (!check_only) 1937df505799SBoris Brezillon sunxi_nfc_select_chip(nand, op->cs); 1938df505799SBoris Brezillon 1939df505799SBoris Brezillon if (sunxi_nand->sels[op->cs].rb >= 0) 1940df505799SBoris Brezillon parser = &sunxi_nfc_op_parser; 1941df505799SBoris Brezillon else 1942df505799SBoris Brezillon parser = &sunxi_nfc_norb_op_parser; 1943df505799SBoris Brezillon 1944df505799SBoris Brezillon return nand_op_parser_exec_op(nand, parser, op, check_only); 1945df505799SBoris Brezillon } 1946df505799SBoris Brezillon 19472a4d9c16SMiquel Raynal static const struct nand_controller_ops sunxi_nand_controller_ops = { 19482a4d9c16SMiquel Raynal .attach_chip = sunxi_nand_attach_chip, 19494c46667bSMiquel Raynal .setup_interface = sunxi_nfc_setup_interface, 1950df505799SBoris Brezillon .exec_op = sunxi_nfc_exec_op, 19512a4d9c16SMiquel Raynal }; 19522a4d9c16SMiquel Raynal 19531fef62c1SBoris BREZILLON static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, 19541fef62c1SBoris BREZILLON struct device_node *np) 19551fef62c1SBoris BREZILLON { 1956f385ebf0SBoris Brezillon struct sunxi_nand_chip *sunxi_nand; 19571fef62c1SBoris BREZILLON struct mtd_info *mtd; 19581fef62c1SBoris BREZILLON struct nand_chip *nand; 19591fef62c1SBoris BREZILLON int nsels; 19601fef62c1SBoris BREZILLON int ret; 19611fef62c1SBoris BREZILLON int i; 19621fef62c1SBoris BREZILLON u32 tmp; 19631fef62c1SBoris BREZILLON 19641fef62c1SBoris BREZILLON if (!of_get_property(np, "reg", &nsels)) 19651fef62c1SBoris BREZILLON return -EINVAL; 19661fef62c1SBoris BREZILLON 19671fef62c1SBoris BREZILLON nsels /= sizeof(u32); 19681fef62c1SBoris BREZILLON if (!nsels) { 19691fef62c1SBoris BREZILLON dev_err(dev, "invalid reg property size\n"); 19701fef62c1SBoris BREZILLON return -EINVAL; 19711fef62c1SBoris BREZILLON } 19721fef62c1SBoris BREZILLON 19736c721acdSBoris Brezillon sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels), 19741fef62c1SBoris BREZILLON GFP_KERNEL); 1975*b0821cc5SZhen Lei if (!sunxi_nand) 19761fef62c1SBoris BREZILLON return -ENOMEM; 19771fef62c1SBoris BREZILLON 1978f385ebf0SBoris Brezillon sunxi_nand->nsels = nsels; 19791fef62c1SBoris BREZILLON 19801fef62c1SBoris BREZILLON for (i = 0; i < nsels; i++) { 19811fef62c1SBoris BREZILLON ret = of_property_read_u32_index(np, "reg", i, &tmp); 19821fef62c1SBoris BREZILLON if (ret) { 19831fef62c1SBoris BREZILLON dev_err(dev, "could not retrieve reg property: %d\n", 19841fef62c1SBoris BREZILLON ret); 19851fef62c1SBoris BREZILLON return ret; 19861fef62c1SBoris BREZILLON } 19871fef62c1SBoris BREZILLON 19881fef62c1SBoris BREZILLON if (tmp > NFC_MAX_CS) { 19891fef62c1SBoris BREZILLON dev_err(dev, 19901fef62c1SBoris BREZILLON "invalid reg value: %u (max CS = 7)\n", 19911fef62c1SBoris BREZILLON tmp); 19921fef62c1SBoris BREZILLON return -EINVAL; 19931fef62c1SBoris BREZILLON } 19941fef62c1SBoris BREZILLON 19951fef62c1SBoris BREZILLON if (test_and_set_bit(tmp, &nfc->assigned_cs)) { 19961fef62c1SBoris BREZILLON dev_err(dev, "CS %d already assigned\n", tmp); 19971fef62c1SBoris BREZILLON return -EINVAL; 19981fef62c1SBoris BREZILLON } 19991fef62c1SBoris BREZILLON 2000f385ebf0SBoris Brezillon sunxi_nand->sels[i].cs = tmp; 20011fef62c1SBoris BREZILLON 20021fef62c1SBoris BREZILLON if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && 2003ddd5ed3aSBoris Brezillon tmp < 2) 2004f385ebf0SBoris Brezillon sunxi_nand->sels[i].rb = tmp; 2005ddd5ed3aSBoris Brezillon else 2006f385ebf0SBoris Brezillon sunxi_nand->sels[i].rb = -1; 20071fef62c1SBoris BREZILLON } 20081fef62c1SBoris BREZILLON 2009f385ebf0SBoris Brezillon nand = &sunxi_nand->nand; 20101fef62c1SBoris BREZILLON /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ 20111fef62c1SBoris BREZILLON nand->controller = &nfc->controller; 20122a4d9c16SMiquel Raynal nand->controller->ops = &sunxi_nand_controller_ops; 20132a4d9c16SMiquel Raynal 2014a3d22a55SBoris BREZILLON /* 2015a3d22a55SBoris BREZILLON * Set the ECC mode to the default value in case nothing is specified 2016a3d22a55SBoris BREZILLON * in the DT. 2017a3d22a55SBoris BREZILLON */ 2018bace41f8SMiquel Raynal nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 201963752199SBrian Norris nand_set_flash_node(nand, np); 20201fef62c1SBoris BREZILLON 202132e9f2d8SBoris BREZILLON mtd = nand_to_mtd(nand); 20221fef62c1SBoris BREZILLON mtd->dev.parent = dev; 20231fef62c1SBoris BREZILLON 202400ad378fSBoris Brezillon ret = nand_scan(nand, nsels); 20251fef62c1SBoris BREZILLON if (ret) 20261fef62c1SBoris BREZILLON return ret; 20271fef62c1SBoris BREZILLON 2028a61ae81aSBrian Norris ret = mtd_device_register(mtd, NULL, 0); 20291fef62c1SBoris BREZILLON if (ret) { 20301fef62c1SBoris BREZILLON dev_err(dev, "failed to register mtd device: %d\n", ret); 20313d84515fSMiquel Raynal nand_cleanup(nand); 20321fef62c1SBoris BREZILLON return ret; 20331fef62c1SBoris BREZILLON } 20341fef62c1SBoris BREZILLON 2035f385ebf0SBoris Brezillon list_add_tail(&sunxi_nand->node, &nfc->chips); 20361fef62c1SBoris BREZILLON 20371fef62c1SBoris BREZILLON return 0; 20381fef62c1SBoris BREZILLON } 20391fef62c1SBoris BREZILLON 20401fef62c1SBoris BREZILLON static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) 20411fef62c1SBoris BREZILLON { 20421fef62c1SBoris BREZILLON struct device_node *np = dev->of_node; 20431fef62c1SBoris BREZILLON struct device_node *nand_np; 20441fef62c1SBoris BREZILLON int nchips = of_get_child_count(np); 20451fef62c1SBoris BREZILLON int ret; 20461fef62c1SBoris BREZILLON 20471fef62c1SBoris BREZILLON if (nchips > 8) { 20481fef62c1SBoris BREZILLON dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); 20491fef62c1SBoris BREZILLON return -EINVAL; 20501fef62c1SBoris BREZILLON } 20511fef62c1SBoris BREZILLON 20521fef62c1SBoris BREZILLON for_each_child_of_node(np, nand_np) { 20531fef62c1SBoris BREZILLON ret = sunxi_nand_chip_init(dev, nfc, nand_np); 2054a81c0f07SJulia Lawall if (ret) { 2055a81c0f07SJulia Lawall of_node_put(nand_np); 20561fef62c1SBoris BREZILLON return ret; 20571fef62c1SBoris BREZILLON } 2058a81c0f07SJulia Lawall } 20591fef62c1SBoris BREZILLON 20601fef62c1SBoris BREZILLON return 0; 20611fef62c1SBoris BREZILLON } 20621fef62c1SBoris BREZILLON 20631fef62c1SBoris BREZILLON static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) 20641fef62c1SBoris BREZILLON { 2065f385ebf0SBoris Brezillon struct sunxi_nand_chip *sunxi_nand; 2066068d86ecSMiquel Raynal struct nand_chip *chip; 2067068d86ecSMiquel Raynal int ret; 20681fef62c1SBoris BREZILLON 20691fef62c1SBoris BREZILLON while (!list_empty(&nfc->chips)) { 2070f385ebf0SBoris Brezillon sunxi_nand = list_first_entry(&nfc->chips, 2071f385ebf0SBoris Brezillon struct sunxi_nand_chip, 20721fef62c1SBoris BREZILLON node); 2073068d86ecSMiquel Raynal chip = &sunxi_nand->nand; 2074068d86ecSMiquel Raynal ret = mtd_device_unregister(nand_to_mtd(chip)); 2075068d86ecSMiquel Raynal WARN_ON(ret); 2076068d86ecSMiquel Raynal nand_cleanup(chip); 2077cbd87780SMiquel Raynal sunxi_nand_ecc_cleanup(sunxi_nand); 2078f385ebf0SBoris Brezillon list_del(&sunxi_nand->node); 20791fef62c1SBoris BREZILLON } 20801fef62c1SBoris BREZILLON } 20811fef62c1SBoris BREZILLON 2082910ef7a4SManuel Dipolt static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r) 2083910ef7a4SManuel Dipolt { 2084910ef7a4SManuel Dipolt int ret; 2085910ef7a4SManuel Dipolt 2086910ef7a4SManuel Dipolt if (nfc->caps->has_mdma) 2087910ef7a4SManuel Dipolt return 0; 2088910ef7a4SManuel Dipolt 2089910ef7a4SManuel Dipolt nfc->dmac = dma_request_chan(nfc->dev, "rxtx"); 2090910ef7a4SManuel Dipolt if (IS_ERR(nfc->dmac)) { 2091910ef7a4SManuel Dipolt ret = PTR_ERR(nfc->dmac); 2092910ef7a4SManuel Dipolt if (ret == -EPROBE_DEFER) 2093910ef7a4SManuel Dipolt return ret; 2094910ef7a4SManuel Dipolt 2095910ef7a4SManuel Dipolt /* Ignore errors to fall back to PIO mode */ 2096910ef7a4SManuel Dipolt dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret); 2097910ef7a4SManuel Dipolt nfc->dmac = NULL; 2098910ef7a4SManuel Dipolt } else { 2099910ef7a4SManuel Dipolt struct dma_slave_config dmac_cfg = { }; 2100910ef7a4SManuel Dipolt 2101910ef7a4SManuel Dipolt dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; 2102910ef7a4SManuel Dipolt dmac_cfg.dst_addr = dmac_cfg.src_addr; 2103910ef7a4SManuel Dipolt dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 2104910ef7a4SManuel Dipolt dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; 2105910ef7a4SManuel Dipolt dmac_cfg.src_maxburst = nfc->caps->dma_maxburst; 2106910ef7a4SManuel Dipolt dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst; 2107910ef7a4SManuel Dipolt dmaengine_slave_config(nfc->dmac, &dmac_cfg); 2108910ef7a4SManuel Dipolt } 2109910ef7a4SManuel Dipolt return 0; 2110910ef7a4SManuel Dipolt } 2111910ef7a4SManuel Dipolt 21121fef62c1SBoris BREZILLON static int sunxi_nfc_probe(struct platform_device *pdev) 21131fef62c1SBoris BREZILLON { 21141fef62c1SBoris BREZILLON struct device *dev = &pdev->dev; 21151fef62c1SBoris BREZILLON struct resource *r; 21161fef62c1SBoris BREZILLON struct sunxi_nfc *nfc; 21171fef62c1SBoris BREZILLON int irq; 21181fef62c1SBoris BREZILLON int ret; 21191fef62c1SBoris BREZILLON 21201fef62c1SBoris BREZILLON nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); 21211fef62c1SBoris BREZILLON if (!nfc) 21221fef62c1SBoris BREZILLON return -ENOMEM; 21231fef62c1SBoris BREZILLON 21241fef62c1SBoris BREZILLON nfc->dev = dev; 21257da45139SMiquel Raynal nand_controller_init(&nfc->controller); 21261fef62c1SBoris BREZILLON INIT_LIST_HEAD(&nfc->chips); 21271fef62c1SBoris BREZILLON 21281fef62c1SBoris BREZILLON r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 21291fef62c1SBoris BREZILLON nfc->regs = devm_ioremap_resource(dev, r); 21301fef62c1SBoris BREZILLON if (IS_ERR(nfc->regs)) 21311fef62c1SBoris BREZILLON return PTR_ERR(nfc->regs); 21321fef62c1SBoris BREZILLON 21331fef62c1SBoris BREZILLON irq = platform_get_irq(pdev, 0); 2134aab478caSStephen Boyd if (irq < 0) 21351fef62c1SBoris BREZILLON return irq; 21361fef62c1SBoris BREZILLON 21371fef62c1SBoris BREZILLON nfc->ahb_clk = devm_clk_get(dev, "ahb"); 21381fef62c1SBoris BREZILLON if (IS_ERR(nfc->ahb_clk)) { 21391fef62c1SBoris BREZILLON dev_err(dev, "failed to retrieve ahb clk\n"); 21401fef62c1SBoris BREZILLON return PTR_ERR(nfc->ahb_clk); 21411fef62c1SBoris BREZILLON } 21421fef62c1SBoris BREZILLON 21431fef62c1SBoris BREZILLON ret = clk_prepare_enable(nfc->ahb_clk); 21441fef62c1SBoris BREZILLON if (ret) 21451fef62c1SBoris BREZILLON return ret; 21461fef62c1SBoris BREZILLON 21471fef62c1SBoris BREZILLON nfc->mod_clk = devm_clk_get(dev, "mod"); 21481fef62c1SBoris BREZILLON if (IS_ERR(nfc->mod_clk)) { 21491fef62c1SBoris BREZILLON dev_err(dev, "failed to retrieve mod clk\n"); 21501fef62c1SBoris BREZILLON ret = PTR_ERR(nfc->mod_clk); 21511fef62c1SBoris BREZILLON goto out_ahb_clk_unprepare; 21521fef62c1SBoris BREZILLON } 21531fef62c1SBoris BREZILLON 21541fef62c1SBoris BREZILLON ret = clk_prepare_enable(nfc->mod_clk); 21551fef62c1SBoris BREZILLON if (ret) 21561fef62c1SBoris BREZILLON goto out_ahb_clk_unprepare; 21571fef62c1SBoris BREZILLON 2158fcf59f1fSPhilipp Zabel nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); 21596b244bbfSPhilipp Zabel if (IS_ERR(nfc->reset)) { 21606b244bbfSPhilipp Zabel ret = PTR_ERR(nfc->reset); 21616b244bbfSPhilipp Zabel goto out_mod_clk_unprepare; 21626b244bbfSPhilipp Zabel } 21636b244bbfSPhilipp Zabel 2164ab9d6a78SIcenowy Zheng ret = reset_control_deassert(nfc->reset); 2165ab9d6a78SIcenowy Zheng if (ret) { 2166ab9d6a78SIcenowy Zheng dev_err(dev, "reset err %d\n", ret); 2167ab9d6a78SIcenowy Zheng goto out_mod_clk_unprepare; 2168ab9d6a78SIcenowy Zheng } 2169ab9d6a78SIcenowy Zheng 2170a760e77dSMiquel Raynal nfc->caps = of_device_get_match_data(&pdev->dev); 2171a760e77dSMiquel Raynal if (!nfc->caps) { 2172a760e77dSMiquel Raynal ret = -EINVAL; 2173a760e77dSMiquel Raynal goto out_ahb_reset_reassert; 2174a760e77dSMiquel Raynal } 2175a760e77dSMiquel Raynal 21761fef62c1SBoris BREZILLON ret = sunxi_nfc_rst(nfc); 21771fef62c1SBoris BREZILLON if (ret) 2178ab9d6a78SIcenowy Zheng goto out_ahb_reset_reassert; 21791fef62c1SBoris BREZILLON 21801fef62c1SBoris BREZILLON writel(0, nfc->regs + NFC_REG_INT); 21811fef62c1SBoris BREZILLON ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, 21821fef62c1SBoris BREZILLON 0, "sunxi-nand", nfc); 21831fef62c1SBoris BREZILLON if (ret) 2184ab9d6a78SIcenowy Zheng goto out_ahb_reset_reassert; 21851fef62c1SBoris BREZILLON 2186910ef7a4SManuel Dipolt ret = sunxi_nfc_dma_init(nfc, r); 2187910ef7a4SManuel Dipolt 2188910ef7a4SManuel Dipolt if (ret) 2189ac80c55bSPeter Ujfalusi goto out_ahb_reset_reassert; 2190ac80c55bSPeter Ujfalusi 21911fef62c1SBoris BREZILLON platform_set_drvdata(pdev, nfc); 21921fef62c1SBoris BREZILLON 21931fef62c1SBoris BREZILLON ret = sunxi_nand_chips_init(dev, nfc); 21941fef62c1SBoris BREZILLON if (ret) { 21951fef62c1SBoris BREZILLON dev_err(dev, "failed to init nand chips\n"); 2196614049a8SBoris Brezillon goto out_release_dmac; 21971fef62c1SBoris BREZILLON } 21981fef62c1SBoris BREZILLON 21991fef62c1SBoris BREZILLON return 0; 22001fef62c1SBoris BREZILLON 2201614049a8SBoris Brezillon out_release_dmac: 2202614049a8SBoris Brezillon if (nfc->dmac) 2203614049a8SBoris Brezillon dma_release_channel(nfc->dmac); 2204ab9d6a78SIcenowy Zheng out_ahb_reset_reassert: 2205ab9d6a78SIcenowy Zheng reset_control_assert(nfc->reset); 22061fef62c1SBoris BREZILLON out_mod_clk_unprepare: 22071fef62c1SBoris BREZILLON clk_disable_unprepare(nfc->mod_clk); 22081fef62c1SBoris BREZILLON out_ahb_clk_unprepare: 22091fef62c1SBoris BREZILLON clk_disable_unprepare(nfc->ahb_clk); 22101fef62c1SBoris BREZILLON 22111fef62c1SBoris BREZILLON return ret; 22121fef62c1SBoris BREZILLON } 22131fef62c1SBoris BREZILLON 22141fef62c1SBoris BREZILLON static int sunxi_nfc_remove(struct platform_device *pdev) 22151fef62c1SBoris BREZILLON { 22161fef62c1SBoris BREZILLON struct sunxi_nfc *nfc = platform_get_drvdata(pdev); 22171fef62c1SBoris BREZILLON 22181fef62c1SBoris BREZILLON sunxi_nand_chips_cleanup(nfc); 2219ab9d6a78SIcenowy Zheng 2220ab9d6a78SIcenowy Zheng reset_control_assert(nfc->reset); 2221ab9d6a78SIcenowy Zheng 2222614049a8SBoris Brezillon if (nfc->dmac) 2223614049a8SBoris Brezillon dma_release_channel(nfc->dmac); 2224dd26a458SBoris Brezillon clk_disable_unprepare(nfc->mod_clk); 2225dd26a458SBoris Brezillon clk_disable_unprepare(nfc->ahb_clk); 22261fef62c1SBoris BREZILLON 22271fef62c1SBoris BREZILLON return 0; 22281fef62c1SBoris BREZILLON } 22291fef62c1SBoris BREZILLON 2230a760e77dSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { 2231a760e77dSMiquel Raynal .reg_io_data = NFC_REG_A10_IO_DATA, 2232a760e77dSMiquel Raynal .dma_maxburst = 4, 2233a760e77dSMiquel Raynal }; 2234a760e77dSMiquel Raynal 2235c7a87cebSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { 2236910ef7a4SManuel Dipolt .has_mdma = true, 2237c7a87cebSMiquel Raynal .reg_io_data = NFC_REG_A23_IO_DATA, 2238c7a87cebSMiquel Raynal .dma_maxburst = 8, 2239c7a87cebSMiquel Raynal }; 2240c7a87cebSMiquel Raynal 22411fef62c1SBoris BREZILLON static const struct of_device_id sunxi_nfc_ids[] = { 2242a760e77dSMiquel Raynal { 2243a760e77dSMiquel Raynal .compatible = "allwinner,sun4i-a10-nand", 2244a760e77dSMiquel Raynal .data = &sunxi_nfc_a10_caps, 2245a760e77dSMiquel Raynal }, 2246c7a87cebSMiquel Raynal { 2247c7a87cebSMiquel Raynal .compatible = "allwinner,sun8i-a23-nand-controller", 2248c7a87cebSMiquel Raynal .data = &sunxi_nfc_a23_caps, 2249c7a87cebSMiquel Raynal }, 22501fef62c1SBoris BREZILLON { /* sentinel */ } 22511fef62c1SBoris BREZILLON }; 22521fef62c1SBoris BREZILLON MODULE_DEVICE_TABLE(of, sunxi_nfc_ids); 22531fef62c1SBoris BREZILLON 22541fef62c1SBoris BREZILLON static struct platform_driver sunxi_nfc_driver = { 22551fef62c1SBoris BREZILLON .driver = { 22561fef62c1SBoris BREZILLON .name = "sunxi_nand", 22571fef62c1SBoris BREZILLON .of_match_table = sunxi_nfc_ids, 22581fef62c1SBoris BREZILLON }, 22591fef62c1SBoris BREZILLON .probe = sunxi_nfc_probe, 22601fef62c1SBoris BREZILLON .remove = sunxi_nfc_remove, 22611fef62c1SBoris BREZILLON }; 22621fef62c1SBoris BREZILLON module_platform_driver(sunxi_nfc_driver); 22631fef62c1SBoris BREZILLON 2264f5f88871SBoris Brezillon MODULE_LICENSE("GPL"); 22651fef62c1SBoris BREZILLON MODULE_AUTHOR("Boris BREZILLON"); 22661fef62c1SBoris BREZILLON MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); 22671fef62c1SBoris BREZILLON MODULE_ALIAS("platform:sunxi_nand"); 2268