1f5f88871SBoris Brezillon // SPDX-License-Identifier: GPL-2.0+
293db446aSBoris Brezillon /*
393db446aSBoris Brezillon * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
493db446aSBoris Brezillon *
593db446aSBoris Brezillon * Derived from:
693db446aSBoris Brezillon * https://github.com/yuq/sunxi-nfc-mtd
793db446aSBoris Brezillon * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
893db446aSBoris Brezillon *
993db446aSBoris Brezillon * https://github.com/hno/Allwinner-Info
1093db446aSBoris Brezillon * Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
1193db446aSBoris Brezillon *
1293db446aSBoris Brezillon * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
1393db446aSBoris Brezillon * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
1493db446aSBoris Brezillon */
1593db446aSBoris Brezillon
1693db446aSBoris Brezillon #include <linux/dma-mapping.h>
1793db446aSBoris Brezillon #include <linux/slab.h>
1893db446aSBoris Brezillon #include <linux/module.h>
1993db446aSBoris Brezillon #include <linux/moduleparam.h>
2093db446aSBoris Brezillon #include <linux/platform_device.h>
2193db446aSBoris Brezillon #include <linux/of.h>
2293db446aSBoris Brezillon #include <linux/mtd/mtd.h>
2393db446aSBoris Brezillon #include <linux/mtd/rawnand.h>
2493db446aSBoris Brezillon #include <linux/mtd/partitions.h>
2593db446aSBoris Brezillon #include <linux/clk.h>
2693db446aSBoris Brezillon #include <linux/delay.h>
2793db446aSBoris Brezillon #include <linux/dmaengine.h>
2893db446aSBoris Brezillon #include <linux/interrupt.h>
2993db446aSBoris Brezillon #include <linux/iopoll.h>
3093db446aSBoris Brezillon #include <linux/reset.h>
3193db446aSBoris Brezillon
3293db446aSBoris Brezillon #define NFC_REG_CTL 0x0000
3393db446aSBoris Brezillon #define NFC_REG_ST 0x0004
3493db446aSBoris Brezillon #define NFC_REG_INT 0x0008
3593db446aSBoris Brezillon #define NFC_REG_TIMING_CTL 0x000C
3693db446aSBoris Brezillon #define NFC_REG_TIMING_CFG 0x0010
3793db446aSBoris Brezillon #define NFC_REG_ADDR_LOW 0x0014
3893db446aSBoris Brezillon #define NFC_REG_ADDR_HIGH 0x0018
3993db446aSBoris Brezillon #define NFC_REG_SECTOR_NUM 0x001C
4093db446aSBoris Brezillon #define NFC_REG_CNT 0x0020
4193db446aSBoris Brezillon #define NFC_REG_CMD 0x0024
4293db446aSBoris Brezillon #define NFC_REG_RCMD_SET 0x0028
4393db446aSBoris Brezillon #define NFC_REG_WCMD_SET 0x002C
44a760e77dSMiquel Raynal #define NFC_REG_A10_IO_DATA 0x0030
45c7a87cebSMiquel Raynal #define NFC_REG_A23_IO_DATA 0x0300
4693db446aSBoris Brezillon #define NFC_REG_ECC_CTL 0x0034
4793db446aSBoris Brezillon #define NFC_REG_ECC_ST 0x0038
4893db446aSBoris Brezillon #define NFC_REG_DEBUG 0x003C
4993db446aSBoris Brezillon #define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
5093db446aSBoris Brezillon #define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
5193db446aSBoris Brezillon #define NFC_REG_SPARE_AREA 0x00A0
5293db446aSBoris Brezillon #define NFC_REG_PAT_ID 0x00A4
53910ef7a4SManuel Dipolt #define NFC_REG_MDMA_ADDR 0x00C0
54c7a87cebSMiquel Raynal #define NFC_REG_MDMA_CNT 0x00C4
5593db446aSBoris Brezillon #define NFC_RAM0_BASE 0x0400
5693db446aSBoris Brezillon #define NFC_RAM1_BASE 0x0800
5793db446aSBoris Brezillon
5893db446aSBoris Brezillon /* define bit use in NFC_CTL */
5993db446aSBoris Brezillon #define NFC_EN BIT(0)
6093db446aSBoris Brezillon #define NFC_RESET BIT(1)
6193db446aSBoris Brezillon #define NFC_BUS_WIDTH_MSK BIT(2)
6293db446aSBoris Brezillon #define NFC_BUS_WIDTH_8 (0 << 2)
6393db446aSBoris Brezillon #define NFC_BUS_WIDTH_16 (1 << 2)
6493db446aSBoris Brezillon #define NFC_RB_SEL_MSK BIT(3)
6593db446aSBoris Brezillon #define NFC_RB_SEL(x) ((x) << 3)
6693db446aSBoris Brezillon #define NFC_CE_SEL_MSK GENMASK(26, 24)
6793db446aSBoris Brezillon #define NFC_CE_SEL(x) ((x) << 24)
6893db446aSBoris Brezillon #define NFC_CE_CTL BIT(6)
6993db446aSBoris Brezillon #define NFC_PAGE_SHIFT_MSK GENMASK(11, 8)
7093db446aSBoris Brezillon #define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8)
7193db446aSBoris Brezillon #define NFC_SAM BIT(12)
7293db446aSBoris Brezillon #define NFC_RAM_METHOD BIT(14)
73c7a87cebSMiquel Raynal #define NFC_DMA_TYPE_NORMAL BIT(15)
7493db446aSBoris Brezillon #define NFC_DEBUG_CTL BIT(31)
7593db446aSBoris Brezillon
7693db446aSBoris Brezillon /* define bit use in NFC_ST */
7793db446aSBoris Brezillon #define NFC_RB_B2R BIT(0)
7893db446aSBoris Brezillon #define NFC_CMD_INT_FLAG BIT(1)
7993db446aSBoris Brezillon #define NFC_DMA_INT_FLAG BIT(2)
8093db446aSBoris Brezillon #define NFC_CMD_FIFO_STATUS BIT(3)
8193db446aSBoris Brezillon #define NFC_STA BIT(4)
8293db446aSBoris Brezillon #define NFC_NATCH_INT_FLAG BIT(5)
8393db446aSBoris Brezillon #define NFC_RB_STATE(x) BIT(x + 8)
8493db446aSBoris Brezillon
8593db446aSBoris Brezillon /* define bit use in NFC_INT */
8693db446aSBoris Brezillon #define NFC_B2R_INT_ENABLE BIT(0)
8793db446aSBoris Brezillon #define NFC_CMD_INT_ENABLE BIT(1)
8893db446aSBoris Brezillon #define NFC_DMA_INT_ENABLE BIT(2)
8993db446aSBoris Brezillon #define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
9093db446aSBoris Brezillon NFC_CMD_INT_ENABLE | \
9193db446aSBoris Brezillon NFC_DMA_INT_ENABLE)
9293db446aSBoris Brezillon
9393db446aSBoris Brezillon /* define bit use in NFC_TIMING_CTL */
9493db446aSBoris Brezillon #define NFC_TIMING_CTL_EDO BIT(8)
9593db446aSBoris Brezillon
9693db446aSBoris Brezillon /* define NFC_TIMING_CFG register layout */
9793db446aSBoris Brezillon #define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
9893db446aSBoris Brezillon (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \
9993db446aSBoris Brezillon (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
10093db446aSBoris Brezillon (((tCAD) & 0x7) << 8))
10193db446aSBoris Brezillon
10293db446aSBoris Brezillon /* define bit use in NFC_CMD */
10393db446aSBoris Brezillon #define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0)
10493db446aSBoris Brezillon #define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8)
10593db446aSBoris Brezillon #define NFC_CMD(x) (x)
10693db446aSBoris Brezillon #define NFC_ADR_NUM_MSK GENMASK(18, 16)
10793db446aSBoris Brezillon #define NFC_ADR_NUM(x) (((x) - 1) << 16)
10893db446aSBoris Brezillon #define NFC_SEND_ADR BIT(19)
10993db446aSBoris Brezillon #define NFC_ACCESS_DIR BIT(20)
11093db446aSBoris Brezillon #define NFC_DATA_TRANS BIT(21)
11193db446aSBoris Brezillon #define NFC_SEND_CMD1 BIT(22)
11293db446aSBoris Brezillon #define NFC_WAIT_FLAG BIT(23)
11393db446aSBoris Brezillon #define NFC_SEND_CMD2 BIT(24)
11493db446aSBoris Brezillon #define NFC_SEQ BIT(25)
11593db446aSBoris Brezillon #define NFC_DATA_SWAP_METHOD BIT(26)
11693db446aSBoris Brezillon #define NFC_ROW_AUTO_INC BIT(27)
11793db446aSBoris Brezillon #define NFC_SEND_CMD3 BIT(28)
11893db446aSBoris Brezillon #define NFC_SEND_CMD4 BIT(29)
11993db446aSBoris Brezillon #define NFC_CMD_TYPE_MSK GENMASK(31, 30)
12093db446aSBoris Brezillon #define NFC_NORMAL_OP (0 << 30)
12193db446aSBoris Brezillon #define NFC_ECC_OP (1 << 30)
122cf3e3fd2SBoris Brezillon #define NFC_PAGE_OP (2U << 30)
12393db446aSBoris Brezillon
12493db446aSBoris Brezillon /* define bit use in NFC_RCMD_SET */
12593db446aSBoris Brezillon #define NFC_READ_CMD_MSK GENMASK(7, 0)
12693db446aSBoris Brezillon #define NFC_RND_READ_CMD0_MSK GENMASK(15, 8)
12793db446aSBoris Brezillon #define NFC_RND_READ_CMD1_MSK GENMASK(23, 16)
12893db446aSBoris Brezillon
12993db446aSBoris Brezillon /* define bit use in NFC_WCMD_SET */
13093db446aSBoris Brezillon #define NFC_PROGRAM_CMD_MSK GENMASK(7, 0)
13193db446aSBoris Brezillon #define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8)
13293db446aSBoris Brezillon #define NFC_READ_CMD0_MSK GENMASK(23, 16)
13393db446aSBoris Brezillon #define NFC_READ_CMD1_MSK GENMASK(31, 24)
13493db446aSBoris Brezillon
13593db446aSBoris Brezillon /* define bit use in NFC_ECC_CTL */
13693db446aSBoris Brezillon #define NFC_ECC_EN BIT(0)
13793db446aSBoris Brezillon #define NFC_ECC_PIPELINE BIT(3)
13893db446aSBoris Brezillon #define NFC_ECC_EXCEPTION BIT(4)
13993db446aSBoris Brezillon #define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
14093db446aSBoris Brezillon #define NFC_ECC_BLOCK_512 BIT(5)
14193db446aSBoris Brezillon #define NFC_RANDOM_EN BIT(9)
14293db446aSBoris Brezillon #define NFC_RANDOM_DIRECTION BIT(10)
14393db446aSBoris Brezillon #define NFC_ECC_MODE_MSK GENMASK(15, 12)
14493db446aSBoris Brezillon #define NFC_ECC_MODE(x) ((x) << 12)
14593db446aSBoris Brezillon #define NFC_RANDOM_SEED_MSK GENMASK(30, 16)
14693db446aSBoris Brezillon #define NFC_RANDOM_SEED(x) ((x) << 16)
14793db446aSBoris Brezillon
14893db446aSBoris Brezillon /* define bit use in NFC_ECC_ST */
14993db446aSBoris Brezillon #define NFC_ECC_ERR(x) BIT(x)
15093db446aSBoris Brezillon #define NFC_ECC_ERR_MSK GENMASK(15, 0)
15193db446aSBoris Brezillon #define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
15293db446aSBoris Brezillon #define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
15393db446aSBoris Brezillon
15493db446aSBoris Brezillon #define NFC_DEFAULT_TIMEOUT_MS 1000
15593db446aSBoris Brezillon
15693db446aSBoris Brezillon #define NFC_SRAM_SIZE 1024
15793db446aSBoris Brezillon
15893db446aSBoris Brezillon #define NFC_MAX_CS 7
15993db446aSBoris Brezillon
16067c88008SBoris Brezillon /**
16167c88008SBoris Brezillon * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
16293db446aSBoris Brezillon *
16393db446aSBoris Brezillon * @cs: the NAND CS id used to communicate with a NAND Chip
16467c88008SBoris Brezillon * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the NFC
16593db446aSBoris Brezillon */
16693db446aSBoris Brezillon struct sunxi_nand_chip_sel {
16793db446aSBoris Brezillon u8 cs;
168ddd5ed3aSBoris Brezillon s8 rb;
16993db446aSBoris Brezillon };
17093db446aSBoris Brezillon
17167c88008SBoris Brezillon /**
17267c88008SBoris Brezillon * struct sunxi_nand_hw_ecc - stores information related to HW ECC support
17393db446aSBoris Brezillon *
174ef3e6327SSamuel Holland * @ecc_ctl: ECC_CTL register value for this NAND chip
17593db446aSBoris Brezillon */
17693db446aSBoris Brezillon struct sunxi_nand_hw_ecc {
177ef3e6327SSamuel Holland u32 ecc_ctl;
17893db446aSBoris Brezillon };
17993db446aSBoris Brezillon
18067c88008SBoris Brezillon /**
18167c88008SBoris Brezillon * struct sunxi_nand_chip - stores NAND chip device related information
18293db446aSBoris Brezillon *
18393db446aSBoris Brezillon * @node: used to store NAND chips into a list
18493db446aSBoris Brezillon * @nand: base NAND chip structure
185cbd87780SMiquel Raynal * @ecc: ECC controller structure
18693db446aSBoris Brezillon * @clk_rate: clk_rate required for this NAND chip
18767c88008SBoris Brezillon * @timing_cfg: TIMING_CFG register value for this NAND chip
18867c88008SBoris Brezillon * @timing_ctl: TIMING_CTL register value for this NAND chip
18993db446aSBoris Brezillon * @nsels: number of CS lines required by the NAND chip
19093db446aSBoris Brezillon * @sels: array of CS lines descriptions
19193db446aSBoris Brezillon */
19293db446aSBoris Brezillon struct sunxi_nand_chip {
19393db446aSBoris Brezillon struct list_head node;
19493db446aSBoris Brezillon struct nand_chip nand;
195ac1c7072SSamuel Holland struct sunxi_nand_hw_ecc ecc;
19693db446aSBoris Brezillon unsigned long clk_rate;
19793db446aSBoris Brezillon u32 timing_cfg;
19893db446aSBoris Brezillon u32 timing_ctl;
19993db446aSBoris Brezillon int nsels;
20049f1c330SGustavo A. R. Silva struct sunxi_nand_chip_sel sels[];
20193db446aSBoris Brezillon };
20293db446aSBoris Brezillon
to_sunxi_nand(struct nand_chip * nand)20393db446aSBoris Brezillon static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
20493db446aSBoris Brezillon {
20593db446aSBoris Brezillon return container_of(nand, struct sunxi_nand_chip, nand);
20693db446aSBoris Brezillon }
20793db446aSBoris Brezillon
208a760e77dSMiquel Raynal /*
209a760e77dSMiquel Raynal * NAND Controller capabilities structure: stores NAND controller capabilities
210a760e77dSMiquel Raynal * for distinction between compatible strings.
211a760e77dSMiquel Raynal *
212910ef7a4SManuel Dipolt * @has_mdma: Use mbus dma mode, otherwise general dma
213c7a87cebSMiquel Raynal * through MBUS on A23/A33 needs extra configuration.
214a760e77dSMiquel Raynal * @reg_io_data: I/O data register
215a760e77dSMiquel Raynal * @dma_maxburst: DMA maxburst
216a760e77dSMiquel Raynal */
217a760e77dSMiquel Raynal struct sunxi_nfc_caps {
218910ef7a4SManuel Dipolt bool has_mdma;
219a760e77dSMiquel Raynal unsigned int reg_io_data;
220a760e77dSMiquel Raynal unsigned int dma_maxburst;
221a760e77dSMiquel Raynal };
222a760e77dSMiquel Raynal
22367c88008SBoris Brezillon /**
22467c88008SBoris Brezillon * struct sunxi_nfc - stores sunxi NAND controller information
22593db446aSBoris Brezillon *
22693db446aSBoris Brezillon * @controller: base controller structure
22793db446aSBoris Brezillon * @dev: parent device (used to print error messages)
22893db446aSBoris Brezillon * @regs: NAND controller registers
22967c88008SBoris Brezillon * @ahb_clk: NAND controller AHB clock
23067c88008SBoris Brezillon * @mod_clk: NAND controller mod clock
23167c88008SBoris Brezillon * @reset: NAND controller reset line
23293db446aSBoris Brezillon * @assigned_cs: bitmask describing already assigned CS lines
23393db446aSBoris Brezillon * @clk_rate: NAND controller current clock rate
23467c88008SBoris Brezillon * @chips: a list containing all the NAND chips attached to this NAND
23567c88008SBoris Brezillon * controller
23667c88008SBoris Brezillon * @complete: a completion object used to wait for NAND controller events
23767c88008SBoris Brezillon * @dmac: the DMA channel attached to the NAND controller
2380d5c506dSLee Jones * @caps: NAND Controller capabilities
23993db446aSBoris Brezillon */
24093db446aSBoris Brezillon struct sunxi_nfc {
2417da45139SMiquel Raynal struct nand_controller controller;
24293db446aSBoris Brezillon struct device *dev;
24393db446aSBoris Brezillon void __iomem *regs;
24493db446aSBoris Brezillon struct clk *ahb_clk;
24593db446aSBoris Brezillon struct clk *mod_clk;
24693db446aSBoris Brezillon struct reset_control *reset;
24793db446aSBoris Brezillon unsigned long assigned_cs;
24893db446aSBoris Brezillon unsigned long clk_rate;
24993db446aSBoris Brezillon struct list_head chips;
25093db446aSBoris Brezillon struct completion complete;
25193db446aSBoris Brezillon struct dma_chan *dmac;
252a760e77dSMiquel Raynal const struct sunxi_nfc_caps *caps;
25393db446aSBoris Brezillon };
25493db446aSBoris Brezillon
to_sunxi_nfc(struct nand_controller * ctrl)2557da45139SMiquel Raynal static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl)
25693db446aSBoris Brezillon {
25793db446aSBoris Brezillon return container_of(ctrl, struct sunxi_nfc, controller);
25893db446aSBoris Brezillon }
25993db446aSBoris Brezillon
sunxi_nfc_interrupt(int irq,void * dev_id)26093db446aSBoris Brezillon static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
26193db446aSBoris Brezillon {
26293db446aSBoris Brezillon struct sunxi_nfc *nfc = dev_id;
26393db446aSBoris Brezillon u32 st = readl(nfc->regs + NFC_REG_ST);
26493db446aSBoris Brezillon u32 ien = readl(nfc->regs + NFC_REG_INT);
26593db446aSBoris Brezillon
26693db446aSBoris Brezillon if (!(ien & st))
26793db446aSBoris Brezillon return IRQ_NONE;
26893db446aSBoris Brezillon
26993db446aSBoris Brezillon if ((ien & st) == ien)
27093db446aSBoris Brezillon complete(&nfc->complete);
27193db446aSBoris Brezillon
27293db446aSBoris Brezillon writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
27393db446aSBoris Brezillon writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
27493db446aSBoris Brezillon
27593db446aSBoris Brezillon return IRQ_HANDLED;
27693db446aSBoris Brezillon }
27793db446aSBoris Brezillon
sunxi_nfc_wait_events(struct sunxi_nfc * nfc,u32 events,bool use_polling,unsigned int timeout_ms)27893db446aSBoris Brezillon static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
27993db446aSBoris Brezillon bool use_polling, unsigned int timeout_ms)
28093db446aSBoris Brezillon {
28193db446aSBoris Brezillon int ret;
28293db446aSBoris Brezillon
28393db446aSBoris Brezillon if (events & ~NFC_INT_MASK)
28493db446aSBoris Brezillon return -EINVAL;
28593db446aSBoris Brezillon
28693db446aSBoris Brezillon if (!timeout_ms)
28793db446aSBoris Brezillon timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
28893db446aSBoris Brezillon
28993db446aSBoris Brezillon if (!use_polling) {
29093db446aSBoris Brezillon init_completion(&nfc->complete);
29193db446aSBoris Brezillon
29293db446aSBoris Brezillon writel(events, nfc->regs + NFC_REG_INT);
29393db446aSBoris Brezillon
29493db446aSBoris Brezillon ret = wait_for_completion_timeout(&nfc->complete,
29593db446aSBoris Brezillon msecs_to_jiffies(timeout_ms));
29693db446aSBoris Brezillon if (!ret)
29793db446aSBoris Brezillon ret = -ETIMEDOUT;
29893db446aSBoris Brezillon else
29993db446aSBoris Brezillon ret = 0;
30093db446aSBoris Brezillon
30193db446aSBoris Brezillon writel(0, nfc->regs + NFC_REG_INT);
30293db446aSBoris Brezillon } else {
30393db446aSBoris Brezillon u32 status;
30493db446aSBoris Brezillon
30593db446aSBoris Brezillon ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
30693db446aSBoris Brezillon (status & events) == events, 1,
30793db446aSBoris Brezillon timeout_ms * 1000);
30893db446aSBoris Brezillon }
30993db446aSBoris Brezillon
31093db446aSBoris Brezillon writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
31193db446aSBoris Brezillon
31293db446aSBoris Brezillon if (ret)
31393db446aSBoris Brezillon dev_err(nfc->dev, "wait interrupt timedout\n");
31493db446aSBoris Brezillon
31593db446aSBoris Brezillon return ret;
31693db446aSBoris Brezillon }
31793db446aSBoris Brezillon
sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc * nfc)31893db446aSBoris Brezillon static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
31993db446aSBoris Brezillon {
32093db446aSBoris Brezillon u32 status;
32193db446aSBoris Brezillon int ret;
32293db446aSBoris Brezillon
32393db446aSBoris Brezillon ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status,
32493db446aSBoris Brezillon !(status & NFC_CMD_FIFO_STATUS), 1,
32593db446aSBoris Brezillon NFC_DEFAULT_TIMEOUT_MS * 1000);
32693db446aSBoris Brezillon if (ret)
32793db446aSBoris Brezillon dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
32893db446aSBoris Brezillon
32993db446aSBoris Brezillon return ret;
33093db446aSBoris Brezillon }
33193db446aSBoris Brezillon
sunxi_nfc_rst(struct sunxi_nfc * nfc)33293db446aSBoris Brezillon static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
33393db446aSBoris Brezillon {
33493db446aSBoris Brezillon u32 ctl;
33593db446aSBoris Brezillon int ret;
33693db446aSBoris Brezillon
33793db446aSBoris Brezillon writel(0, nfc->regs + NFC_REG_ECC_CTL);
33893db446aSBoris Brezillon writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
33993db446aSBoris Brezillon
34093db446aSBoris Brezillon ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl,
34193db446aSBoris Brezillon !(ctl & NFC_RESET), 1,
34293db446aSBoris Brezillon NFC_DEFAULT_TIMEOUT_MS * 1000);
34393db446aSBoris Brezillon if (ret)
34493db446aSBoris Brezillon dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
34593db446aSBoris Brezillon
34693db446aSBoris Brezillon return ret;
34793db446aSBoris Brezillon }
34893db446aSBoris Brezillon
sunxi_nfc_dma_op_prepare(struct sunxi_nfc * nfc,const void * buf,int chunksize,int nchunks,enum dma_data_direction ddir,struct scatterlist * sg)349cde567e3SBoris Brezillon static int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf,
35093db446aSBoris Brezillon int chunksize, int nchunks,
35193db446aSBoris Brezillon enum dma_data_direction ddir,
35293db446aSBoris Brezillon struct scatterlist *sg)
35393db446aSBoris Brezillon {
35493db446aSBoris Brezillon struct dma_async_tx_descriptor *dmad;
35593db446aSBoris Brezillon enum dma_transfer_direction tdir;
35693db446aSBoris Brezillon dma_cookie_t dmat;
35793db446aSBoris Brezillon int ret;
35893db446aSBoris Brezillon
35993db446aSBoris Brezillon if (ddir == DMA_FROM_DEVICE)
36093db446aSBoris Brezillon tdir = DMA_DEV_TO_MEM;
36193db446aSBoris Brezillon else
36293db446aSBoris Brezillon tdir = DMA_MEM_TO_DEV;
36393db446aSBoris Brezillon
36493db446aSBoris Brezillon sg_init_one(sg, buf, nchunks * chunksize);
36593db446aSBoris Brezillon ret = dma_map_sg(nfc->dev, sg, 1, ddir);
36693db446aSBoris Brezillon if (!ret)
36793db446aSBoris Brezillon return -ENOMEM;
36893db446aSBoris Brezillon
369910ef7a4SManuel Dipolt if (!nfc->caps->has_mdma) {
37093db446aSBoris Brezillon dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK);
37193db446aSBoris Brezillon if (!dmad) {
37293db446aSBoris Brezillon ret = -EINVAL;
37393db446aSBoris Brezillon goto err_unmap_buf;
37493db446aSBoris Brezillon }
375910ef7a4SManuel Dipolt }
37693db446aSBoris Brezillon
37793db446aSBoris Brezillon writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD,
37893db446aSBoris Brezillon nfc->regs + NFC_REG_CTL);
37993db446aSBoris Brezillon writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM);
38093db446aSBoris Brezillon writel(chunksize, nfc->regs + NFC_REG_CNT);
381c7a87cebSMiquel Raynal
382910ef7a4SManuel Dipolt if (nfc->caps->has_mdma) {
383910ef7a4SManuel Dipolt writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL,
384910ef7a4SManuel Dipolt nfc->regs + NFC_REG_CTL);
385910ef7a4SManuel Dipolt writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT);
386910ef7a4SManuel Dipolt writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR);
387910ef7a4SManuel Dipolt } else {
38893db446aSBoris Brezillon dmat = dmaengine_submit(dmad);
38993db446aSBoris Brezillon
39093db446aSBoris Brezillon ret = dma_submit_error(dmat);
39193db446aSBoris Brezillon if (ret)
39293db446aSBoris Brezillon goto err_clr_dma_flag;
393910ef7a4SManuel Dipolt }
39493db446aSBoris Brezillon
39593db446aSBoris Brezillon return 0;
39693db446aSBoris Brezillon
39793db446aSBoris Brezillon err_clr_dma_flag:
39893db446aSBoris Brezillon writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
39993db446aSBoris Brezillon nfc->regs + NFC_REG_CTL);
40093db446aSBoris Brezillon
40193db446aSBoris Brezillon err_unmap_buf:
40293db446aSBoris Brezillon dma_unmap_sg(nfc->dev, sg, 1, ddir);
40393db446aSBoris Brezillon return ret;
40493db446aSBoris Brezillon }
40593db446aSBoris Brezillon
sunxi_nfc_dma_op_cleanup(struct sunxi_nfc * nfc,enum dma_data_direction ddir,struct scatterlist * sg)406cde567e3SBoris Brezillon static void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc,
40793db446aSBoris Brezillon enum dma_data_direction ddir,
40893db446aSBoris Brezillon struct scatterlist *sg)
40993db446aSBoris Brezillon {
41093db446aSBoris Brezillon dma_unmap_sg(nfc->dev, sg, 1, ddir);
41193db446aSBoris Brezillon writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD,
41293db446aSBoris Brezillon nfc->regs + NFC_REG_CTL);
41393db446aSBoris Brezillon }
41493db446aSBoris Brezillon
sunxi_nfc_select_chip(struct nand_chip * nand,unsigned int cs)415df505799SBoris Brezillon static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
41693db446aSBoris Brezillon {
417758b56f5SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
41893db446aSBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
41993db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
42093db446aSBoris Brezillon struct sunxi_nand_chip_sel *sel;
42193db446aSBoris Brezillon u32 ctl;
42293db446aSBoris Brezillon
42359186a40SSamuel Holland if (cs >= sunxi_nand->nsels)
42493db446aSBoris Brezillon return;
42593db446aSBoris Brezillon
42693db446aSBoris Brezillon ctl = readl(nfc->regs + NFC_REG_CTL) &
42793db446aSBoris Brezillon ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN);
42893db446aSBoris Brezillon
429df505799SBoris Brezillon sel = &sunxi_nand->sels[cs];
430df505799SBoris Brezillon ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift);
431df505799SBoris Brezillon if (sel->rb >= 0)
432ddd5ed3aSBoris Brezillon ctl |= NFC_RB_SEL(sel->rb);
43393db446aSBoris Brezillon
43493db446aSBoris Brezillon writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
43593db446aSBoris Brezillon
43693db446aSBoris Brezillon if (nfc->clk_rate != sunxi_nand->clk_rate) {
43793db446aSBoris Brezillon clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
43893db446aSBoris Brezillon nfc->clk_rate = sunxi_nand->clk_rate;
43993db446aSBoris Brezillon }
44093db446aSBoris Brezillon
44193db446aSBoris Brezillon writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL);
44293db446aSBoris Brezillon writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG);
44393db446aSBoris Brezillon writel(ctl, nfc->regs + NFC_REG_CTL);
44493db446aSBoris Brezillon }
44593db446aSBoris Brezillon
sunxi_nfc_read_buf(struct nand_chip * nand,uint8_t * buf,int len)4467e534323SBoris Brezillon static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len)
44793db446aSBoris Brezillon {
44893db446aSBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
44993db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
45093db446aSBoris Brezillon int ret;
45193db446aSBoris Brezillon int cnt;
45293db446aSBoris Brezillon int offs = 0;
45393db446aSBoris Brezillon u32 tmp;
45493db446aSBoris Brezillon
45593db446aSBoris Brezillon while (len > offs) {
45693db446aSBoris Brezillon bool poll = false;
45793db446aSBoris Brezillon
45893db446aSBoris Brezillon cnt = min(len - offs, NFC_SRAM_SIZE);
45993db446aSBoris Brezillon
46093db446aSBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
46193db446aSBoris Brezillon if (ret)
46293db446aSBoris Brezillon break;
46393db446aSBoris Brezillon
46493db446aSBoris Brezillon writel(cnt, nfc->regs + NFC_REG_CNT);
46593db446aSBoris Brezillon tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
46693db446aSBoris Brezillon writel(tmp, nfc->regs + NFC_REG_CMD);
46793db446aSBoris Brezillon
46893db446aSBoris Brezillon /* Arbitrary limit for polling mode */
46993db446aSBoris Brezillon if (cnt < 64)
47093db446aSBoris Brezillon poll = true;
47193db446aSBoris Brezillon
47293db446aSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
47393db446aSBoris Brezillon if (ret)
47493db446aSBoris Brezillon break;
47593db446aSBoris Brezillon
47693db446aSBoris Brezillon if (buf)
47793db446aSBoris Brezillon memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
47893db446aSBoris Brezillon cnt);
47993db446aSBoris Brezillon offs += cnt;
48093db446aSBoris Brezillon }
48193db446aSBoris Brezillon }
48293db446aSBoris Brezillon
sunxi_nfc_write_buf(struct nand_chip * nand,const uint8_t * buf,int len)483c0739d85SBoris Brezillon static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf,
48493db446aSBoris Brezillon int len)
48593db446aSBoris Brezillon {
48693db446aSBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
48793db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
48893db446aSBoris Brezillon int ret;
48993db446aSBoris Brezillon int cnt;
49093db446aSBoris Brezillon int offs = 0;
49193db446aSBoris Brezillon u32 tmp;
49293db446aSBoris Brezillon
49393db446aSBoris Brezillon while (len > offs) {
49493db446aSBoris Brezillon bool poll = false;
49593db446aSBoris Brezillon
49693db446aSBoris Brezillon cnt = min(len - offs, NFC_SRAM_SIZE);
49793db446aSBoris Brezillon
49893db446aSBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
49993db446aSBoris Brezillon if (ret)
50093db446aSBoris Brezillon break;
50193db446aSBoris Brezillon
50293db446aSBoris Brezillon writel(cnt, nfc->regs + NFC_REG_CNT);
50393db446aSBoris Brezillon memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
50493db446aSBoris Brezillon tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
50593db446aSBoris Brezillon NFC_ACCESS_DIR;
50693db446aSBoris Brezillon writel(tmp, nfc->regs + NFC_REG_CMD);
50793db446aSBoris Brezillon
50893db446aSBoris Brezillon /* Arbitrary limit for polling mode */
50993db446aSBoris Brezillon if (cnt < 64)
51093db446aSBoris Brezillon poll = true;
51193db446aSBoris Brezillon
51293db446aSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
51393db446aSBoris Brezillon if (ret)
51493db446aSBoris Brezillon break;
51593db446aSBoris Brezillon
51693db446aSBoris Brezillon offs += cnt;
51793db446aSBoris Brezillon }
51893db446aSBoris Brezillon }
51993db446aSBoris Brezillon
52093db446aSBoris Brezillon /* These seed values have been extracted from Allwinner's BSP */
52193db446aSBoris Brezillon static const u16 sunxi_nfc_randomizer_page_seeds[] = {
52293db446aSBoris Brezillon 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
52393db446aSBoris Brezillon 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
52493db446aSBoris Brezillon 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
52593db446aSBoris Brezillon 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
52693db446aSBoris Brezillon 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
52793db446aSBoris Brezillon 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
52893db446aSBoris Brezillon 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
52993db446aSBoris Brezillon 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
53093db446aSBoris Brezillon 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
53193db446aSBoris Brezillon 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
53293db446aSBoris Brezillon 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
53393db446aSBoris Brezillon 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
53493db446aSBoris Brezillon 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
53593db446aSBoris Brezillon 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
53693db446aSBoris Brezillon 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
53793db446aSBoris Brezillon 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
53893db446aSBoris Brezillon };
53993db446aSBoris Brezillon
54093db446aSBoris Brezillon /*
54193db446aSBoris Brezillon * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds
54293db446aSBoris Brezillon * have been generated using
54393db446aSBoris Brezillon * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what
54493db446aSBoris Brezillon * the randomizer engine does internally before de/scrambling OOB data.
54593db446aSBoris Brezillon *
54693db446aSBoris Brezillon * Those tables are statically defined to avoid calculating randomizer state
54793db446aSBoris Brezillon * at runtime.
54893db446aSBoris Brezillon */
54993db446aSBoris Brezillon static const u16 sunxi_nfc_randomizer_ecc512_seeds[] = {
55093db446aSBoris Brezillon 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64,
55193db446aSBoris Brezillon 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409,
55293db446aSBoris Brezillon 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617,
55393db446aSBoris Brezillon 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d,
55493db446aSBoris Brezillon 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91,
55593db446aSBoris Brezillon 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d,
55693db446aSBoris Brezillon 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab,
55793db446aSBoris Brezillon 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8,
55893db446aSBoris Brezillon 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8,
55993db446aSBoris Brezillon 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b,
56093db446aSBoris Brezillon 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5,
56193db446aSBoris Brezillon 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a,
56293db446aSBoris Brezillon 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891,
56393db446aSBoris Brezillon 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36,
56493db446aSBoris Brezillon 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd,
56593db446aSBoris Brezillon 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0,
56693db446aSBoris Brezillon };
56793db446aSBoris Brezillon
56893db446aSBoris Brezillon static const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = {
56993db446aSBoris Brezillon 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6,
57093db446aSBoris Brezillon 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982,
57193db446aSBoris Brezillon 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9,
57293db446aSBoris Brezillon 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07,
57393db446aSBoris Brezillon 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e,
57493db446aSBoris Brezillon 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2,
57593db446aSBoris Brezillon 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c,
57693db446aSBoris Brezillon 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f,
57793db446aSBoris Brezillon 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc,
57893db446aSBoris Brezillon 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e,
57993db446aSBoris Brezillon 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8,
58093db446aSBoris Brezillon 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68,
58193db446aSBoris Brezillon 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d,
58293db446aSBoris Brezillon 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179,
58393db446aSBoris Brezillon 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601,
58493db446aSBoris Brezillon 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd,
58593db446aSBoris Brezillon };
58693db446aSBoris Brezillon
sunxi_nfc_randomizer_step(u16 state,int count)58793db446aSBoris Brezillon static u16 sunxi_nfc_randomizer_step(u16 state, int count)
58893db446aSBoris Brezillon {
58993db446aSBoris Brezillon state &= 0x7fff;
59093db446aSBoris Brezillon
59193db446aSBoris Brezillon /*
59293db446aSBoris Brezillon * This loop is just a simple implementation of a Fibonacci LFSR using
59393db446aSBoris Brezillon * the x16 + x15 + 1 polynomial.
59493db446aSBoris Brezillon */
59593db446aSBoris Brezillon while (count--)
59693db446aSBoris Brezillon state = ((state >> 1) |
59793db446aSBoris Brezillon (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff;
59893db446aSBoris Brezillon
59993db446aSBoris Brezillon return state;
60093db446aSBoris Brezillon }
60193db446aSBoris Brezillon
sunxi_nfc_randomizer_state(struct nand_chip * nand,int page,bool ecc)602cde567e3SBoris Brezillon static u16 sunxi_nfc_randomizer_state(struct nand_chip *nand, int page,
603cde567e3SBoris Brezillon bool ecc)
60493db446aSBoris Brezillon {
605cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
60693db446aSBoris Brezillon const u16 *seeds = sunxi_nfc_randomizer_page_seeds;
60793db446aSBoris Brezillon int mod = mtd_div_by_ws(mtd->erasesize, mtd);
60893db446aSBoris Brezillon
60993db446aSBoris Brezillon if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds))
61093db446aSBoris Brezillon mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds);
61193db446aSBoris Brezillon
61293db446aSBoris Brezillon if (ecc) {
61393db446aSBoris Brezillon if (mtd->ecc_step_size == 512)
61493db446aSBoris Brezillon seeds = sunxi_nfc_randomizer_ecc512_seeds;
61593db446aSBoris Brezillon else
61693db446aSBoris Brezillon seeds = sunxi_nfc_randomizer_ecc1024_seeds;
61793db446aSBoris Brezillon }
61893db446aSBoris Brezillon
61993db446aSBoris Brezillon return seeds[page % mod];
62093db446aSBoris Brezillon }
62193db446aSBoris Brezillon
sunxi_nfc_randomizer_config(struct nand_chip * nand,int page,bool ecc)622cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page,
623cde567e3SBoris Brezillon bool ecc)
62493db446aSBoris Brezillon {
62593db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
62693db446aSBoris Brezillon u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
62793db446aSBoris Brezillon u16 state;
62893db446aSBoris Brezillon
62993db446aSBoris Brezillon if (!(nand->options & NAND_NEED_SCRAMBLING))
63093db446aSBoris Brezillon return;
63193db446aSBoris Brezillon
63293db446aSBoris Brezillon ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
633cde567e3SBoris Brezillon state = sunxi_nfc_randomizer_state(nand, page, ecc);
63493db446aSBoris Brezillon ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
63593db446aSBoris Brezillon writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
63693db446aSBoris Brezillon }
63793db446aSBoris Brezillon
sunxi_nfc_randomizer_enable(struct nand_chip * nand)638cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_enable(struct nand_chip *nand)
63993db446aSBoris Brezillon {
64093db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
64193db446aSBoris Brezillon
64293db446aSBoris Brezillon if (!(nand->options & NAND_NEED_SCRAMBLING))
64393db446aSBoris Brezillon return;
64493db446aSBoris Brezillon
64593db446aSBoris Brezillon writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
64693db446aSBoris Brezillon nfc->regs + NFC_REG_ECC_CTL);
64793db446aSBoris Brezillon }
64893db446aSBoris Brezillon
sunxi_nfc_randomizer_disable(struct nand_chip * nand)649cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_disable(struct nand_chip *nand)
65093db446aSBoris Brezillon {
65193db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
65293db446aSBoris Brezillon
65393db446aSBoris Brezillon if (!(nand->options & NAND_NEED_SCRAMBLING))
65493db446aSBoris Brezillon return;
65593db446aSBoris Brezillon
65693db446aSBoris Brezillon writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
65793db446aSBoris Brezillon nfc->regs + NFC_REG_ECC_CTL);
65893db446aSBoris Brezillon }
65993db446aSBoris Brezillon
sunxi_nfc_randomize_bbm(struct nand_chip * nand,int page,u8 * bbm)660cde567e3SBoris Brezillon static void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm)
66193db446aSBoris Brezillon {
662cde567e3SBoris Brezillon u16 state = sunxi_nfc_randomizer_state(nand, page, true);
66393db446aSBoris Brezillon
66493db446aSBoris Brezillon bbm[0] ^= state;
66593db446aSBoris Brezillon bbm[1] ^= sunxi_nfc_randomizer_step(state, 8);
66693db446aSBoris Brezillon }
66793db446aSBoris Brezillon
sunxi_nfc_randomizer_write_buf(struct nand_chip * nand,const uint8_t * buf,int len,bool ecc,int page)668cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand,
66993db446aSBoris Brezillon const uint8_t *buf, int len,
67093db446aSBoris Brezillon bool ecc, int page)
67193db446aSBoris Brezillon {
672cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, ecc);
673cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand);
674cde567e3SBoris Brezillon sunxi_nfc_write_buf(nand, buf, len);
675cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand);
67693db446aSBoris Brezillon }
67793db446aSBoris Brezillon
sunxi_nfc_randomizer_read_buf(struct nand_chip * nand,uint8_t * buf,int len,bool ecc,int page)678cde567e3SBoris Brezillon static void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf,
67993db446aSBoris Brezillon int len, bool ecc, int page)
68093db446aSBoris Brezillon {
681cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, ecc);
682cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand);
683cde567e3SBoris Brezillon sunxi_nfc_read_buf(nand, buf, len);
684cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand);
68593db446aSBoris Brezillon }
68693db446aSBoris Brezillon
sunxi_nfc_hw_ecc_enable(struct nand_chip * nand)687cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand)
68893db446aSBoris Brezillon {
689cbd87780SMiquel Raynal struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
69093db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
69193db446aSBoris Brezillon
692ef3e6327SSamuel Holland writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL);
69393db446aSBoris Brezillon }
69493db446aSBoris Brezillon
sunxi_nfc_hw_ecc_disable(struct nand_chip * nand)695cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand)
69693db446aSBoris Brezillon {
69793db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
69893db446aSBoris Brezillon
699ef3e6327SSamuel Holland writel(0, nfc->regs + NFC_REG_ECC_CTL);
70093db446aSBoris Brezillon }
70193db446aSBoris Brezillon
sunxi_nfc_user_data_to_buf(u32 user_data,u8 * buf)70293db446aSBoris Brezillon static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf)
70393db446aSBoris Brezillon {
70493db446aSBoris Brezillon buf[0] = user_data;
70593db446aSBoris Brezillon buf[1] = user_data >> 8;
70693db446aSBoris Brezillon buf[2] = user_data >> 16;
70793db446aSBoris Brezillon buf[3] = user_data >> 24;
70893db446aSBoris Brezillon }
70993db446aSBoris Brezillon
sunxi_nfc_buf_to_user_data(const u8 * buf)71093db446aSBoris Brezillon static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf)
71193db446aSBoris Brezillon {
71293db446aSBoris Brezillon return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
71393db446aSBoris Brezillon }
71493db446aSBoris Brezillon
sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip * nand,u8 * oob,int step,bool bbm,int page)715cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
71693db446aSBoris Brezillon int step, bool bbm, int page)
71793db446aSBoris Brezillon {
71893db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
71993db446aSBoris Brezillon
72093db446aSBoris Brezillon sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
72193db446aSBoris Brezillon oob);
72293db446aSBoris Brezillon
72393db446aSBoris Brezillon /* De-randomize the Bad Block Marker. */
72493db446aSBoris Brezillon if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
725cde567e3SBoris Brezillon sunxi_nfc_randomize_bbm(nand, page, oob);
72693db446aSBoris Brezillon }
72793db446aSBoris Brezillon
sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip * nand,const u8 * oob,int step,bool bbm,int page)728cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
72993db446aSBoris Brezillon const u8 *oob, int step,
73093db446aSBoris Brezillon bool bbm, int page)
73193db446aSBoris Brezillon {
73293db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
73393db446aSBoris Brezillon u8 user_data[4];
73493db446aSBoris Brezillon
73593db446aSBoris Brezillon /* Randomize the Bad Block Marker. */
73693db446aSBoris Brezillon if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
73793db446aSBoris Brezillon memcpy(user_data, oob, sizeof(user_data));
738cde567e3SBoris Brezillon sunxi_nfc_randomize_bbm(nand, page, user_data);
73993db446aSBoris Brezillon oob = user_data;
74093db446aSBoris Brezillon }
74193db446aSBoris Brezillon
74293db446aSBoris Brezillon writel(sunxi_nfc_buf_to_user_data(oob),
74393db446aSBoris Brezillon nfc->regs + NFC_REG_USER_DATA(step));
74493db446aSBoris Brezillon }
74593db446aSBoris Brezillon
sunxi_nfc_hw_ecc_update_stats(struct nand_chip * nand,unsigned int * max_bitflips,int ret)746cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
74793db446aSBoris Brezillon unsigned int *max_bitflips, int ret)
74893db446aSBoris Brezillon {
749cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
750cde567e3SBoris Brezillon
75193db446aSBoris Brezillon if (ret < 0) {
75293db446aSBoris Brezillon mtd->ecc_stats.failed++;
75393db446aSBoris Brezillon } else {
75493db446aSBoris Brezillon mtd->ecc_stats.corrected += ret;
75593db446aSBoris Brezillon *max_bitflips = max_t(unsigned int, *max_bitflips, ret);
75693db446aSBoris Brezillon }
75793db446aSBoris Brezillon }
75893db446aSBoris Brezillon
sunxi_nfc_hw_ecc_correct(struct nand_chip * nand,u8 * data,u8 * oob,int step,u32 status,bool * erased)759cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
76093db446aSBoris Brezillon int step, u32 status, bool *erased)
76193db446aSBoris Brezillon {
76293db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
76393db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
76493db446aSBoris Brezillon u32 tmp;
76593db446aSBoris Brezillon
76693db446aSBoris Brezillon *erased = false;
76793db446aSBoris Brezillon
76893db446aSBoris Brezillon if (status & NFC_ECC_ERR(step))
76993db446aSBoris Brezillon return -EBADMSG;
77093db446aSBoris Brezillon
77193db446aSBoris Brezillon if (status & NFC_ECC_PAT_FOUND(step)) {
77293db446aSBoris Brezillon u8 pattern;
77393db446aSBoris Brezillon
77493db446aSBoris Brezillon if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
77593db446aSBoris Brezillon pattern = 0x0;
77693db446aSBoris Brezillon } else {
77793db446aSBoris Brezillon pattern = 0xff;
77893db446aSBoris Brezillon *erased = true;
77993db446aSBoris Brezillon }
78093db446aSBoris Brezillon
78193db446aSBoris Brezillon if (data)
78293db446aSBoris Brezillon memset(data, pattern, ecc->size);
78393db446aSBoris Brezillon
78493db446aSBoris Brezillon if (oob)
78593db446aSBoris Brezillon memset(oob, pattern, ecc->bytes + 4);
78693db446aSBoris Brezillon
78793db446aSBoris Brezillon return 0;
78893db446aSBoris Brezillon }
78993db446aSBoris Brezillon
79093db446aSBoris Brezillon tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
79193db446aSBoris Brezillon
79293db446aSBoris Brezillon return NFC_ECC_ERR_CNT(step, tmp);
79393db446aSBoris Brezillon }
79493db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_chunk(struct nand_chip * nand,u8 * data,int data_off,u8 * oob,int oob_off,int * cur_off,unsigned int * max_bitflips,bool bbm,bool oob_required,int page)795cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
79693db446aSBoris Brezillon u8 *data, int data_off,
79793db446aSBoris Brezillon u8 *oob, int oob_off,
79893db446aSBoris Brezillon int *cur_off,
79993db446aSBoris Brezillon unsigned int *max_bitflips,
80093db446aSBoris Brezillon bool bbm, bool oob_required, int page)
80193db446aSBoris Brezillon {
80293db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
80393db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
80493db446aSBoris Brezillon int raw_mode = 0;
80593db446aSBoris Brezillon bool erased;
80693db446aSBoris Brezillon int ret;
80793db446aSBoris Brezillon
80893db446aSBoris Brezillon if (*cur_off != data_off)
80993db446aSBoris Brezillon nand_change_read_column_op(nand, data_off, NULL, 0, false);
81093db446aSBoris Brezillon
811cde567e3SBoris Brezillon sunxi_nfc_randomizer_read_buf(nand, NULL, ecc->size, false, page);
81293db446aSBoris Brezillon
81393db446aSBoris Brezillon if (data_off + ecc->size != oob_off)
81493db446aSBoris Brezillon nand_change_read_column_op(nand, oob_off, NULL, 0, false);
81593db446aSBoris Brezillon
81693db446aSBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
81793db446aSBoris Brezillon if (ret)
81893db446aSBoris Brezillon return ret;
81993db446aSBoris Brezillon
820cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand);
82193db446aSBoris Brezillon writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
82293db446aSBoris Brezillon nfc->regs + NFC_REG_CMD);
82393db446aSBoris Brezillon
82493db446aSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
825cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand);
82693db446aSBoris Brezillon if (ret)
82793db446aSBoris Brezillon return ret;
82893db446aSBoris Brezillon
82993db446aSBoris Brezillon *cur_off = oob_off + ecc->bytes + 4;
83093db446aSBoris Brezillon
831cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0,
83293db446aSBoris Brezillon readl(nfc->regs + NFC_REG_ECC_ST),
83393db446aSBoris Brezillon &erased);
83493db446aSBoris Brezillon if (erased)
83593db446aSBoris Brezillon return 1;
83693db446aSBoris Brezillon
83793db446aSBoris Brezillon if (ret < 0) {
83893db446aSBoris Brezillon /*
83993db446aSBoris Brezillon * Re-read the data with the randomizer disabled to identify
84093db446aSBoris Brezillon * bitflips in erased pages.
84193db446aSBoris Brezillon */
84293db446aSBoris Brezillon if (nand->options & NAND_NEED_SCRAMBLING)
84393db446aSBoris Brezillon nand_change_read_column_op(nand, data_off, data,
84493db446aSBoris Brezillon ecc->size, false);
84593db446aSBoris Brezillon else
84693db446aSBoris Brezillon memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
84793db446aSBoris Brezillon ecc->size);
84893db446aSBoris Brezillon
84993db446aSBoris Brezillon nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
85093db446aSBoris Brezillon false);
85193db446aSBoris Brezillon
85293db446aSBoris Brezillon ret = nand_check_erased_ecc_chunk(data, ecc->size,
85393db446aSBoris Brezillon oob, ecc->bytes + 4,
85493db446aSBoris Brezillon NULL, 0, ecc->strength);
85593db446aSBoris Brezillon if (ret >= 0)
85693db446aSBoris Brezillon raw_mode = 1;
85793db446aSBoris Brezillon } else {
85893db446aSBoris Brezillon memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size);
85993db446aSBoris Brezillon
86093db446aSBoris Brezillon if (oob_required) {
86193db446aSBoris Brezillon nand_change_read_column_op(nand, oob_off, NULL, 0,
86293db446aSBoris Brezillon false);
863cde567e3SBoris Brezillon sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4,
86493db446aSBoris Brezillon true, page);
86593db446aSBoris Brezillon
866cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0,
86793db446aSBoris Brezillon bbm, page);
86893db446aSBoris Brezillon }
86993db446aSBoris Brezillon }
87093db446aSBoris Brezillon
871cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_update_stats(nand, max_bitflips, ret);
87293db446aSBoris Brezillon
87393db446aSBoris Brezillon return raw_mode;
87493db446aSBoris Brezillon }
87593db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip * nand,u8 * oob,int * cur_off,bool randomize,int page)876cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand,
87793db446aSBoris Brezillon u8 *oob, int *cur_off,
87893db446aSBoris Brezillon bool randomize, int page)
87993db446aSBoris Brezillon {
880cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
88193db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
88293db446aSBoris Brezillon int offset = ((ecc->bytes + 4) * ecc->steps);
88393db446aSBoris Brezillon int len = mtd->oobsize - offset;
88493db446aSBoris Brezillon
88593db446aSBoris Brezillon if (len <= 0)
88693db446aSBoris Brezillon return;
88793db446aSBoris Brezillon
88893db446aSBoris Brezillon if (!cur_off || *cur_off != offset)
88993db446aSBoris Brezillon nand_change_read_column_op(nand, mtd->writesize, NULL, 0,
89093db446aSBoris Brezillon false);
89193db446aSBoris Brezillon
89293db446aSBoris Brezillon if (!randomize)
8937e534323SBoris Brezillon sunxi_nfc_read_buf(nand, oob + offset, len);
89493db446aSBoris Brezillon else
895cde567e3SBoris Brezillon sunxi_nfc_randomizer_read_buf(nand, oob + offset, len,
89693db446aSBoris Brezillon false, page);
89793db446aSBoris Brezillon
89893db446aSBoris Brezillon if (cur_off)
89993db446aSBoris Brezillon *cur_off = mtd->oobsize + mtd->writesize;
90093db446aSBoris Brezillon }
90193db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip * nand,uint8_t * buf,int oob_required,int page,int nchunks)902cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf,
90393db446aSBoris Brezillon int oob_required, int page,
90493db446aSBoris Brezillon int nchunks)
90593db446aSBoris Brezillon {
90693db446aSBoris Brezillon bool randomized = nand->options & NAND_NEED_SCRAMBLING;
90793db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
908cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
90993db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
91093db446aSBoris Brezillon unsigned int max_bitflips = 0;
91193db446aSBoris Brezillon int ret, i, raw_mode = 0;
91293db446aSBoris Brezillon struct scatterlist sg;
913910ef7a4SManuel Dipolt u32 status, wait;
91493db446aSBoris Brezillon
91593db446aSBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
91693db446aSBoris Brezillon if (ret)
91793db446aSBoris Brezillon return ret;
91893db446aSBoris Brezillon
919cde567e3SBoris Brezillon ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks,
92093db446aSBoris Brezillon DMA_FROM_DEVICE, &sg);
92193db446aSBoris Brezillon if (ret)
92293db446aSBoris Brezillon return ret;
92393db446aSBoris Brezillon
924cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand);
925cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, false);
926cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand);
92793db446aSBoris Brezillon
92893db446aSBoris Brezillon writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) |
92993db446aSBoris Brezillon NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET);
93093db446aSBoris Brezillon
931910ef7a4SManuel Dipolt wait = NFC_CMD_INT_FLAG;
932910ef7a4SManuel Dipolt
933910ef7a4SManuel Dipolt if (nfc->caps->has_mdma)
934910ef7a4SManuel Dipolt wait |= NFC_DMA_INT_FLAG;
935910ef7a4SManuel Dipolt else
93693db446aSBoris Brezillon dma_async_issue_pending(nfc->dmac);
93793db446aSBoris Brezillon
93893db446aSBoris Brezillon writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
93993db446aSBoris Brezillon nfc->regs + NFC_REG_CMD);
94093db446aSBoris Brezillon
941910ef7a4SManuel Dipolt ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
942910ef7a4SManuel Dipolt if (ret && !nfc->caps->has_mdma)
94393db446aSBoris Brezillon dmaengine_terminate_all(nfc->dmac);
94493db446aSBoris Brezillon
945cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand);
946cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand);
94793db446aSBoris Brezillon
948cde567e3SBoris Brezillon sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg);
94993db446aSBoris Brezillon
95093db446aSBoris Brezillon if (ret)
95193db446aSBoris Brezillon return ret;
95293db446aSBoris Brezillon
95393db446aSBoris Brezillon status = readl(nfc->regs + NFC_REG_ECC_ST);
95493db446aSBoris Brezillon
95593db446aSBoris Brezillon for (i = 0; i < nchunks; i++) {
95693db446aSBoris Brezillon int data_off = i * ecc->size;
95793db446aSBoris Brezillon int oob_off = i * (ecc->bytes + 4);
95893db446aSBoris Brezillon u8 *data = buf + data_off;
95993db446aSBoris Brezillon u8 *oob = nand->oob_poi + oob_off;
96093db446aSBoris Brezillon bool erased;
96193db446aSBoris Brezillon
962cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL,
96393db446aSBoris Brezillon oob_required ? oob : NULL,
96493db446aSBoris Brezillon i, status, &erased);
96593db446aSBoris Brezillon
96693db446aSBoris Brezillon /* ECC errors are handled in the second loop. */
96793db446aSBoris Brezillon if (ret < 0)
96893db446aSBoris Brezillon continue;
96993db446aSBoris Brezillon
97093db446aSBoris Brezillon if (oob_required && !erased) {
97193db446aSBoris Brezillon /* TODO: use DMA to retrieve OOB */
97293db446aSBoris Brezillon nand_change_read_column_op(nand,
97393db446aSBoris Brezillon mtd->writesize + oob_off,
97493db446aSBoris Brezillon oob, ecc->bytes + 4, false);
97593db446aSBoris Brezillon
976cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i,
97793db446aSBoris Brezillon !i, page);
97893db446aSBoris Brezillon }
97993db446aSBoris Brezillon
98093db446aSBoris Brezillon if (erased)
98193db446aSBoris Brezillon raw_mode = 1;
98293db446aSBoris Brezillon
983cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
98493db446aSBoris Brezillon }
98593db446aSBoris Brezillon
98693db446aSBoris Brezillon if (status & NFC_ECC_ERR_MSK) {
98793db446aSBoris Brezillon for (i = 0; i < nchunks; i++) {
98893db446aSBoris Brezillon int data_off = i * ecc->size;
98993db446aSBoris Brezillon int oob_off = i * (ecc->bytes + 4);
99093db446aSBoris Brezillon u8 *data = buf + data_off;
99193db446aSBoris Brezillon u8 *oob = nand->oob_poi + oob_off;
99293db446aSBoris Brezillon
99393db446aSBoris Brezillon if (!(status & NFC_ECC_ERR(i)))
99493db446aSBoris Brezillon continue;
99593db446aSBoris Brezillon
99693db446aSBoris Brezillon /*
99793db446aSBoris Brezillon * Re-read the data with the randomizer disabled to
99893db446aSBoris Brezillon * identify bitflips in erased pages.
99993db446aSBoris Brezillon * TODO: use DMA to read page in raw mode
100093db446aSBoris Brezillon */
100193db446aSBoris Brezillon if (randomized)
100293db446aSBoris Brezillon nand_change_read_column_op(nand, data_off,
100393db446aSBoris Brezillon data, ecc->size,
100493db446aSBoris Brezillon false);
100593db446aSBoris Brezillon
100693db446aSBoris Brezillon /* TODO: use DMA to retrieve OOB */
100793db446aSBoris Brezillon nand_change_read_column_op(nand,
100893db446aSBoris Brezillon mtd->writesize + oob_off,
100993db446aSBoris Brezillon oob, ecc->bytes + 4, false);
101093db446aSBoris Brezillon
101193db446aSBoris Brezillon ret = nand_check_erased_ecc_chunk(data, ecc->size,
101293db446aSBoris Brezillon oob, ecc->bytes + 4,
101393db446aSBoris Brezillon NULL, 0,
101493db446aSBoris Brezillon ecc->strength);
101593db446aSBoris Brezillon if (ret >= 0)
101693db446aSBoris Brezillon raw_mode = 1;
101793db446aSBoris Brezillon
1018cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
101993db446aSBoris Brezillon }
102093db446aSBoris Brezillon }
102193db446aSBoris Brezillon
102293db446aSBoris Brezillon if (oob_required)
1023cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi,
102493db446aSBoris Brezillon NULL, !raw_mode,
102593db446aSBoris Brezillon page);
102693db446aSBoris Brezillon
102793db446aSBoris Brezillon return max_bitflips;
102893db446aSBoris Brezillon }
102993db446aSBoris Brezillon
sunxi_nfc_hw_ecc_write_chunk(struct nand_chip * nand,const u8 * data,int data_off,const u8 * oob,int oob_off,int * cur_off,bool bbm,int page)1030cde567e3SBoris Brezillon static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
103193db446aSBoris Brezillon const u8 *data, int data_off,
103293db446aSBoris Brezillon const u8 *oob, int oob_off,
103393db446aSBoris Brezillon int *cur_off, bool bbm,
103493db446aSBoris Brezillon int page)
103593db446aSBoris Brezillon {
103693db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
103793db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
103893db446aSBoris Brezillon int ret;
103993db446aSBoris Brezillon
104093db446aSBoris Brezillon if (data_off != *cur_off)
104193db446aSBoris Brezillon nand_change_write_column_op(nand, data_off, NULL, 0, false);
104293db446aSBoris Brezillon
1043cde567e3SBoris Brezillon sunxi_nfc_randomizer_write_buf(nand, data, ecc->size, false, page);
104493db446aSBoris Brezillon
104593db446aSBoris Brezillon if (data_off + ecc->size != oob_off)
104693db446aSBoris Brezillon nand_change_write_column_op(nand, oob_off, NULL, 0, false);
104793db446aSBoris Brezillon
104893db446aSBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
104993db446aSBoris Brezillon if (ret)
105093db446aSBoris Brezillon return ret;
105193db446aSBoris Brezillon
1052cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand);
1053cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
105493db446aSBoris Brezillon
105593db446aSBoris Brezillon writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
105693db446aSBoris Brezillon NFC_ACCESS_DIR | NFC_ECC_OP,
105793db446aSBoris Brezillon nfc->regs + NFC_REG_CMD);
105893db446aSBoris Brezillon
105993db446aSBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
1060cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand);
106193db446aSBoris Brezillon if (ret)
106293db446aSBoris Brezillon return ret;
106393db446aSBoris Brezillon
106493db446aSBoris Brezillon *cur_off = oob_off + ecc->bytes + 4;
106593db446aSBoris Brezillon
106693db446aSBoris Brezillon return 0;
106793db446aSBoris Brezillon }
106893db446aSBoris Brezillon
sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip * nand,u8 * oob,int * cur_off,int page)1069cde567e3SBoris Brezillon static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
107093db446aSBoris Brezillon u8 *oob, int *cur_off,
107193db446aSBoris Brezillon int page)
107293db446aSBoris Brezillon {
1073cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
107493db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
107593db446aSBoris Brezillon int offset = ((ecc->bytes + 4) * ecc->steps);
107693db446aSBoris Brezillon int len = mtd->oobsize - offset;
107793db446aSBoris Brezillon
107893db446aSBoris Brezillon if (len <= 0)
107993db446aSBoris Brezillon return;
108093db446aSBoris Brezillon
108193db446aSBoris Brezillon if (!cur_off || *cur_off != offset)
108293db446aSBoris Brezillon nand_change_write_column_op(nand, offset + mtd->writesize,
108393db446aSBoris Brezillon NULL, 0, false);
108493db446aSBoris Brezillon
1085cde567e3SBoris Brezillon sunxi_nfc_randomizer_write_buf(nand, oob + offset, len, false, page);
108693db446aSBoris Brezillon
108793db446aSBoris Brezillon if (cur_off)
108893db446aSBoris Brezillon *cur_off = mtd->oobsize + mtd->writesize;
108993db446aSBoris Brezillon }
109093db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_page(struct nand_chip * nand,uint8_t * buf,int oob_required,int page)1091a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
109293db446aSBoris Brezillon int oob_required, int page)
109393db446aSBoris Brezillon {
1094a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
1095a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
109693db446aSBoris Brezillon unsigned int max_bitflips = 0;
109793db446aSBoris Brezillon int ret, i, cur_off = 0;
109893db446aSBoris Brezillon bool raw_mode = false;
109993db446aSBoris Brezillon
1100df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1101df505799SBoris Brezillon
1102a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0);
110393db446aSBoris Brezillon
1104cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand);
110593db446aSBoris Brezillon
110693db446aSBoris Brezillon for (i = 0; i < ecc->steps; i++) {
110793db446aSBoris Brezillon int data_off = i * ecc->size;
110893db446aSBoris Brezillon int oob_off = i * (ecc->bytes + 4);
110993db446aSBoris Brezillon u8 *data = buf + data_off;
1110a55abb36SBoris Brezillon u8 *oob = nand->oob_poi + oob_off;
111193db446aSBoris Brezillon
1112cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob,
111393db446aSBoris Brezillon oob_off + mtd->writesize,
111493db446aSBoris Brezillon &cur_off, &max_bitflips,
111593db446aSBoris Brezillon !i, oob_required, page);
111693db446aSBoris Brezillon if (ret < 0)
111793db446aSBoris Brezillon return ret;
111893db446aSBoris Brezillon else if (ret)
111993db446aSBoris Brezillon raw_mode = true;
112093db446aSBoris Brezillon }
112193db446aSBoris Brezillon
112293db446aSBoris Brezillon if (oob_required)
1123cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, &cur_off,
112493db446aSBoris Brezillon !raw_mode, page);
112593db446aSBoris Brezillon
1126cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand);
112793db446aSBoris Brezillon
112893db446aSBoris Brezillon return max_bitflips;
112993db446aSBoris Brezillon }
113093db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip * nand,u8 * buf,int oob_required,int page)1131a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf,
113293db446aSBoris Brezillon int oob_required, int page)
113393db446aSBoris Brezillon {
113493db446aSBoris Brezillon int ret;
113593db446aSBoris Brezillon
1136df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1137df505799SBoris Brezillon
1138a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0);
113993db446aSBoris Brezillon
1140cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page,
1141a55abb36SBoris Brezillon nand->ecc.steps);
114293db446aSBoris Brezillon if (ret >= 0)
114393db446aSBoris Brezillon return ret;
114493db446aSBoris Brezillon
114593db446aSBoris Brezillon /* Fallback to PIO mode */
1146a55abb36SBoris Brezillon return sunxi_nfc_hw_ecc_read_page(nand, buf, oob_required, page);
114793db446aSBoris Brezillon }
114893db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_subpage(struct nand_chip * nand,u32 data_offs,u32 readlen,u8 * bufpoi,int page)1149a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
115093db446aSBoris Brezillon u32 data_offs, u32 readlen,
115193db446aSBoris Brezillon u8 *bufpoi, int page)
115293db446aSBoris Brezillon {
1153a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
1154a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
115593db446aSBoris Brezillon int ret, i, cur_off = 0;
115693db446aSBoris Brezillon unsigned int max_bitflips = 0;
115793db446aSBoris Brezillon
1158df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1159df505799SBoris Brezillon
1160a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0);
116193db446aSBoris Brezillon
1162cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand);
116393db446aSBoris Brezillon
116493db446aSBoris Brezillon for (i = data_offs / ecc->size;
116593db446aSBoris Brezillon i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
116693db446aSBoris Brezillon int data_off = i * ecc->size;
116793db446aSBoris Brezillon int oob_off = i * (ecc->bytes + 4);
116893db446aSBoris Brezillon u8 *data = bufpoi + data_off;
1169a55abb36SBoris Brezillon u8 *oob = nand->oob_poi + oob_off;
117093db446aSBoris Brezillon
1171cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off,
117293db446aSBoris Brezillon oob,
117393db446aSBoris Brezillon oob_off + mtd->writesize,
117493db446aSBoris Brezillon &cur_off, &max_bitflips, !i,
117593db446aSBoris Brezillon false, page);
117693db446aSBoris Brezillon if (ret < 0)
117793db446aSBoris Brezillon return ret;
117893db446aSBoris Brezillon }
117993db446aSBoris Brezillon
1180cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand);
118193db446aSBoris Brezillon
118293db446aSBoris Brezillon return max_bitflips;
118393db446aSBoris Brezillon }
118493db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip * nand,u32 data_offs,u32 readlen,u8 * buf,int page)1185a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand,
118693db446aSBoris Brezillon u32 data_offs, u32 readlen,
118793db446aSBoris Brezillon u8 *buf, int page)
118893db446aSBoris Brezillon {
1189a55abb36SBoris Brezillon int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size);
119093db446aSBoris Brezillon int ret;
119193db446aSBoris Brezillon
1192df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1193df505799SBoris Brezillon
1194a55abb36SBoris Brezillon nand_read_page_op(nand, page, 0, NULL, 0);
119593db446aSBoris Brezillon
1196cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks);
119793db446aSBoris Brezillon if (ret >= 0)
119893db446aSBoris Brezillon return ret;
119993db446aSBoris Brezillon
120093db446aSBoris Brezillon /* Fallback to PIO mode */
1201a55abb36SBoris Brezillon return sunxi_nfc_hw_ecc_read_subpage(nand, data_offs, readlen,
120293db446aSBoris Brezillon buf, page);
120393db446aSBoris Brezillon }
120493db446aSBoris Brezillon
sunxi_nfc_hw_ecc_write_page(struct nand_chip * nand,const uint8_t * buf,int oob_required,int page)1205a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
120693db446aSBoris Brezillon const uint8_t *buf, int oob_required,
120793db446aSBoris Brezillon int page)
120893db446aSBoris Brezillon {
1209a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
1210a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
121193db446aSBoris Brezillon int ret, i, cur_off = 0;
121293db446aSBoris Brezillon
1213df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1214df505799SBoris Brezillon
1215a55abb36SBoris Brezillon nand_prog_page_begin_op(nand, page, 0, NULL, 0);
121693db446aSBoris Brezillon
1217cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand);
121893db446aSBoris Brezillon
121993db446aSBoris Brezillon for (i = 0; i < ecc->steps; i++) {
122093db446aSBoris Brezillon int data_off = i * ecc->size;
122193db446aSBoris Brezillon int oob_off = i * (ecc->bytes + 4);
122293db446aSBoris Brezillon const u8 *data = buf + data_off;
1223a55abb36SBoris Brezillon const u8 *oob = nand->oob_poi + oob_off;
122493db446aSBoris Brezillon
1225cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
122693db446aSBoris Brezillon oob_off + mtd->writesize,
122793db446aSBoris Brezillon &cur_off, !i, page);
122893db446aSBoris Brezillon if (ret)
122993db446aSBoris Brezillon return ret;
123093db446aSBoris Brezillon }
123193db446aSBoris Brezillon
1232a55abb36SBoris Brezillon if (oob_required || (nand->options & NAND_NEED_SCRAMBLING))
1233cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi,
123493db446aSBoris Brezillon &cur_off, page);
123593db446aSBoris Brezillon
1236cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand);
123793db446aSBoris Brezillon
1238a55abb36SBoris Brezillon return nand_prog_page_end_op(nand);
123993db446aSBoris Brezillon }
124093db446aSBoris Brezillon
sunxi_nfc_hw_ecc_write_subpage(struct nand_chip * nand,u32 data_offs,u32 data_len,const u8 * buf,int oob_required,int page)1241a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
124293db446aSBoris Brezillon u32 data_offs, u32 data_len,
124393db446aSBoris Brezillon const u8 *buf, int oob_required,
124493db446aSBoris Brezillon int page)
124593db446aSBoris Brezillon {
1246a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
1247a55abb36SBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
124893db446aSBoris Brezillon int ret, i, cur_off = 0;
124993db446aSBoris Brezillon
1250df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1251df505799SBoris Brezillon
1252a55abb36SBoris Brezillon nand_prog_page_begin_op(nand, page, 0, NULL, 0);
125393db446aSBoris Brezillon
1254cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand);
125593db446aSBoris Brezillon
125693db446aSBoris Brezillon for (i = data_offs / ecc->size;
125793db446aSBoris Brezillon i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
125893db446aSBoris Brezillon int data_off = i * ecc->size;
125993db446aSBoris Brezillon int oob_off = i * (ecc->bytes + 4);
126093db446aSBoris Brezillon const u8 *data = buf + data_off;
1261a55abb36SBoris Brezillon const u8 *oob = nand->oob_poi + oob_off;
126293db446aSBoris Brezillon
1263cde567e3SBoris Brezillon ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob,
126493db446aSBoris Brezillon oob_off + mtd->writesize,
126593db446aSBoris Brezillon &cur_off, !i, page);
126693db446aSBoris Brezillon if (ret)
126793db446aSBoris Brezillon return ret;
126893db446aSBoris Brezillon }
126993db446aSBoris Brezillon
1270cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand);
127193db446aSBoris Brezillon
1272a55abb36SBoris Brezillon return nand_prog_page_end_op(nand);
127393db446aSBoris Brezillon }
127493db446aSBoris Brezillon
sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip * nand,const u8 * buf,int oob_required,int page)1275a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
127693db446aSBoris Brezillon const u8 *buf,
127793db446aSBoris Brezillon int oob_required,
127893db446aSBoris Brezillon int page)
127993db446aSBoris Brezillon {
128093db446aSBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
128193db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
128293db446aSBoris Brezillon struct scatterlist sg;
1283910ef7a4SManuel Dipolt u32 wait;
128493db446aSBoris Brezillon int ret, i;
128593db446aSBoris Brezillon
1286df505799SBoris Brezillon sunxi_nfc_select_chip(nand, nand->cur_cs);
1287df505799SBoris Brezillon
128893db446aSBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
128993db446aSBoris Brezillon if (ret)
129093db446aSBoris Brezillon return ret;
129193db446aSBoris Brezillon
1292cde567e3SBoris Brezillon ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps,
129393db446aSBoris Brezillon DMA_TO_DEVICE, &sg);
129493db446aSBoris Brezillon if (ret)
129593db446aSBoris Brezillon goto pio_fallback;
129693db446aSBoris Brezillon
129793db446aSBoris Brezillon for (i = 0; i < ecc->steps; i++) {
129893db446aSBoris Brezillon const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
129993db446aSBoris Brezillon
1300cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
130193db446aSBoris Brezillon }
130293db446aSBoris Brezillon
1303a55abb36SBoris Brezillon nand_prog_page_begin_op(nand, page, 0, NULL, 0);
130493db446aSBoris Brezillon
1305cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_enable(nand);
1306cde567e3SBoris Brezillon sunxi_nfc_randomizer_config(nand, page, false);
1307cde567e3SBoris Brezillon sunxi_nfc_randomizer_enable(nand);
130893db446aSBoris Brezillon
130993db446aSBoris Brezillon writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
131073277443SBoris Brezillon nfc->regs + NFC_REG_WCMD_SET);
131193db446aSBoris Brezillon
1312910ef7a4SManuel Dipolt wait = NFC_CMD_INT_FLAG;
1313910ef7a4SManuel Dipolt
1314910ef7a4SManuel Dipolt if (nfc->caps->has_mdma)
1315910ef7a4SManuel Dipolt wait |= NFC_DMA_INT_FLAG;
1316910ef7a4SManuel Dipolt else
131793db446aSBoris Brezillon dma_async_issue_pending(nfc->dmac);
131893db446aSBoris Brezillon
131993db446aSBoris Brezillon writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD |
132093db446aSBoris Brezillon NFC_DATA_TRANS | NFC_ACCESS_DIR,
132193db446aSBoris Brezillon nfc->regs + NFC_REG_CMD);
132293db446aSBoris Brezillon
1323910ef7a4SManuel Dipolt ret = sunxi_nfc_wait_events(nfc, wait, false, 0);
1324910ef7a4SManuel Dipolt if (ret && !nfc->caps->has_mdma)
132593db446aSBoris Brezillon dmaengine_terminate_all(nfc->dmac);
132693db446aSBoris Brezillon
1327cde567e3SBoris Brezillon sunxi_nfc_randomizer_disable(nand);
1328cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_disable(nand);
132993db446aSBoris Brezillon
1330cde567e3SBoris Brezillon sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg);
133193db446aSBoris Brezillon
133293db446aSBoris Brezillon if (ret)
133393db446aSBoris Brezillon return ret;
133493db446aSBoris Brezillon
1335a55abb36SBoris Brezillon if (oob_required || (nand->options & NAND_NEED_SCRAMBLING))
133693db446aSBoris Brezillon /* TODO: use DMA to transfer extra OOB bytes ? */
1337cde567e3SBoris Brezillon sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi,
133893db446aSBoris Brezillon NULL, page);
133993db446aSBoris Brezillon
1340a55abb36SBoris Brezillon return nand_prog_page_end_op(nand);
134193db446aSBoris Brezillon
134293db446aSBoris Brezillon pio_fallback:
1343a55abb36SBoris Brezillon return sunxi_nfc_hw_ecc_write_page(nand, buf, oob_required, page);
134493db446aSBoris Brezillon }
134593db446aSBoris Brezillon
sunxi_nfc_hw_ecc_read_oob(struct nand_chip * nand,int page)1346a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page)
134793db446aSBoris Brezillon {
1348eeab7174SBoris Brezillon u8 *buf = nand_get_data_buf(nand);
134993db446aSBoris Brezillon
1350eeab7174SBoris Brezillon return nand->ecc.read_page(nand, buf, 1, page);
135193db446aSBoris Brezillon }
135293db446aSBoris Brezillon
sunxi_nfc_hw_ecc_write_oob(struct nand_chip * nand,int page)1353a55abb36SBoris Brezillon static int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page)
135493db446aSBoris Brezillon {
1355a55abb36SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
1356eeab7174SBoris Brezillon u8 *buf = nand_get_data_buf(nand);
135793db446aSBoris Brezillon int ret;
135893db446aSBoris Brezillon
1359eeab7174SBoris Brezillon memset(buf, 0xff, mtd->writesize);
1360eeab7174SBoris Brezillon ret = nand->ecc.write_page(nand, buf, 1, page);
136193db446aSBoris Brezillon if (ret)
136293db446aSBoris Brezillon return ret;
136393db446aSBoris Brezillon
136493db446aSBoris Brezillon /* Send command to program the OOB data */
1365a55abb36SBoris Brezillon return nand_prog_page_end_op(nand);
136693db446aSBoris Brezillon }
136793db446aSBoris Brezillon
136893db446aSBoris Brezillon static const s32 tWB_lut[] = {6, 12, 16, 20};
136993db446aSBoris Brezillon static const s32 tRHW_lut[] = {4, 8, 12, 20};
137093db446aSBoris Brezillon
_sunxi_nand_lookup_timing(const s32 * lut,int lut_size,u32 duration,u32 clk_period)137193db446aSBoris Brezillon static int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration,
137293db446aSBoris Brezillon u32 clk_period)
137393db446aSBoris Brezillon {
137493db446aSBoris Brezillon u32 clk_cycles = DIV_ROUND_UP(duration, clk_period);
137593db446aSBoris Brezillon int i;
137693db446aSBoris Brezillon
137793db446aSBoris Brezillon for (i = 0; i < lut_size; i++) {
137893db446aSBoris Brezillon if (clk_cycles <= lut[i])
137993db446aSBoris Brezillon return i;
138093db446aSBoris Brezillon }
138193db446aSBoris Brezillon
138293db446aSBoris Brezillon /* Doesn't fit */
138393db446aSBoris Brezillon return -EINVAL;
138493db446aSBoris Brezillon }
138593db446aSBoris Brezillon
138693db446aSBoris Brezillon #define sunxi_nand_lookup_timing(l, p, c) \
138793db446aSBoris Brezillon _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c)
138893db446aSBoris Brezillon
sunxi_nfc_setup_interface(struct nand_chip * nand,int csline,const struct nand_interface_config * conf)13894c46667bSMiquel Raynal static int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline,
13904c46667bSMiquel Raynal const struct nand_interface_config *conf)
139193db446aSBoris Brezillon {
1392f385ebf0SBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1393f385ebf0SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
139493db446aSBoris Brezillon const struct nand_sdr_timings *timings;
139593db446aSBoris Brezillon u32 min_clk_period = 0;
139693db446aSBoris Brezillon s32 tWB, tADL, tWHR, tRHW, tCAD;
139793db446aSBoris Brezillon long real_clk_rate;
139893db446aSBoris Brezillon
139993db446aSBoris Brezillon timings = nand_get_sdr_timings(conf);
140093db446aSBoris Brezillon if (IS_ERR(timings))
140193db446aSBoris Brezillon return -ENOTSUPP;
140293db446aSBoris Brezillon
140393db446aSBoris Brezillon /* T1 <=> tCLS */
140493db446aSBoris Brezillon if (timings->tCLS_min > min_clk_period)
140593db446aSBoris Brezillon min_clk_period = timings->tCLS_min;
140693db446aSBoris Brezillon
140793db446aSBoris Brezillon /* T2 <=> tCLH */
140893db446aSBoris Brezillon if (timings->tCLH_min > min_clk_period)
140993db446aSBoris Brezillon min_clk_period = timings->tCLH_min;
141093db446aSBoris Brezillon
141193db446aSBoris Brezillon /* T3 <=> tCS */
141293db446aSBoris Brezillon if (timings->tCS_min > min_clk_period)
141393db446aSBoris Brezillon min_clk_period = timings->tCS_min;
141493db446aSBoris Brezillon
141593db446aSBoris Brezillon /* T4 <=> tCH */
141693db446aSBoris Brezillon if (timings->tCH_min > min_clk_period)
141793db446aSBoris Brezillon min_clk_period = timings->tCH_min;
141893db446aSBoris Brezillon
141993db446aSBoris Brezillon /* T5 <=> tWP */
142093db446aSBoris Brezillon if (timings->tWP_min > min_clk_period)
142193db446aSBoris Brezillon min_clk_period = timings->tWP_min;
142293db446aSBoris Brezillon
142393db446aSBoris Brezillon /* T6 <=> tWH */
142493db446aSBoris Brezillon if (timings->tWH_min > min_clk_period)
142593db446aSBoris Brezillon min_clk_period = timings->tWH_min;
142693db446aSBoris Brezillon
142793db446aSBoris Brezillon /* T7 <=> tALS */
142893db446aSBoris Brezillon if (timings->tALS_min > min_clk_period)
142993db446aSBoris Brezillon min_clk_period = timings->tALS_min;
143093db446aSBoris Brezillon
143193db446aSBoris Brezillon /* T8 <=> tDS */
143293db446aSBoris Brezillon if (timings->tDS_min > min_clk_period)
143393db446aSBoris Brezillon min_clk_period = timings->tDS_min;
143493db446aSBoris Brezillon
143593db446aSBoris Brezillon /* T9 <=> tDH */
143693db446aSBoris Brezillon if (timings->tDH_min > min_clk_period)
143793db446aSBoris Brezillon min_clk_period = timings->tDH_min;
143893db446aSBoris Brezillon
143993db446aSBoris Brezillon /* T10 <=> tRR */
144093db446aSBoris Brezillon if (timings->tRR_min > (min_clk_period * 3))
144193db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
144293db446aSBoris Brezillon
144393db446aSBoris Brezillon /* T11 <=> tALH */
144493db446aSBoris Brezillon if (timings->tALH_min > min_clk_period)
144593db446aSBoris Brezillon min_clk_period = timings->tALH_min;
144693db446aSBoris Brezillon
144793db446aSBoris Brezillon /* T12 <=> tRP */
144893db446aSBoris Brezillon if (timings->tRP_min > min_clk_period)
144993db446aSBoris Brezillon min_clk_period = timings->tRP_min;
145093db446aSBoris Brezillon
145193db446aSBoris Brezillon /* T13 <=> tREH */
145293db446aSBoris Brezillon if (timings->tREH_min > min_clk_period)
145393db446aSBoris Brezillon min_clk_period = timings->tREH_min;
145493db446aSBoris Brezillon
145593db446aSBoris Brezillon /* T14 <=> tRC */
145693db446aSBoris Brezillon if (timings->tRC_min > (min_clk_period * 2))
145793db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
145893db446aSBoris Brezillon
145993db446aSBoris Brezillon /* T15 <=> tWC */
146093db446aSBoris Brezillon if (timings->tWC_min > (min_clk_period * 2))
146193db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
146293db446aSBoris Brezillon
146393db446aSBoris Brezillon /* T16 - T19 + tCAD */
146493db446aSBoris Brezillon if (timings->tWB_max > (min_clk_period * 20))
146593db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20);
146693db446aSBoris Brezillon
146793db446aSBoris Brezillon if (timings->tADL_min > (min_clk_period * 32))
146893db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32);
146993db446aSBoris Brezillon
147093db446aSBoris Brezillon if (timings->tWHR_min > (min_clk_period * 32))
147193db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32);
147293db446aSBoris Brezillon
147393db446aSBoris Brezillon if (timings->tRHW_min > (min_clk_period * 20))
147493db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20);
147593db446aSBoris Brezillon
1476511d05e0SBoris Brezillon /*
1477511d05e0SBoris Brezillon * In non-EDO, tREA should be less than tRP to guarantee that the
1478511d05e0SBoris Brezillon * controller does not sample the IO lines too early. Unfortunately,
1479511d05e0SBoris Brezillon * the sunxi NAND controller does not allow us to have different
1480511d05e0SBoris Brezillon * values for tRP and tREH (tRP = tREH = tRW / 2).
1481511d05e0SBoris Brezillon *
1482511d05e0SBoris Brezillon * We have 2 options to overcome this limitation:
1483511d05e0SBoris Brezillon *
1484511d05e0SBoris Brezillon * 1/ Extend tRC to fulfil the tREA <= tRC / 2 constraint
1485511d05e0SBoris Brezillon * 2/ Use EDO mode (only works if timings->tRLOH > 0)
1486511d05e0SBoris Brezillon */
1487511d05e0SBoris Brezillon if (timings->tREA_max > min_clk_period && !timings->tRLOH_min)
1488511d05e0SBoris Brezillon min_clk_period = timings->tREA_max;
1489511d05e0SBoris Brezillon
149093db446aSBoris Brezillon tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max,
149193db446aSBoris Brezillon min_clk_period);
149293db446aSBoris Brezillon if (tWB < 0) {
149393db446aSBoris Brezillon dev_err(nfc->dev, "unsupported tWB\n");
149493db446aSBoris Brezillon return tWB;
149593db446aSBoris Brezillon }
149693db446aSBoris Brezillon
149793db446aSBoris Brezillon tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3;
149893db446aSBoris Brezillon if (tADL > 3) {
149993db446aSBoris Brezillon dev_err(nfc->dev, "unsupported tADL\n");
150093db446aSBoris Brezillon return -EINVAL;
150193db446aSBoris Brezillon }
150293db446aSBoris Brezillon
150393db446aSBoris Brezillon tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3;
150493db446aSBoris Brezillon if (tWHR > 3) {
150593db446aSBoris Brezillon dev_err(nfc->dev, "unsupported tWHR\n");
150693db446aSBoris Brezillon return -EINVAL;
150793db446aSBoris Brezillon }
150893db446aSBoris Brezillon
150993db446aSBoris Brezillon tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min,
151093db446aSBoris Brezillon min_clk_period);
151193db446aSBoris Brezillon if (tRHW < 0) {
151293db446aSBoris Brezillon dev_err(nfc->dev, "unsupported tRHW\n");
151393db446aSBoris Brezillon return tRHW;
151493db446aSBoris Brezillon }
151593db446aSBoris Brezillon
151693db446aSBoris Brezillon if (csline == NAND_DATA_IFACE_CHECK_ONLY)
151793db446aSBoris Brezillon return 0;
151893db446aSBoris Brezillon
151993db446aSBoris Brezillon /*
152093db446aSBoris Brezillon * TODO: according to ONFI specs this value only applies for DDR NAND,
152193db446aSBoris Brezillon * but Allwinner seems to set this to 0x7. Mimic them for now.
152293db446aSBoris Brezillon */
152393db446aSBoris Brezillon tCAD = 0x7;
152493db446aSBoris Brezillon
152593db446aSBoris Brezillon /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */
1526f385ebf0SBoris Brezillon sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD);
152793db446aSBoris Brezillon
152893db446aSBoris Brezillon /* Convert min_clk_period from picoseconds to nanoseconds */
152993db446aSBoris Brezillon min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
153093db446aSBoris Brezillon
153193db446aSBoris Brezillon /*
153293db446aSBoris Brezillon * Unlike what is stated in Allwinner datasheet, the clk_rate should
153393db446aSBoris Brezillon * be set to (1 / min_clk_period), and not (2 / min_clk_period).
153493db446aSBoris Brezillon * This new formula was verified with a scope and validated by
153593db446aSBoris Brezillon * Allwinner engineers.
153693db446aSBoris Brezillon */
1537f385ebf0SBoris Brezillon sunxi_nand->clk_rate = NSEC_PER_SEC / min_clk_period;
1538f385ebf0SBoris Brezillon real_clk_rate = clk_round_rate(nfc->mod_clk, sunxi_nand->clk_rate);
153993db446aSBoris Brezillon if (real_clk_rate <= 0) {
1540f385ebf0SBoris Brezillon dev_err(nfc->dev, "Unable to round clk %lu\n",
1541f385ebf0SBoris Brezillon sunxi_nand->clk_rate);
154293db446aSBoris Brezillon return -EINVAL;
154393db446aSBoris Brezillon }
154493db446aSBoris Brezillon
1545511d05e0SBoris Brezillon sunxi_nand->timing_ctl = 0;
1546511d05e0SBoris Brezillon
154793db446aSBoris Brezillon /*
154893db446aSBoris Brezillon * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data
154993db446aSBoris Brezillon * output cycle timings shall be used if the host drives tRC less than
1550511d05e0SBoris Brezillon * 30 ns. We should also use EDO mode if tREA is bigger than tRP.
155193db446aSBoris Brezillon */
155293db446aSBoris Brezillon min_clk_period = NSEC_PER_SEC / real_clk_rate;
1553511d05e0SBoris Brezillon if (min_clk_period * 2 < 30 || min_clk_period * 1000 < timings->tREA_max)
1554511d05e0SBoris Brezillon sunxi_nand->timing_ctl = NFC_TIMING_CTL_EDO;
155593db446aSBoris Brezillon
155693db446aSBoris Brezillon return 0;
155793db446aSBoris Brezillon }
155893db446aSBoris Brezillon
sunxi_nand_ooblayout_ecc(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)155993db446aSBoris Brezillon static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
156093db446aSBoris Brezillon struct mtd_oob_region *oobregion)
156193db446aSBoris Brezillon {
156293db446aSBoris Brezillon struct nand_chip *nand = mtd_to_nand(mtd);
156393db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
156493db446aSBoris Brezillon
156593db446aSBoris Brezillon if (section >= ecc->steps)
156693db446aSBoris Brezillon return -ERANGE;
156793db446aSBoris Brezillon
156893db446aSBoris Brezillon oobregion->offset = section * (ecc->bytes + 4) + 4;
156993db446aSBoris Brezillon oobregion->length = ecc->bytes;
157093db446aSBoris Brezillon
157193db446aSBoris Brezillon return 0;
157293db446aSBoris Brezillon }
157393db446aSBoris Brezillon
sunxi_nand_ooblayout_free(struct mtd_info * mtd,int section,struct mtd_oob_region * oobregion)157493db446aSBoris Brezillon static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
157593db446aSBoris Brezillon struct mtd_oob_region *oobregion)
157693db446aSBoris Brezillon {
157793db446aSBoris Brezillon struct nand_chip *nand = mtd_to_nand(mtd);
157893db446aSBoris Brezillon struct nand_ecc_ctrl *ecc = &nand->ecc;
157993db446aSBoris Brezillon
158093db446aSBoris Brezillon if (section > ecc->steps)
158193db446aSBoris Brezillon return -ERANGE;
158293db446aSBoris Brezillon
158393db446aSBoris Brezillon /*
158493db446aSBoris Brezillon * The first 2 bytes are used for BB markers, hence we
158593db446aSBoris Brezillon * only have 2 bytes available in the first user data
158693db446aSBoris Brezillon * section.
158793db446aSBoris Brezillon */
1588bace41f8SMiquel Raynal if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) {
158993db446aSBoris Brezillon oobregion->offset = 2;
159093db446aSBoris Brezillon oobregion->length = 2;
159193db446aSBoris Brezillon
159293db446aSBoris Brezillon return 0;
159393db446aSBoris Brezillon }
159493db446aSBoris Brezillon
15953998a461SSamuel Holland /*
15963998a461SSamuel Holland * The controller does not provide access to OOB bytes
15973998a461SSamuel Holland * past the end of the ECC data.
15983998a461SSamuel Holland */
15993998a461SSamuel Holland if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
16003998a461SSamuel Holland return -ERANGE;
16013998a461SSamuel Holland
160293db446aSBoris Brezillon oobregion->offset = section * (ecc->bytes + 4);
160393db446aSBoris Brezillon
160493db446aSBoris Brezillon if (section < ecc->steps)
160593db446aSBoris Brezillon oobregion->length = 4;
160693db446aSBoris Brezillon else
160734569d86SSamuel Holland oobregion->length = mtd->oobsize - oobregion->offset;
160893db446aSBoris Brezillon
160993db446aSBoris Brezillon return 0;
161093db446aSBoris Brezillon }
161193db446aSBoris Brezillon
161293db446aSBoris Brezillon static const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = {
161393db446aSBoris Brezillon .ecc = sunxi_nand_ooblayout_ecc,
161493db446aSBoris Brezillon .free = sunxi_nand_ooblayout_free,
161593db446aSBoris Brezillon };
161693db446aSBoris Brezillon
sunxi_nand_hw_ecc_ctrl_init(struct nand_chip * nand,struct nand_ecc_ctrl * ecc,struct device_node * np)1617cde567e3SBoris Brezillon static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
161893db446aSBoris Brezillon struct nand_ecc_ctrl *ecc,
161993db446aSBoris Brezillon struct device_node *np)
162093db446aSBoris Brezillon {
162193db446aSBoris Brezillon static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
1622cbd87780SMiquel Raynal struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1623cde567e3SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1624cde567e3SBoris Brezillon struct mtd_info *mtd = nand_to_mtd(nand);
1625b5156335SMiquel Raynal struct nand_device *nanddev = mtd_to_nanddev(mtd);
162693db446aSBoris Brezillon int nsectors;
162793db446aSBoris Brezillon int i;
162893db446aSBoris Brezillon
1629b5156335SMiquel Raynal if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
163093db446aSBoris Brezillon int bytes;
163193db446aSBoris Brezillon
163293db446aSBoris Brezillon ecc->size = 1024;
163393db446aSBoris Brezillon nsectors = mtd->writesize / ecc->size;
163493db446aSBoris Brezillon
163593db446aSBoris Brezillon /* Reserve 2 bytes for the BBM */
163693db446aSBoris Brezillon bytes = (mtd->oobsize - 2) / nsectors;
163793db446aSBoris Brezillon
163893db446aSBoris Brezillon /* 4 non-ECC bytes are added before each ECC bytes section */
163993db446aSBoris Brezillon bytes -= 4;
164093db446aSBoris Brezillon
164193db446aSBoris Brezillon /* and bytes has to be even. */
164293db446aSBoris Brezillon if (bytes % 2)
164393db446aSBoris Brezillon bytes--;
164493db446aSBoris Brezillon
164593db446aSBoris Brezillon ecc->strength = bytes * 8 / fls(8 * ecc->size);
164693db446aSBoris Brezillon
164793db446aSBoris Brezillon for (i = 0; i < ARRAY_SIZE(strengths); i++) {
164893db446aSBoris Brezillon if (strengths[i] > ecc->strength)
164993db446aSBoris Brezillon break;
165093db446aSBoris Brezillon }
165193db446aSBoris Brezillon
165293db446aSBoris Brezillon if (!i)
165393db446aSBoris Brezillon ecc->strength = 0;
165493db446aSBoris Brezillon else
165593db446aSBoris Brezillon ecc->strength = strengths[i - 1];
165693db446aSBoris Brezillon }
165793db446aSBoris Brezillon
165893db446aSBoris Brezillon if (ecc->size != 512 && ecc->size != 1024)
165993db446aSBoris Brezillon return -EINVAL;
166093db446aSBoris Brezillon
166193db446aSBoris Brezillon /* Prefer 1k ECC chunk over 512 ones */
166293db446aSBoris Brezillon if (ecc->size == 512 && mtd->writesize > 512) {
166393db446aSBoris Brezillon ecc->size = 1024;
166493db446aSBoris Brezillon ecc->strength *= 2;
166593db446aSBoris Brezillon }
166693db446aSBoris Brezillon
166793db446aSBoris Brezillon /* Add ECC info retrieval from DT */
166893db446aSBoris Brezillon for (i = 0; i < ARRAY_SIZE(strengths); i++) {
166993db446aSBoris Brezillon if (ecc->strength <= strengths[i]) {
167093db446aSBoris Brezillon /*
167193db446aSBoris Brezillon * Update ecc->strength value with the actual strength
167293db446aSBoris Brezillon * that will be used by the ECC engine.
167393db446aSBoris Brezillon */
167493db446aSBoris Brezillon ecc->strength = strengths[i];
167593db446aSBoris Brezillon break;
167693db446aSBoris Brezillon }
167793db446aSBoris Brezillon }
167893db446aSBoris Brezillon
167993db446aSBoris Brezillon if (i >= ARRAY_SIZE(strengths)) {
168093db446aSBoris Brezillon dev_err(nfc->dev, "unsupported strength\n");
1681ac1c7072SSamuel Holland return -ENOTSUPP;
168293db446aSBoris Brezillon }
168393db446aSBoris Brezillon
168493db446aSBoris Brezillon /* HW ECC always request ECC bytes for 1024 bytes blocks */
168593db446aSBoris Brezillon ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
168693db446aSBoris Brezillon
168793db446aSBoris Brezillon /* HW ECC always work with even numbers of ECC bytes */
168893db446aSBoris Brezillon ecc->bytes = ALIGN(ecc->bytes, 2);
168993db446aSBoris Brezillon
169093db446aSBoris Brezillon nsectors = mtd->writesize / ecc->size;
169193db446aSBoris Brezillon
1692ac1c7072SSamuel Holland if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
1693ac1c7072SSamuel Holland return -EINVAL;
169493db446aSBoris Brezillon
169515d6f118SBoris Brezillon ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
169615d6f118SBoris Brezillon ecc->write_oob = sunxi_nfc_hw_ecc_write_oob;
169793db446aSBoris Brezillon mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops);
169893db446aSBoris Brezillon
1699910ef7a4SManuel Dipolt if (nfc->dmac || nfc->caps->has_mdma) {
170093db446aSBoris Brezillon ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma;
170193db446aSBoris Brezillon ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma;
170293db446aSBoris Brezillon ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma;
1703ce8148d7SMiquel Raynal nand->options |= NAND_USES_DMA;
170493db446aSBoris Brezillon } else {
170593db446aSBoris Brezillon ecc->read_page = sunxi_nfc_hw_ecc_read_page;
170693db446aSBoris Brezillon ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage;
170793db446aSBoris Brezillon ecc->write_page = sunxi_nfc_hw_ecc_write_page;
170893db446aSBoris Brezillon }
170993db446aSBoris Brezillon
171093db446aSBoris Brezillon /* TODO: support DMA for raw accesses and subpage write */
171193db446aSBoris Brezillon ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage;
171293db446aSBoris Brezillon ecc->read_oob_raw = nand_read_oob_std;
171393db446aSBoris Brezillon ecc->write_oob_raw = nand_write_oob_std;
171493db446aSBoris Brezillon
1715ef3e6327SSamuel Holland sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION |
1716ef3e6327SSamuel Holland NFC_ECC_PIPELINE | NFC_ECC_EN;
1717ef3e6327SSamuel Holland
1718ef3e6327SSamuel Holland if (ecc->size == 512)
1719ef3e6327SSamuel Holland sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
1720ef3e6327SSamuel Holland
172193db446aSBoris Brezillon return 0;
172293db446aSBoris Brezillon }
172393db446aSBoris Brezillon
sunxi_nand_attach_chip(struct nand_chip * nand)17242a4d9c16SMiquel Raynal static int sunxi_nand_attach_chip(struct nand_chip *nand)
172593db446aSBoris Brezillon {
172653576c7bSMiquel Raynal const struct nand_ecc_props *requirements =
172753576c7bSMiquel Raynal nanddev_get_ecc_requirements(&nand->base);
17282a4d9c16SMiquel Raynal struct nand_ecc_ctrl *ecc = &nand->ecc;
17292a4d9c16SMiquel Raynal struct device_node *np = nand_get_flash_node(nand);
173093db446aSBoris Brezillon int ret;
173193db446aSBoris Brezillon
17322a4d9c16SMiquel Raynal if (nand->bbt_options & NAND_BBT_USE_FLASH)
17332a4d9c16SMiquel Raynal nand->bbt_options |= NAND_BBT_NO_OOB;
17342a4d9c16SMiquel Raynal
17352a4d9c16SMiquel Raynal if (nand->options & NAND_NEED_SCRAMBLING)
17362a4d9c16SMiquel Raynal nand->options |= NAND_NO_SUBPAGE_WRITE;
17372a4d9c16SMiquel Raynal
17382a4d9c16SMiquel Raynal nand->options |= NAND_SUBPAGE_READ;
17392a4d9c16SMiquel Raynal
174093db446aSBoris Brezillon if (!ecc->size) {
174153576c7bSMiquel Raynal ecc->size = requirements->step_size;
174253576c7bSMiquel Raynal ecc->strength = requirements->strength;
174393db446aSBoris Brezillon }
174493db446aSBoris Brezillon
174593db446aSBoris Brezillon if (!ecc->size || !ecc->strength)
174693db446aSBoris Brezillon return -EINVAL;
174793db446aSBoris Brezillon
1748bace41f8SMiquel Raynal switch (ecc->engine_type) {
1749bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_ON_HOST:
1750cde567e3SBoris Brezillon ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np);
175193db446aSBoris Brezillon if (ret)
175293db446aSBoris Brezillon return ret;
175393db446aSBoris Brezillon break;
1754bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_NONE:
1755bace41f8SMiquel Raynal case NAND_ECC_ENGINE_TYPE_SOFT:
175693db446aSBoris Brezillon break;
175793db446aSBoris Brezillon default:
175893db446aSBoris Brezillon return -EINVAL;
175993db446aSBoris Brezillon }
176093db446aSBoris Brezillon
176193db446aSBoris Brezillon return 0;
176293db446aSBoris Brezillon }
176393db446aSBoris Brezillon
sunxi_nfc_exec_subop(struct nand_chip * nand,const struct nand_subop * subop)1764df505799SBoris Brezillon static int sunxi_nfc_exec_subop(struct nand_chip *nand,
1765df505799SBoris Brezillon const struct nand_subop *subop)
1766df505799SBoris Brezillon {
1767df505799SBoris Brezillon struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
1768df505799SBoris Brezillon u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { };
1769df505799SBoris Brezillon unsigned int i, j, remaining, start;
1770df505799SBoris Brezillon void *inbuf = NULL;
1771df505799SBoris Brezillon int ret;
1772df505799SBoris Brezillon
1773df505799SBoris Brezillon for (i = 0; i < subop->ninstrs; i++) {
1774df505799SBoris Brezillon const struct nand_op_instr *instr = &subop->instrs[i];
1775df505799SBoris Brezillon
1776df505799SBoris Brezillon switch (instr->type) {
1777df505799SBoris Brezillon case NAND_OP_CMD_INSTR:
1778df505799SBoris Brezillon if (cmd & NFC_SEND_CMD1) {
1779df505799SBoris Brezillon if (WARN_ON(cmd & NFC_SEND_CMD2))
1780df505799SBoris Brezillon return -EINVAL;
1781df505799SBoris Brezillon
1782df505799SBoris Brezillon cmd |= NFC_SEND_CMD2;
1783df505799SBoris Brezillon extcmd |= instr->ctx.cmd.opcode;
1784df505799SBoris Brezillon } else {
1785df505799SBoris Brezillon cmd |= NFC_SEND_CMD1 |
1786df505799SBoris Brezillon NFC_CMD(instr->ctx.cmd.opcode);
1787df505799SBoris Brezillon }
1788df505799SBoris Brezillon break;
1789df505799SBoris Brezillon
1790df505799SBoris Brezillon case NAND_OP_ADDR_INSTR:
1791df505799SBoris Brezillon remaining = nand_subop_get_num_addr_cyc(subop, i);
1792df505799SBoris Brezillon start = nand_subop_get_addr_start_off(subop, i);
1793df505799SBoris Brezillon for (j = 0; j < 8 && j + start < remaining; j++) {
1794df505799SBoris Brezillon u32 addr = instr->ctx.addr.addrs[j + start];
1795df505799SBoris Brezillon
1796df505799SBoris Brezillon addrs[j / 4] |= addr << (j % 4) * 8;
1797df505799SBoris Brezillon }
1798df505799SBoris Brezillon
1799df505799SBoris Brezillon if (j)
1800df505799SBoris Brezillon cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j);
1801df505799SBoris Brezillon
1802df505799SBoris Brezillon break;
1803df505799SBoris Brezillon
1804df505799SBoris Brezillon case NAND_OP_DATA_IN_INSTR:
1805df505799SBoris Brezillon case NAND_OP_DATA_OUT_INSTR:
1806df505799SBoris Brezillon start = nand_subop_get_data_start_off(subop, i);
1807df505799SBoris Brezillon remaining = nand_subop_get_data_len(subop, i);
1808df505799SBoris Brezillon cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
1809df505799SBoris Brezillon cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
1810df505799SBoris Brezillon
1811df505799SBoris Brezillon if (instr->type == NAND_OP_DATA_OUT_INSTR) {
1812df505799SBoris Brezillon cmd |= NFC_ACCESS_DIR;
1813df505799SBoris Brezillon memcpy_toio(nfc->regs + NFC_RAM0_BASE,
1814df505799SBoris Brezillon instr->ctx.data.buf.out + start,
1815df505799SBoris Brezillon cnt);
1816df505799SBoris Brezillon } else {
1817df505799SBoris Brezillon inbuf = instr->ctx.data.buf.in + start;
1818df505799SBoris Brezillon }
1819df505799SBoris Brezillon
1820df505799SBoris Brezillon break;
1821df505799SBoris Brezillon
1822df505799SBoris Brezillon case NAND_OP_WAITRDY_INSTR:
1823df505799SBoris Brezillon cmd |= NFC_WAIT_FLAG;
1824df505799SBoris Brezillon break;
1825df505799SBoris Brezillon }
1826df505799SBoris Brezillon }
1827df505799SBoris Brezillon
1828df505799SBoris Brezillon ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
1829df505799SBoris Brezillon if (ret)
1830df505799SBoris Brezillon return ret;
1831df505799SBoris Brezillon
1832df505799SBoris Brezillon if (cmd & NFC_SEND_ADR) {
1833df505799SBoris Brezillon writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW);
1834df505799SBoris Brezillon writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH);
1835df505799SBoris Brezillon }
1836df505799SBoris Brezillon
1837df505799SBoris Brezillon if (cmd & NFC_SEND_CMD2)
1838df505799SBoris Brezillon writel(extcmd,
1839df505799SBoris Brezillon nfc->regs +
1840df505799SBoris Brezillon (cmd & NFC_ACCESS_DIR ?
1841df505799SBoris Brezillon NFC_REG_WCMD_SET : NFC_REG_RCMD_SET));
1842df505799SBoris Brezillon
1843df505799SBoris Brezillon if (cmd & NFC_DATA_TRANS)
1844df505799SBoris Brezillon writel(cnt, nfc->regs + NFC_REG_CNT);
1845df505799SBoris Brezillon
1846df505799SBoris Brezillon writel(cmd, nfc->regs + NFC_REG_CMD);
1847df505799SBoris Brezillon
1848df505799SBoris Brezillon ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG,
1849df505799SBoris Brezillon !(cmd & NFC_WAIT_FLAG) && cnt < 64,
1850df505799SBoris Brezillon 0);
1851df505799SBoris Brezillon if (ret)
1852df505799SBoris Brezillon return ret;
1853df505799SBoris Brezillon
1854df505799SBoris Brezillon if (inbuf)
1855df505799SBoris Brezillon memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt);
1856df505799SBoris Brezillon
1857df505799SBoris Brezillon return 0;
1858df505799SBoris Brezillon }
1859df505799SBoris Brezillon
sunxi_nfc_soft_waitrdy(struct nand_chip * nand,const struct nand_subop * subop)1860df505799SBoris Brezillon static int sunxi_nfc_soft_waitrdy(struct nand_chip *nand,
1861df505799SBoris Brezillon const struct nand_subop *subop)
1862df505799SBoris Brezillon {
1863df505799SBoris Brezillon return nand_soft_waitrdy(nand,
1864df505799SBoris Brezillon subop->instrs[0].ctx.waitrdy.timeout_ms);
1865df505799SBoris Brezillon }
1866df505799SBoris Brezillon
1867df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER(
1868df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1869df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1870df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1871df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1872df505799SBoris Brezillon NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
1873df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
1874df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1875df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1876df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1877df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
1878df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1879df505799SBoris Brezillon NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
1880df505799SBoris Brezillon );
1881df505799SBoris Brezillon
1882df505799SBoris Brezillon static const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER(
1883df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1884df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1885df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1886df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1887df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)),
1888df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop,
1889df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true),
1890df505799SBoris Brezillon NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
1891df505799SBoris Brezillon NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024),
1892df505799SBoris Brezillon NAND_OP_PARSER_PAT_CMD_ELEM(true)),
1893df505799SBoris Brezillon NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy,
1894df505799SBoris Brezillon NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)),
1895df505799SBoris Brezillon );
1896df505799SBoris Brezillon
sunxi_nfc_exec_op(struct nand_chip * nand,const struct nand_operation * op,bool check_only)1897df505799SBoris Brezillon static int sunxi_nfc_exec_op(struct nand_chip *nand,
1898df505799SBoris Brezillon const struct nand_operation *op, bool check_only)
1899df505799SBoris Brezillon {
1900df505799SBoris Brezillon struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
1901df505799SBoris Brezillon const struct nand_op_parser *parser;
1902df505799SBoris Brezillon
1903ce446b4bSBoris Brezillon if (!check_only)
1904df505799SBoris Brezillon sunxi_nfc_select_chip(nand, op->cs);
1905df505799SBoris Brezillon
1906df505799SBoris Brezillon if (sunxi_nand->sels[op->cs].rb >= 0)
1907df505799SBoris Brezillon parser = &sunxi_nfc_op_parser;
1908df505799SBoris Brezillon else
1909df505799SBoris Brezillon parser = &sunxi_nfc_norb_op_parser;
1910df505799SBoris Brezillon
1911df505799SBoris Brezillon return nand_op_parser_exec_op(nand, parser, op, check_only);
1912df505799SBoris Brezillon }
1913df505799SBoris Brezillon
19142a4d9c16SMiquel Raynal static const struct nand_controller_ops sunxi_nand_controller_ops = {
19152a4d9c16SMiquel Raynal .attach_chip = sunxi_nand_attach_chip,
19164c46667bSMiquel Raynal .setup_interface = sunxi_nfc_setup_interface,
1917df505799SBoris Brezillon .exec_op = sunxi_nfc_exec_op,
19182a4d9c16SMiquel Raynal };
19192a4d9c16SMiquel Raynal
sunxi_nand_chips_cleanup(struct sunxi_nfc * nfc)19206d7fea22SSamuel Holland static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
19216d7fea22SSamuel Holland {
19226d7fea22SSamuel Holland struct sunxi_nand_chip *sunxi_nand;
19236d7fea22SSamuel Holland struct nand_chip *chip;
19246d7fea22SSamuel Holland int ret;
19256d7fea22SSamuel Holland
19266d7fea22SSamuel Holland while (!list_empty(&nfc->chips)) {
19276d7fea22SSamuel Holland sunxi_nand = list_first_entry(&nfc->chips,
19286d7fea22SSamuel Holland struct sunxi_nand_chip,
19296d7fea22SSamuel Holland node);
19306d7fea22SSamuel Holland chip = &sunxi_nand->nand;
19316d7fea22SSamuel Holland ret = mtd_device_unregister(nand_to_mtd(chip));
19326d7fea22SSamuel Holland WARN_ON(ret);
19336d7fea22SSamuel Holland nand_cleanup(chip);
19346d7fea22SSamuel Holland list_del(&sunxi_nand->node);
19356d7fea22SSamuel Holland }
19366d7fea22SSamuel Holland }
19376d7fea22SSamuel Holland
sunxi_nand_chip_init(struct device * dev,struct sunxi_nfc * nfc,struct device_node * np)193893db446aSBoris Brezillon static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
193993db446aSBoris Brezillon struct device_node *np)
194093db446aSBoris Brezillon {
1941f385ebf0SBoris Brezillon struct sunxi_nand_chip *sunxi_nand;
194293db446aSBoris Brezillon struct mtd_info *mtd;
194393db446aSBoris Brezillon struct nand_chip *nand;
194493db446aSBoris Brezillon int nsels;
194593db446aSBoris Brezillon int ret;
194693db446aSBoris Brezillon int i;
194793db446aSBoris Brezillon u32 tmp;
194893db446aSBoris Brezillon
194993db446aSBoris Brezillon if (!of_get_property(np, "reg", &nsels))
195093db446aSBoris Brezillon return -EINVAL;
195193db446aSBoris Brezillon
195293db446aSBoris Brezillon nsels /= sizeof(u32);
195393db446aSBoris Brezillon if (!nsels) {
195493db446aSBoris Brezillon dev_err(dev, "invalid reg property size\n");
195593db446aSBoris Brezillon return -EINVAL;
195693db446aSBoris Brezillon }
195793db446aSBoris Brezillon
19586c721acdSBoris Brezillon sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels),
195993db446aSBoris Brezillon GFP_KERNEL);
1960b0821cc5SZhen Lei if (!sunxi_nand)
196193db446aSBoris Brezillon return -ENOMEM;
196293db446aSBoris Brezillon
1963f385ebf0SBoris Brezillon sunxi_nand->nsels = nsels;
196493db446aSBoris Brezillon
196593db446aSBoris Brezillon for (i = 0; i < nsels; i++) {
196693db446aSBoris Brezillon ret = of_property_read_u32_index(np, "reg", i, &tmp);
196793db446aSBoris Brezillon if (ret) {
196893db446aSBoris Brezillon dev_err(dev, "could not retrieve reg property: %d\n",
196993db446aSBoris Brezillon ret);
197093db446aSBoris Brezillon return ret;
197193db446aSBoris Brezillon }
197293db446aSBoris Brezillon
197393db446aSBoris Brezillon if (tmp > NFC_MAX_CS) {
197493db446aSBoris Brezillon dev_err(dev,
197593db446aSBoris Brezillon "invalid reg value: %u (max CS = 7)\n",
197693db446aSBoris Brezillon tmp);
197793db446aSBoris Brezillon return -EINVAL;
197893db446aSBoris Brezillon }
197993db446aSBoris Brezillon
198093db446aSBoris Brezillon if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
198193db446aSBoris Brezillon dev_err(dev, "CS %d already assigned\n", tmp);
198293db446aSBoris Brezillon return -EINVAL;
198393db446aSBoris Brezillon }
198493db446aSBoris Brezillon
1985f385ebf0SBoris Brezillon sunxi_nand->sels[i].cs = tmp;
198693db446aSBoris Brezillon
198793db446aSBoris Brezillon if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
1988ddd5ed3aSBoris Brezillon tmp < 2)
1989f385ebf0SBoris Brezillon sunxi_nand->sels[i].rb = tmp;
1990ddd5ed3aSBoris Brezillon else
1991f385ebf0SBoris Brezillon sunxi_nand->sels[i].rb = -1;
199293db446aSBoris Brezillon }
199393db446aSBoris Brezillon
1994f385ebf0SBoris Brezillon nand = &sunxi_nand->nand;
199593db446aSBoris Brezillon /* Default tR value specified in the ONFI spec (chapter 4.15.1) */
199693db446aSBoris Brezillon nand->controller = &nfc->controller;
19972a4d9c16SMiquel Raynal nand->controller->ops = &sunxi_nand_controller_ops;
19982a4d9c16SMiquel Raynal
199993db446aSBoris Brezillon /*
200093db446aSBoris Brezillon * Set the ECC mode to the default value in case nothing is specified
200193db446aSBoris Brezillon * in the DT.
200293db446aSBoris Brezillon */
2003bace41f8SMiquel Raynal nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
200493db446aSBoris Brezillon nand_set_flash_node(nand, np);
200593db446aSBoris Brezillon
200693db446aSBoris Brezillon mtd = nand_to_mtd(nand);
200793db446aSBoris Brezillon mtd->dev.parent = dev;
200893db446aSBoris Brezillon
200900ad378fSBoris Brezillon ret = nand_scan(nand, nsels);
201093db446aSBoris Brezillon if (ret)
201193db446aSBoris Brezillon return ret;
201293db446aSBoris Brezillon
201393db446aSBoris Brezillon ret = mtd_device_register(mtd, NULL, 0);
201493db446aSBoris Brezillon if (ret) {
201593db446aSBoris Brezillon dev_err(dev, "failed to register mtd device: %d\n", ret);
20163d84515fSMiquel Raynal nand_cleanup(nand);
201793db446aSBoris Brezillon return ret;
201893db446aSBoris Brezillon }
201993db446aSBoris Brezillon
2020f385ebf0SBoris Brezillon list_add_tail(&sunxi_nand->node, &nfc->chips);
202193db446aSBoris Brezillon
202293db446aSBoris Brezillon return 0;
202393db446aSBoris Brezillon }
202493db446aSBoris Brezillon
sunxi_nand_chips_init(struct device * dev,struct sunxi_nfc * nfc)202593db446aSBoris Brezillon static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
202693db446aSBoris Brezillon {
202793db446aSBoris Brezillon struct device_node *np = dev->of_node;
202893db446aSBoris Brezillon struct device_node *nand_np;
202993db446aSBoris Brezillon int ret;
203093db446aSBoris Brezillon
203193db446aSBoris Brezillon for_each_child_of_node(np, nand_np) {
203293db446aSBoris Brezillon ret = sunxi_nand_chip_init(dev, nfc, nand_np);
203393db446aSBoris Brezillon if (ret) {
203493db446aSBoris Brezillon of_node_put(nand_np);
20356d7fea22SSamuel Holland sunxi_nand_chips_cleanup(nfc);
203693db446aSBoris Brezillon return ret;
203793db446aSBoris Brezillon }
203893db446aSBoris Brezillon }
203993db446aSBoris Brezillon
204093db446aSBoris Brezillon return 0;
204193db446aSBoris Brezillon }
204293db446aSBoris Brezillon
sunxi_nfc_dma_init(struct sunxi_nfc * nfc,struct resource * r)2043910ef7a4SManuel Dipolt static int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r)
2044910ef7a4SManuel Dipolt {
2045910ef7a4SManuel Dipolt int ret;
2046910ef7a4SManuel Dipolt
2047910ef7a4SManuel Dipolt if (nfc->caps->has_mdma)
2048910ef7a4SManuel Dipolt return 0;
2049910ef7a4SManuel Dipolt
2050910ef7a4SManuel Dipolt nfc->dmac = dma_request_chan(nfc->dev, "rxtx");
2051910ef7a4SManuel Dipolt if (IS_ERR(nfc->dmac)) {
2052910ef7a4SManuel Dipolt ret = PTR_ERR(nfc->dmac);
2053910ef7a4SManuel Dipolt if (ret == -EPROBE_DEFER)
2054910ef7a4SManuel Dipolt return ret;
2055910ef7a4SManuel Dipolt
2056910ef7a4SManuel Dipolt /* Ignore errors to fall back to PIO mode */
2057910ef7a4SManuel Dipolt dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret);
2058910ef7a4SManuel Dipolt nfc->dmac = NULL;
2059910ef7a4SManuel Dipolt } else {
2060910ef7a4SManuel Dipolt struct dma_slave_config dmac_cfg = { };
2061910ef7a4SManuel Dipolt
2062910ef7a4SManuel Dipolt dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data;
2063910ef7a4SManuel Dipolt dmac_cfg.dst_addr = dmac_cfg.src_addr;
2064910ef7a4SManuel Dipolt dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
2065910ef7a4SManuel Dipolt dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width;
2066910ef7a4SManuel Dipolt dmac_cfg.src_maxburst = nfc->caps->dma_maxburst;
2067910ef7a4SManuel Dipolt dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst;
2068910ef7a4SManuel Dipolt dmaengine_slave_config(nfc->dmac, &dmac_cfg);
2069910ef7a4SManuel Dipolt }
2070910ef7a4SManuel Dipolt return 0;
2071910ef7a4SManuel Dipolt }
2072910ef7a4SManuel Dipolt
sunxi_nfc_probe(struct platform_device * pdev)207393db446aSBoris Brezillon static int sunxi_nfc_probe(struct platform_device *pdev)
207493db446aSBoris Brezillon {
207593db446aSBoris Brezillon struct device *dev = &pdev->dev;
207693db446aSBoris Brezillon struct resource *r;
207793db446aSBoris Brezillon struct sunxi_nfc *nfc;
207893db446aSBoris Brezillon int irq;
207993db446aSBoris Brezillon int ret;
208093db446aSBoris Brezillon
208193db446aSBoris Brezillon nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
208293db446aSBoris Brezillon if (!nfc)
208393db446aSBoris Brezillon return -ENOMEM;
208493db446aSBoris Brezillon
208593db446aSBoris Brezillon nfc->dev = dev;
20867da45139SMiquel Raynal nand_controller_init(&nfc->controller);
208793db446aSBoris Brezillon INIT_LIST_HEAD(&nfc->chips);
208893db446aSBoris Brezillon
208981a16e15SYangtao Li nfc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
209093db446aSBoris Brezillon if (IS_ERR(nfc->regs))
209193db446aSBoris Brezillon return PTR_ERR(nfc->regs);
209293db446aSBoris Brezillon
209393db446aSBoris Brezillon irq = platform_get_irq(pdev, 0);
2094aab478caSStephen Boyd if (irq < 0)
209593db446aSBoris Brezillon return irq;
209693db446aSBoris Brezillon
2097*a95da272SLi Zetao nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
209893db446aSBoris Brezillon if (IS_ERR(nfc->ahb_clk)) {
209993db446aSBoris Brezillon dev_err(dev, "failed to retrieve ahb clk\n");
210093db446aSBoris Brezillon return PTR_ERR(nfc->ahb_clk);
210193db446aSBoris Brezillon }
210293db446aSBoris Brezillon
2103*a95da272SLi Zetao nfc->mod_clk = devm_clk_get_enabled(dev, "mod");
210493db446aSBoris Brezillon if (IS_ERR(nfc->mod_clk)) {
210593db446aSBoris Brezillon dev_err(dev, "failed to retrieve mod clk\n");
2106*a95da272SLi Zetao return PTR_ERR(nfc->mod_clk);
210793db446aSBoris Brezillon }
210893db446aSBoris Brezillon
210993db446aSBoris Brezillon nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
2110*a95da272SLi Zetao if (IS_ERR(nfc->reset))
2111*a95da272SLi Zetao return PTR_ERR(nfc->reset);
211293db446aSBoris Brezillon
211393db446aSBoris Brezillon ret = reset_control_deassert(nfc->reset);
211493db446aSBoris Brezillon if (ret) {
211593db446aSBoris Brezillon dev_err(dev, "reset err %d\n", ret);
2116*a95da272SLi Zetao return ret;
211793db446aSBoris Brezillon }
211893db446aSBoris Brezillon
2119a760e77dSMiquel Raynal nfc->caps = of_device_get_match_data(&pdev->dev);
2120a760e77dSMiquel Raynal if (!nfc->caps) {
2121a760e77dSMiquel Raynal ret = -EINVAL;
2122a760e77dSMiquel Raynal goto out_ahb_reset_reassert;
2123a760e77dSMiquel Raynal }
2124a760e77dSMiquel Raynal
212593db446aSBoris Brezillon ret = sunxi_nfc_rst(nfc);
212693db446aSBoris Brezillon if (ret)
212793db446aSBoris Brezillon goto out_ahb_reset_reassert;
212893db446aSBoris Brezillon
212993db446aSBoris Brezillon writel(0, nfc->regs + NFC_REG_INT);
213093db446aSBoris Brezillon ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
213193db446aSBoris Brezillon 0, "sunxi-nand", nfc);
213293db446aSBoris Brezillon if (ret)
213393db446aSBoris Brezillon goto out_ahb_reset_reassert;
213493db446aSBoris Brezillon
2135910ef7a4SManuel Dipolt ret = sunxi_nfc_dma_init(nfc, r);
2136910ef7a4SManuel Dipolt
2137910ef7a4SManuel Dipolt if (ret)
2138ac80c55bSPeter Ujfalusi goto out_ahb_reset_reassert;
2139ac80c55bSPeter Ujfalusi
214093db446aSBoris Brezillon platform_set_drvdata(pdev, nfc);
214193db446aSBoris Brezillon
214293db446aSBoris Brezillon ret = sunxi_nand_chips_init(dev, nfc);
214393db446aSBoris Brezillon if (ret) {
214493db446aSBoris Brezillon dev_err(dev, "failed to init nand chips\n");
214593db446aSBoris Brezillon goto out_release_dmac;
214693db446aSBoris Brezillon }
214793db446aSBoris Brezillon
214893db446aSBoris Brezillon return 0;
214993db446aSBoris Brezillon
215093db446aSBoris Brezillon out_release_dmac:
215193db446aSBoris Brezillon if (nfc->dmac)
215293db446aSBoris Brezillon dma_release_channel(nfc->dmac);
215393db446aSBoris Brezillon out_ahb_reset_reassert:
215493db446aSBoris Brezillon reset_control_assert(nfc->reset);
215593db446aSBoris Brezillon
215693db446aSBoris Brezillon return ret;
215793db446aSBoris Brezillon }
215893db446aSBoris Brezillon
sunxi_nfc_remove(struct platform_device * pdev)2159ec185b18SUwe Kleine-König static void sunxi_nfc_remove(struct platform_device *pdev)
216093db446aSBoris Brezillon {
216193db446aSBoris Brezillon struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
216293db446aSBoris Brezillon
216393db446aSBoris Brezillon sunxi_nand_chips_cleanup(nfc);
216493db446aSBoris Brezillon
216593db446aSBoris Brezillon reset_control_assert(nfc->reset);
216693db446aSBoris Brezillon
216793db446aSBoris Brezillon if (nfc->dmac)
216893db446aSBoris Brezillon dma_release_channel(nfc->dmac);
216993db446aSBoris Brezillon }
217093db446aSBoris Brezillon
2171a760e77dSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
2172a760e77dSMiquel Raynal .reg_io_data = NFC_REG_A10_IO_DATA,
2173a760e77dSMiquel Raynal .dma_maxburst = 4,
2174a760e77dSMiquel Raynal };
2175a760e77dSMiquel Raynal
2176c7a87cebSMiquel Raynal static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
2177910ef7a4SManuel Dipolt .has_mdma = true,
2178c7a87cebSMiquel Raynal .reg_io_data = NFC_REG_A23_IO_DATA,
2179c7a87cebSMiquel Raynal .dma_maxburst = 8,
2180c7a87cebSMiquel Raynal };
2181c7a87cebSMiquel Raynal
218293db446aSBoris Brezillon static const struct of_device_id sunxi_nfc_ids[] = {
2183a760e77dSMiquel Raynal {
2184a760e77dSMiquel Raynal .compatible = "allwinner,sun4i-a10-nand",
2185a760e77dSMiquel Raynal .data = &sunxi_nfc_a10_caps,
2186a760e77dSMiquel Raynal },
2187c7a87cebSMiquel Raynal {
2188c7a87cebSMiquel Raynal .compatible = "allwinner,sun8i-a23-nand-controller",
2189c7a87cebSMiquel Raynal .data = &sunxi_nfc_a23_caps,
2190c7a87cebSMiquel Raynal },
219193db446aSBoris Brezillon { /* sentinel */ }
219293db446aSBoris Brezillon };
219393db446aSBoris Brezillon MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
219493db446aSBoris Brezillon
219593db446aSBoris Brezillon static struct platform_driver sunxi_nfc_driver = {
219693db446aSBoris Brezillon .driver = {
219793db446aSBoris Brezillon .name = "sunxi_nand",
219893db446aSBoris Brezillon .of_match_table = sunxi_nfc_ids,
219993db446aSBoris Brezillon },
220093db446aSBoris Brezillon .probe = sunxi_nfc_probe,
2201ec185b18SUwe Kleine-König .remove_new = sunxi_nfc_remove,
220293db446aSBoris Brezillon };
220393db446aSBoris Brezillon module_platform_driver(sunxi_nfc_driver);
220493db446aSBoris Brezillon
2205f5f88871SBoris Brezillon MODULE_LICENSE("GPL");
220693db446aSBoris Brezillon MODULE_AUTHOR("Boris BREZILLON");
220793db446aSBoris Brezillon MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
220893db446aSBoris Brezillon MODULE_ALIAS("platform:sunxi_nand");
2209