162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from: 662306a36Sopenharmony_ci * https://github.com/yuq/sunxi-nfc-mtd 762306a36Sopenharmony_ci * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * https://github.com/hno/Allwinner-Info 1062306a36Sopenharmony_ci * Copyright (C) 2013 Henrik Nordström <Henrik Nordström> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com> 1362306a36Sopenharmony_ci * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org> 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/moduleparam.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2362306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 2462306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 2562306a36Sopenharmony_ci#include <linux/clk.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/dmaengine.h> 2862306a36Sopenharmony_ci#include <linux/interrupt.h> 2962306a36Sopenharmony_ci#include <linux/iopoll.h> 3062306a36Sopenharmony_ci#include <linux/reset.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define NFC_REG_CTL 0x0000 3362306a36Sopenharmony_ci#define NFC_REG_ST 0x0004 3462306a36Sopenharmony_ci#define NFC_REG_INT 0x0008 3562306a36Sopenharmony_ci#define NFC_REG_TIMING_CTL 0x000C 3662306a36Sopenharmony_ci#define NFC_REG_TIMING_CFG 0x0010 3762306a36Sopenharmony_ci#define NFC_REG_ADDR_LOW 0x0014 3862306a36Sopenharmony_ci#define NFC_REG_ADDR_HIGH 0x0018 3962306a36Sopenharmony_ci#define NFC_REG_SECTOR_NUM 0x001C 4062306a36Sopenharmony_ci#define NFC_REG_CNT 0x0020 4162306a36Sopenharmony_ci#define NFC_REG_CMD 0x0024 4262306a36Sopenharmony_ci#define NFC_REG_RCMD_SET 0x0028 4362306a36Sopenharmony_ci#define NFC_REG_WCMD_SET 0x002C 4462306a36Sopenharmony_ci#define NFC_REG_A10_IO_DATA 0x0030 4562306a36Sopenharmony_ci#define NFC_REG_A23_IO_DATA 0x0300 4662306a36Sopenharmony_ci#define NFC_REG_ECC_CTL 0x0034 4762306a36Sopenharmony_ci#define NFC_REG_ECC_ST 0x0038 4862306a36Sopenharmony_ci#define NFC_REG_DEBUG 0x003C 4962306a36Sopenharmony_ci#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) 5062306a36Sopenharmony_ci#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) 5162306a36Sopenharmony_ci#define NFC_REG_SPARE_AREA 0x00A0 5262306a36Sopenharmony_ci#define NFC_REG_PAT_ID 0x00A4 5362306a36Sopenharmony_ci#define NFC_REG_MDMA_ADDR 0x00C0 5462306a36Sopenharmony_ci#define NFC_REG_MDMA_CNT 0x00C4 5562306a36Sopenharmony_ci#define NFC_RAM0_BASE 0x0400 5662306a36Sopenharmony_ci#define NFC_RAM1_BASE 0x0800 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* define bit use in NFC_CTL */ 5962306a36Sopenharmony_ci#define NFC_EN BIT(0) 6062306a36Sopenharmony_ci#define NFC_RESET BIT(1) 6162306a36Sopenharmony_ci#define NFC_BUS_WIDTH_MSK BIT(2) 6262306a36Sopenharmony_ci#define NFC_BUS_WIDTH_8 (0 << 2) 6362306a36Sopenharmony_ci#define NFC_BUS_WIDTH_16 (1 << 2) 6462306a36Sopenharmony_ci#define NFC_RB_SEL_MSK BIT(3) 6562306a36Sopenharmony_ci#define NFC_RB_SEL(x) ((x) << 3) 6662306a36Sopenharmony_ci#define NFC_CE_SEL_MSK GENMASK(26, 24) 6762306a36Sopenharmony_ci#define NFC_CE_SEL(x) ((x) << 24) 6862306a36Sopenharmony_ci#define NFC_CE_CTL BIT(6) 6962306a36Sopenharmony_ci#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) 7062306a36Sopenharmony_ci#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) 7162306a36Sopenharmony_ci#define NFC_SAM BIT(12) 7262306a36Sopenharmony_ci#define NFC_RAM_METHOD BIT(14) 7362306a36Sopenharmony_ci#define NFC_DMA_TYPE_NORMAL BIT(15) 7462306a36Sopenharmony_ci#define NFC_DEBUG_CTL BIT(31) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* define bit use in NFC_ST */ 7762306a36Sopenharmony_ci#define NFC_RB_B2R BIT(0) 7862306a36Sopenharmony_ci#define NFC_CMD_INT_FLAG BIT(1) 7962306a36Sopenharmony_ci#define NFC_DMA_INT_FLAG BIT(2) 8062306a36Sopenharmony_ci#define NFC_CMD_FIFO_STATUS BIT(3) 8162306a36Sopenharmony_ci#define NFC_STA BIT(4) 8262306a36Sopenharmony_ci#define NFC_NATCH_INT_FLAG BIT(5) 8362306a36Sopenharmony_ci#define NFC_RB_STATE(x) BIT(x + 8) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* define bit use in NFC_INT */ 8662306a36Sopenharmony_ci#define NFC_B2R_INT_ENABLE BIT(0) 8762306a36Sopenharmony_ci#define NFC_CMD_INT_ENABLE BIT(1) 8862306a36Sopenharmony_ci#define NFC_DMA_INT_ENABLE BIT(2) 8962306a36Sopenharmony_ci#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ 9062306a36Sopenharmony_ci NFC_CMD_INT_ENABLE | \ 9162306a36Sopenharmony_ci NFC_DMA_INT_ENABLE) 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* define bit use in NFC_TIMING_CTL */ 9462306a36Sopenharmony_ci#define NFC_TIMING_CTL_EDO BIT(8) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* define NFC_TIMING_CFG register layout */ 9762306a36Sopenharmony_ci#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ 9862306a36Sopenharmony_ci (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ 9962306a36Sopenharmony_ci (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ 10062306a36Sopenharmony_ci (((tCAD) & 0x7) << 8)) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* define bit use in NFC_CMD */ 10362306a36Sopenharmony_ci#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) 10462306a36Sopenharmony_ci#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) 10562306a36Sopenharmony_ci#define NFC_CMD(x) (x) 10662306a36Sopenharmony_ci#define NFC_ADR_NUM_MSK GENMASK(18, 16) 10762306a36Sopenharmony_ci#define NFC_ADR_NUM(x) (((x) - 1) << 16) 10862306a36Sopenharmony_ci#define NFC_SEND_ADR BIT(19) 10962306a36Sopenharmony_ci#define NFC_ACCESS_DIR BIT(20) 11062306a36Sopenharmony_ci#define NFC_DATA_TRANS BIT(21) 11162306a36Sopenharmony_ci#define NFC_SEND_CMD1 BIT(22) 11262306a36Sopenharmony_ci#define NFC_WAIT_FLAG BIT(23) 11362306a36Sopenharmony_ci#define NFC_SEND_CMD2 BIT(24) 11462306a36Sopenharmony_ci#define NFC_SEQ BIT(25) 11562306a36Sopenharmony_ci#define NFC_DATA_SWAP_METHOD BIT(26) 11662306a36Sopenharmony_ci#define NFC_ROW_AUTO_INC BIT(27) 11762306a36Sopenharmony_ci#define NFC_SEND_CMD3 BIT(28) 11862306a36Sopenharmony_ci#define NFC_SEND_CMD4 BIT(29) 11962306a36Sopenharmony_ci#define NFC_CMD_TYPE_MSK GENMASK(31, 30) 12062306a36Sopenharmony_ci#define NFC_NORMAL_OP (0 << 30) 12162306a36Sopenharmony_ci#define NFC_ECC_OP (1 << 30) 12262306a36Sopenharmony_ci#define NFC_PAGE_OP (2U << 30) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* define bit use in NFC_RCMD_SET */ 12562306a36Sopenharmony_ci#define NFC_READ_CMD_MSK GENMASK(7, 0) 12662306a36Sopenharmony_ci#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) 12762306a36Sopenharmony_ci#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* define bit use in NFC_WCMD_SET */ 13062306a36Sopenharmony_ci#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) 13162306a36Sopenharmony_ci#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) 13262306a36Sopenharmony_ci#define NFC_READ_CMD0_MSK GENMASK(23, 16) 13362306a36Sopenharmony_ci#define NFC_READ_CMD1_MSK GENMASK(31, 24) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* define bit use in NFC_ECC_CTL */ 13662306a36Sopenharmony_ci#define NFC_ECC_EN BIT(0) 13762306a36Sopenharmony_ci#define NFC_ECC_PIPELINE BIT(3) 13862306a36Sopenharmony_ci#define NFC_ECC_EXCEPTION BIT(4) 13962306a36Sopenharmony_ci#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) 14062306a36Sopenharmony_ci#define NFC_ECC_BLOCK_512 BIT(5) 14162306a36Sopenharmony_ci#define NFC_RANDOM_EN BIT(9) 14262306a36Sopenharmony_ci#define NFC_RANDOM_DIRECTION BIT(10) 14362306a36Sopenharmony_ci#define NFC_ECC_MODE_MSK GENMASK(15, 12) 14462306a36Sopenharmony_ci#define NFC_ECC_MODE(x) ((x) << 12) 14562306a36Sopenharmony_ci#define NFC_RANDOM_SEED_MSK GENMASK(30, 16) 14662306a36Sopenharmony_ci#define NFC_RANDOM_SEED(x) ((x) << 16) 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* define bit use in NFC_ECC_ST */ 14962306a36Sopenharmony_ci#define NFC_ECC_ERR(x) BIT(x) 15062306a36Sopenharmony_ci#define NFC_ECC_ERR_MSK GENMASK(15, 0) 15162306a36Sopenharmony_ci#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) 15262306a36Sopenharmony_ci#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define NFC_DEFAULT_TIMEOUT_MS 1000 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define NFC_SRAM_SIZE 1024 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define NFC_MAX_CS 7 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/** 16162306a36Sopenharmony_ci * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * @cs: the NAND CS id used to communicate with a NAND Chip 16462306a36Sopenharmony_ci * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the NFC 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistruct sunxi_nand_chip_sel { 16762306a36Sopenharmony_ci u8 cs; 16862306a36Sopenharmony_ci s8 rb; 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/** 17262306a36Sopenharmony_ci * struct sunxi_nand_hw_ecc - stores information related to HW ECC support 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * @ecc_ctl: ECC_CTL register value for this NAND chip 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistruct sunxi_nand_hw_ecc { 17762306a36Sopenharmony_ci u32 ecc_ctl; 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * struct sunxi_nand_chip - stores NAND chip device related information 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * @node: used to store NAND chips into a list 18462306a36Sopenharmony_ci * @nand: base NAND chip structure 18562306a36Sopenharmony_ci * @ecc: ECC controller structure 18662306a36Sopenharmony_ci * @clk_rate: clk_rate required for this NAND chip 18762306a36Sopenharmony_ci * @timing_cfg: TIMING_CFG register value for this NAND chip 18862306a36Sopenharmony_ci * @timing_ctl: TIMING_CTL register value for this NAND chip 18962306a36Sopenharmony_ci * @nsels: number of CS lines required by the NAND chip 19062306a36Sopenharmony_ci * @sels: array of CS lines descriptions 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistruct sunxi_nand_chip { 19362306a36Sopenharmony_ci struct list_head node; 19462306a36Sopenharmony_ci struct nand_chip nand; 19562306a36Sopenharmony_ci struct sunxi_nand_hw_ecc ecc; 19662306a36Sopenharmony_ci unsigned long clk_rate; 19762306a36Sopenharmony_ci u32 timing_cfg; 19862306a36Sopenharmony_ci u32 timing_ctl; 19962306a36Sopenharmony_ci int nsels; 20062306a36Sopenharmony_ci struct sunxi_nand_chip_sel sels[]; 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci return container_of(nand, struct sunxi_nand_chip, nand); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * NAND Controller capabilities structure: stores NAND controller capabilities 21062306a36Sopenharmony_ci * for distinction between compatible strings. 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * @has_mdma: Use mbus dma mode, otherwise general dma 21362306a36Sopenharmony_ci * through MBUS on A23/A33 needs extra configuration. 21462306a36Sopenharmony_ci * @reg_io_data: I/O data register 21562306a36Sopenharmony_ci * @dma_maxburst: DMA maxburst 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistruct sunxi_nfc_caps { 21862306a36Sopenharmony_ci bool has_mdma; 21962306a36Sopenharmony_ci unsigned int reg_io_data; 22062306a36Sopenharmony_ci unsigned int dma_maxburst; 22162306a36Sopenharmony_ci}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/** 22462306a36Sopenharmony_ci * struct sunxi_nfc - stores sunxi NAND controller information 22562306a36Sopenharmony_ci * 22662306a36Sopenharmony_ci * @controller: base controller structure 22762306a36Sopenharmony_ci * @dev: parent device (used to print error messages) 22862306a36Sopenharmony_ci * @regs: NAND controller registers 22962306a36Sopenharmony_ci * @ahb_clk: NAND controller AHB clock 23062306a36Sopenharmony_ci * @mod_clk: NAND controller mod clock 23162306a36Sopenharmony_ci * @reset: NAND controller reset line 23262306a36Sopenharmony_ci * @assigned_cs: bitmask describing already assigned CS lines 23362306a36Sopenharmony_ci * @clk_rate: NAND controller current clock rate 23462306a36Sopenharmony_ci * @chips: a list containing all the NAND chips attached to this NAND 23562306a36Sopenharmony_ci * controller 23662306a36Sopenharmony_ci * @complete: a completion object used to wait for NAND controller events 23762306a36Sopenharmony_ci * @dmac: the DMA channel attached to the NAND controller 23862306a36Sopenharmony_ci * @caps: NAND Controller capabilities 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_cistruct sunxi_nfc { 24162306a36Sopenharmony_ci struct nand_controller controller; 24262306a36Sopenharmony_ci struct device *dev; 24362306a36Sopenharmony_ci void __iomem *regs; 24462306a36Sopenharmony_ci struct clk *ahb_clk; 24562306a36Sopenharmony_ci struct clk *mod_clk; 24662306a36Sopenharmony_ci struct reset_control *reset; 24762306a36Sopenharmony_ci unsigned long assigned_cs; 24862306a36Sopenharmony_ci unsigned long clk_rate; 24962306a36Sopenharmony_ci struct list_head chips; 25062306a36Sopenharmony_ci struct completion complete; 25162306a36Sopenharmony_ci struct dma_chan *dmac; 25262306a36Sopenharmony_ci const struct sunxi_nfc_caps *caps; 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci return container_of(ctrl, struct sunxi_nfc, controller); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct sunxi_nfc *nfc = dev_id; 26362306a36Sopenharmony_ci u32 st = readl(nfc->regs + NFC_REG_ST); 26462306a36Sopenharmony_ci u32 ien = readl(nfc->regs + NFC_REG_INT); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (!(ien & st)) 26762306a36Sopenharmony_ci return IRQ_NONE; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if ((ien & st) == ien) 27062306a36Sopenharmony_ci complete(&nfc->complete); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); 27362306a36Sopenharmony_ci writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return IRQ_HANDLED; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, 27962306a36Sopenharmony_ci bool use_polling, unsigned int timeout_ms) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci int ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (events & ~NFC_INT_MASK) 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (!timeout_ms) 28762306a36Sopenharmony_ci timeout_ms = NFC_DEFAULT_TIMEOUT_MS; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!use_polling) { 29062306a36Sopenharmony_ci init_completion(&nfc->complete); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci writel(events, nfc->regs + NFC_REG_INT); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ret = wait_for_completion_timeout(&nfc->complete, 29562306a36Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 29662306a36Sopenharmony_ci if (!ret) 29762306a36Sopenharmony_ci ret = -ETIMEDOUT; 29862306a36Sopenharmony_ci else 29962306a36Sopenharmony_ci ret = 0; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci writel(0, nfc->regs + NFC_REG_INT); 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci u32 status; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, 30662306a36Sopenharmony_ci (status & events) == events, 1, 30762306a36Sopenharmony_ci timeout_ms * 1000); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (ret) 31362306a36Sopenharmony_ci dev_err(nfc->dev, "wait interrupt timedout\n"); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci u32 status; 32162306a36Sopenharmony_ci int ret; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, 32462306a36Sopenharmony_ci !(status & NFC_CMD_FIFO_STATUS), 1, 32562306a36Sopenharmony_ci NFC_DEFAULT_TIMEOUT_MS * 1000); 32662306a36Sopenharmony_ci if (ret) 32762306a36Sopenharmony_ci dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int sunxi_nfc_rst(struct sunxi_nfc *nfc) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci u32 ctl; 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci writel(0, nfc->regs + NFC_REG_ECC_CTL); 33862306a36Sopenharmony_ci writel(NFC_RESET, nfc->regs + NFC_REG_CTL); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, 34162306a36Sopenharmony_ci !(ctl & NFC_RESET), 1, 34262306a36Sopenharmony_ci NFC_DEFAULT_TIMEOUT_MS * 1000); 34362306a36Sopenharmony_ci if (ret) 34462306a36Sopenharmony_ci dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, 35062306a36Sopenharmony_ci int chunksize, int nchunks, 35162306a36Sopenharmony_ci enum dma_data_direction ddir, 35262306a36Sopenharmony_ci struct scatterlist *sg) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct dma_async_tx_descriptor *dmad; 35562306a36Sopenharmony_ci enum dma_transfer_direction tdir; 35662306a36Sopenharmony_ci dma_cookie_t dmat; 35762306a36Sopenharmony_ci int ret; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (ddir == DMA_FROM_DEVICE) 36062306a36Sopenharmony_ci tdir = DMA_DEV_TO_MEM; 36162306a36Sopenharmony_ci else 36262306a36Sopenharmony_ci tdir = DMA_MEM_TO_DEV; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci sg_init_one(sg, buf, nchunks * chunksize); 36562306a36Sopenharmony_ci ret = dma_map_sg(nfc->dev, sg, 1, ddir); 36662306a36Sopenharmony_ci if (!ret) 36762306a36Sopenharmony_ci return -ENOMEM; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!nfc->caps->has_mdma) { 37062306a36Sopenharmony_ci dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); 37162306a36Sopenharmony_ci if (!dmad) { 37262306a36Sopenharmony_ci ret = -EINVAL; 37362306a36Sopenharmony_ci goto err_unmap_buf; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, 37862306a36Sopenharmony_ci nfc->regs + NFC_REG_CTL); 37962306a36Sopenharmony_ci writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); 38062306a36Sopenharmony_ci writel(chunksize, nfc->regs + NFC_REG_CNT); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (nfc->caps->has_mdma) { 38362306a36Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_DMA_TYPE_NORMAL, 38462306a36Sopenharmony_ci nfc->regs + NFC_REG_CTL); 38562306a36Sopenharmony_ci writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); 38662306a36Sopenharmony_ci writel(sg_dma_address(sg), nfc->regs + NFC_REG_MDMA_ADDR); 38762306a36Sopenharmony_ci } else { 38862306a36Sopenharmony_ci dmat = dmaengine_submit(dmad); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci ret = dma_submit_error(dmat); 39162306a36Sopenharmony_ci if (ret) 39262306a36Sopenharmony_ci goto err_clr_dma_flag; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cierr_clr_dma_flag: 39862306a36Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, 39962306a36Sopenharmony_ci nfc->regs + NFC_REG_CTL); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cierr_unmap_buf: 40262306a36Sopenharmony_ci dma_unmap_sg(nfc->dev, sg, 1, ddir); 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc, 40762306a36Sopenharmony_ci enum dma_data_direction ddir, 40862306a36Sopenharmony_ci struct scatterlist *sg) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci dma_unmap_sg(nfc->dev, sg, 1, ddir); 41162306a36Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, 41262306a36Sopenharmony_ci nfc->regs + NFC_REG_CTL); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 41862306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 41962306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 42062306a36Sopenharmony_ci struct sunxi_nand_chip_sel *sel; 42162306a36Sopenharmony_ci u32 ctl; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (cs >= sunxi_nand->nsels) 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ctl = readl(nfc->regs + NFC_REG_CTL) & 42762306a36Sopenharmony_ci ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci sel = &sunxi_nand->sels[cs]; 43062306a36Sopenharmony_ci ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift); 43162306a36Sopenharmony_ci if (sel->rb >= 0) 43262306a36Sopenharmony_ci ctl |= NFC_RB_SEL(sel->rb); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (nfc->clk_rate != sunxi_nand->clk_rate) { 43762306a36Sopenharmony_ci clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); 43862306a36Sopenharmony_ci nfc->clk_rate = sunxi_nand->clk_rate; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); 44262306a36Sopenharmony_ci writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); 44362306a36Sopenharmony_ci writel(ctl, nfc->regs + NFC_REG_CTL); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 44962306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 45062306a36Sopenharmony_ci int ret; 45162306a36Sopenharmony_ci int cnt; 45262306a36Sopenharmony_ci int offs = 0; 45362306a36Sopenharmony_ci u32 tmp; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci while (len > offs) { 45662306a36Sopenharmony_ci bool poll = false; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci cnt = min(len - offs, NFC_SRAM_SIZE); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 46162306a36Sopenharmony_ci if (ret) 46262306a36Sopenharmony_ci break; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci writel(cnt, nfc->regs + NFC_REG_CNT); 46562306a36Sopenharmony_ci tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; 46662306a36Sopenharmony_ci writel(tmp, nfc->regs + NFC_REG_CMD); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Arbitrary limit for polling mode */ 46962306a36Sopenharmony_ci if (cnt < 64) 47062306a36Sopenharmony_ci poll = true; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); 47362306a36Sopenharmony_ci if (ret) 47462306a36Sopenharmony_ci break; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (buf) 47762306a36Sopenharmony_ci memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, 47862306a36Sopenharmony_ci cnt); 47962306a36Sopenharmony_ci offs += cnt; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf, 48462306a36Sopenharmony_ci int len) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 48762306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 48862306a36Sopenharmony_ci int ret; 48962306a36Sopenharmony_ci int cnt; 49062306a36Sopenharmony_ci int offs = 0; 49162306a36Sopenharmony_ci u32 tmp; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci while (len > offs) { 49462306a36Sopenharmony_ci bool poll = false; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci cnt = min(len - offs, NFC_SRAM_SIZE); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 49962306a36Sopenharmony_ci if (ret) 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci writel(cnt, nfc->regs + NFC_REG_CNT); 50362306a36Sopenharmony_ci memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); 50462306a36Sopenharmony_ci tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | 50562306a36Sopenharmony_ci NFC_ACCESS_DIR; 50662306a36Sopenharmony_ci writel(tmp, nfc->regs + NFC_REG_CMD); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Arbitrary limit for polling mode */ 50962306a36Sopenharmony_ci if (cnt < 64) 51062306a36Sopenharmony_ci poll = true; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); 51362306a36Sopenharmony_ci if (ret) 51462306a36Sopenharmony_ci break; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci offs += cnt; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* These seed values have been extracted from Allwinner's BSP */ 52162306a36Sopenharmony_cistatic const u16 sunxi_nfc_randomizer_page_seeds[] = { 52262306a36Sopenharmony_ci 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, 52362306a36Sopenharmony_ci 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, 52462306a36Sopenharmony_ci 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, 52562306a36Sopenharmony_ci 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, 52662306a36Sopenharmony_ci 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, 52762306a36Sopenharmony_ci 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, 52862306a36Sopenharmony_ci 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, 52962306a36Sopenharmony_ci 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, 53062306a36Sopenharmony_ci 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, 53162306a36Sopenharmony_ci 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, 53262306a36Sopenharmony_ci 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, 53362306a36Sopenharmony_ci 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, 53462306a36Sopenharmony_ci 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, 53562306a36Sopenharmony_ci 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, 53662306a36Sopenharmony_ci 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, 53762306a36Sopenharmony_ci 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci/* 54162306a36Sopenharmony_ci * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds 54262306a36Sopenharmony_ci * have been generated using 54362306a36Sopenharmony_ci * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what 54462306a36Sopenharmony_ci * the randomizer engine does internally before de/scrambling OOB data. 54562306a36Sopenharmony_ci * 54662306a36Sopenharmony_ci * Those tables are statically defined to avoid calculating randomizer state 54762306a36Sopenharmony_ci * at runtime. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_cistatic const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { 55062306a36Sopenharmony_ci 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, 55162306a36Sopenharmony_ci 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, 55262306a36Sopenharmony_ci 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, 55362306a36Sopenharmony_ci 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, 55462306a36Sopenharmony_ci 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, 55562306a36Sopenharmony_ci 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, 55662306a36Sopenharmony_ci 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, 55762306a36Sopenharmony_ci 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, 55862306a36Sopenharmony_ci 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, 55962306a36Sopenharmony_ci 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, 56062306a36Sopenharmony_ci 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, 56162306a36Sopenharmony_ci 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, 56262306a36Sopenharmony_ci 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, 56362306a36Sopenharmony_ci 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, 56462306a36Sopenharmony_ci 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, 56562306a36Sopenharmony_ci 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, 56662306a36Sopenharmony_ci}; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { 56962306a36Sopenharmony_ci 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, 57062306a36Sopenharmony_ci 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, 57162306a36Sopenharmony_ci 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, 57262306a36Sopenharmony_ci 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, 57362306a36Sopenharmony_ci 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, 57462306a36Sopenharmony_ci 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, 57562306a36Sopenharmony_ci 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, 57662306a36Sopenharmony_ci 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, 57762306a36Sopenharmony_ci 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, 57862306a36Sopenharmony_ci 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, 57962306a36Sopenharmony_ci 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, 58062306a36Sopenharmony_ci 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, 58162306a36Sopenharmony_ci 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, 58262306a36Sopenharmony_ci 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, 58362306a36Sopenharmony_ci 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, 58462306a36Sopenharmony_ci 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic u16 sunxi_nfc_randomizer_step(u16 state, int count) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci state &= 0x7fff; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* 59262306a36Sopenharmony_ci * This loop is just a simple implementation of a Fibonacci LFSR using 59362306a36Sopenharmony_ci * the x16 + x15 + 1 polynomial. 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci while (count--) 59662306a36Sopenharmony_ci state = ((state >> 1) | 59762306a36Sopenharmony_ci (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return state; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic u16 sunxi_nfc_randomizer_state(struct nand_chip *nand, int page, 60362306a36Sopenharmony_ci bool ecc) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 60662306a36Sopenharmony_ci const u16 *seeds = sunxi_nfc_randomizer_page_seeds; 60762306a36Sopenharmony_ci int mod = mtd_div_by_ws(mtd->erasesize, mtd); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) 61062306a36Sopenharmony_ci mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (ecc) { 61362306a36Sopenharmony_ci if (mtd->ecc_step_size == 512) 61462306a36Sopenharmony_ci seeds = sunxi_nfc_randomizer_ecc512_seeds; 61562306a36Sopenharmony_ci else 61662306a36Sopenharmony_ci seeds = sunxi_nfc_randomizer_ecc1024_seeds; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return seeds[page % mod]; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page, 62362306a36Sopenharmony_ci bool ecc) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 62662306a36Sopenharmony_ci u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 62762306a36Sopenharmony_ci u16 state; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (!(nand->options & NAND_NEED_SCRAMBLING)) 63062306a36Sopenharmony_ci return; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 63362306a36Sopenharmony_ci state = sunxi_nfc_randomizer_state(nand, page, ecc); 63462306a36Sopenharmony_ci ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; 63562306a36Sopenharmony_ci writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic void sunxi_nfc_randomizer_enable(struct nand_chip *nand) 63962306a36Sopenharmony_ci{ 64062306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (!(nand->options & NAND_NEED_SCRAMBLING)) 64362306a36Sopenharmony_ci return; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, 64662306a36Sopenharmony_ci nfc->regs + NFC_REG_ECC_CTL); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void sunxi_nfc_randomizer_disable(struct nand_chip *nand) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!(nand->options & NAND_NEED_SCRAMBLING)) 65462306a36Sopenharmony_ci return; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, 65762306a36Sopenharmony_ci nfc->regs + NFC_REG_ECC_CTL); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci u16 state = sunxi_nfc_randomizer_state(nand, page, true); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci bbm[0] ^= state; 66562306a36Sopenharmony_ci bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand, 66962306a36Sopenharmony_ci const uint8_t *buf, int len, 67062306a36Sopenharmony_ci bool ecc, int page) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, ecc); 67362306a36Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 67462306a36Sopenharmony_ci sunxi_nfc_write_buf(nand, buf, len); 67562306a36Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf, 67962306a36Sopenharmony_ci int len, bool ecc, int page) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, ecc); 68262306a36Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 68362306a36Sopenharmony_ci sunxi_nfc_read_buf(nand, buf, len); 68462306a36Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 69062306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci writel(sunxi_nand->ecc.ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci writel(0, nfc->regs + NFC_REG_ECC_CTL); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci buf[0] = user_data; 70562306a36Sopenharmony_ci buf[1] = user_data >> 8; 70662306a36Sopenharmony_ci buf[2] = user_data >> 16; 70762306a36Sopenharmony_ci buf[3] = user_data >> 24; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, 71662306a36Sopenharmony_ci int step, bool bbm, int page) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), 72162306a36Sopenharmony_ci oob); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* De-randomize the Bad Block Marker. */ 72462306a36Sopenharmony_ci if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) 72562306a36Sopenharmony_ci sunxi_nfc_randomize_bbm(nand, page, oob); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, 72962306a36Sopenharmony_ci const u8 *oob, int step, 73062306a36Sopenharmony_ci bool bbm, int page) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 73362306a36Sopenharmony_ci u8 user_data[4]; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Randomize the Bad Block Marker. */ 73662306a36Sopenharmony_ci if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { 73762306a36Sopenharmony_ci memcpy(user_data, oob, sizeof(user_data)); 73862306a36Sopenharmony_ci sunxi_nfc_randomize_bbm(nand, page, user_data); 73962306a36Sopenharmony_ci oob = user_data; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci writel(sunxi_nfc_buf_to_user_data(oob), 74362306a36Sopenharmony_ci nfc->regs + NFC_REG_USER_DATA(step)); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, 74762306a36Sopenharmony_ci unsigned int *max_bitflips, int ret) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (ret < 0) { 75262306a36Sopenharmony_ci mtd->ecc_stats.failed++; 75362306a36Sopenharmony_ci } else { 75462306a36Sopenharmony_ci mtd->ecc_stats.corrected += ret; 75562306a36Sopenharmony_ci *max_bitflips = max_t(unsigned int, *max_bitflips, ret); 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, 76062306a36Sopenharmony_ci int step, u32 status, bool *erased) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 76362306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 76462306a36Sopenharmony_ci u32 tmp; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci *erased = false; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (status & NFC_ECC_ERR(step)) 76962306a36Sopenharmony_ci return -EBADMSG; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (status & NFC_ECC_PAT_FOUND(step)) { 77262306a36Sopenharmony_ci u8 pattern; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { 77562306a36Sopenharmony_ci pattern = 0x0; 77662306a36Sopenharmony_ci } else { 77762306a36Sopenharmony_ci pattern = 0xff; 77862306a36Sopenharmony_ci *erased = true; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (data) 78262306a36Sopenharmony_ci memset(data, pattern, ecc->size); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (oob) 78562306a36Sopenharmony_ci memset(oob, pattern, ecc->bytes + 4); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return NFC_ECC_ERR_CNT(step, tmp); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, 79662306a36Sopenharmony_ci u8 *data, int data_off, 79762306a36Sopenharmony_ci u8 *oob, int oob_off, 79862306a36Sopenharmony_ci int *cur_off, 79962306a36Sopenharmony_ci unsigned int *max_bitflips, 80062306a36Sopenharmony_ci bool bbm, bool oob_required, int page) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 80362306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 80462306a36Sopenharmony_ci int raw_mode = 0; 80562306a36Sopenharmony_ci bool erased; 80662306a36Sopenharmony_ci int ret; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (*cur_off != data_off) 80962306a36Sopenharmony_ci nand_change_read_column_op(nand, data_off, NULL, 0, false); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci sunxi_nfc_randomizer_read_buf(nand, NULL, ecc->size, false, page); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (data_off + ecc->size != oob_off) 81462306a36Sopenharmony_ci nand_change_read_column_op(nand, oob_off, NULL, 0, false); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 81762306a36Sopenharmony_ci if (ret) 81862306a36Sopenharmony_ci return ret; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 82162306a36Sopenharmony_ci writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, 82262306a36Sopenharmony_ci nfc->regs + NFC_REG_CMD); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 82562306a36Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 82662306a36Sopenharmony_ci if (ret) 82762306a36Sopenharmony_ci return ret; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci *cur_off = oob_off + ecc->bytes + 4; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0, 83262306a36Sopenharmony_ci readl(nfc->regs + NFC_REG_ECC_ST), 83362306a36Sopenharmony_ci &erased); 83462306a36Sopenharmony_ci if (erased) 83562306a36Sopenharmony_ci return 1; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (ret < 0) { 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * Re-read the data with the randomizer disabled to identify 84062306a36Sopenharmony_ci * bitflips in erased pages. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) 84362306a36Sopenharmony_ci nand_change_read_column_op(nand, data_off, data, 84462306a36Sopenharmony_ci ecc->size, false); 84562306a36Sopenharmony_ci else 84662306a36Sopenharmony_ci memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, 84762306a36Sopenharmony_ci ecc->size); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, 85062306a36Sopenharmony_ci false); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, ecc->size, 85362306a36Sopenharmony_ci oob, ecc->bytes + 4, 85462306a36Sopenharmony_ci NULL, 0, ecc->strength); 85562306a36Sopenharmony_ci if (ret >= 0) 85662306a36Sopenharmony_ci raw_mode = 1; 85762306a36Sopenharmony_ci } else { 85862306a36Sopenharmony_ci memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (oob_required) { 86162306a36Sopenharmony_ci nand_change_read_column_op(nand, oob_off, NULL, 0, 86262306a36Sopenharmony_ci false); 86362306a36Sopenharmony_ci sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4, 86462306a36Sopenharmony_ci true, page); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0, 86762306a36Sopenharmony_ci bbm, page); 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci sunxi_nfc_hw_ecc_update_stats(nand, max_bitflips, ret); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return raw_mode; 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand, 87762306a36Sopenharmony_ci u8 *oob, int *cur_off, 87862306a36Sopenharmony_ci bool randomize, int page) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 88162306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 88262306a36Sopenharmony_ci int offset = ((ecc->bytes + 4) * ecc->steps); 88362306a36Sopenharmony_ci int len = mtd->oobsize - offset; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (len <= 0) 88662306a36Sopenharmony_ci return; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (!cur_off || *cur_off != offset) 88962306a36Sopenharmony_ci nand_change_read_column_op(nand, mtd->writesize, NULL, 0, 89062306a36Sopenharmony_ci false); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (!randomize) 89362306a36Sopenharmony_ci sunxi_nfc_read_buf(nand, oob + offset, len); 89462306a36Sopenharmony_ci else 89562306a36Sopenharmony_ci sunxi_nfc_randomizer_read_buf(nand, oob + offset, len, 89662306a36Sopenharmony_ci false, page); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (cur_off) 89962306a36Sopenharmony_ci *cur_off = mtd->oobsize + mtd->writesize; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf, 90362306a36Sopenharmony_ci int oob_required, int page, 90462306a36Sopenharmony_ci int nchunks) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci bool randomized = nand->options & NAND_NEED_SCRAMBLING; 90762306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 90862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 90962306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 91062306a36Sopenharmony_ci unsigned int max_bitflips = 0; 91162306a36Sopenharmony_ci int ret, i, raw_mode = 0; 91262306a36Sopenharmony_ci struct scatterlist sg; 91362306a36Sopenharmony_ci u32 status, wait; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 91662306a36Sopenharmony_ci if (ret) 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks, 92062306a36Sopenharmony_ci DMA_FROM_DEVICE, &sg); 92162306a36Sopenharmony_ci if (ret) 92262306a36Sopenharmony_ci return ret; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 92562306a36Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, false); 92662306a36Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | 92962306a36Sopenharmony_ci NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci wait = NFC_CMD_INT_FLAG; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (nfc->caps->has_mdma) 93462306a36Sopenharmony_ci wait |= NFC_DMA_INT_FLAG; 93562306a36Sopenharmony_ci else 93662306a36Sopenharmony_ci dma_async_issue_pending(nfc->dmac); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, 93962306a36Sopenharmony_ci nfc->regs + NFC_REG_CMD); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, wait, false, 0); 94262306a36Sopenharmony_ci if (ret && !nfc->caps->has_mdma) 94362306a36Sopenharmony_ci dmaengine_terminate_all(nfc->dmac); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 94662306a36Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (ret) 95162306a36Sopenharmony_ci return ret; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci status = readl(nfc->regs + NFC_REG_ECC_ST); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci for (i = 0; i < nchunks; i++) { 95662306a36Sopenharmony_ci int data_off = i * ecc->size; 95762306a36Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 95862306a36Sopenharmony_ci u8 *data = buf + data_off; 95962306a36Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 96062306a36Sopenharmony_ci bool erased; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL, 96362306a36Sopenharmony_ci oob_required ? oob : NULL, 96462306a36Sopenharmony_ci i, status, &erased); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* ECC errors are handled in the second loop. */ 96762306a36Sopenharmony_ci if (ret < 0) 96862306a36Sopenharmony_ci continue; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if (oob_required && !erased) { 97162306a36Sopenharmony_ci /* TODO: use DMA to retrieve OOB */ 97262306a36Sopenharmony_ci nand_change_read_column_op(nand, 97362306a36Sopenharmony_ci mtd->writesize + oob_off, 97462306a36Sopenharmony_ci oob, ecc->bytes + 4, false); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, 97762306a36Sopenharmony_ci !i, page); 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (erased) 98162306a36Sopenharmony_ci raw_mode = 1; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (status & NFC_ECC_ERR_MSK) { 98762306a36Sopenharmony_ci for (i = 0; i < nchunks; i++) { 98862306a36Sopenharmony_ci int data_off = i * ecc->size; 98962306a36Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 99062306a36Sopenharmony_ci u8 *data = buf + data_off; 99162306a36Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (!(status & NFC_ECC_ERR(i))) 99462306a36Sopenharmony_ci continue; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* 99762306a36Sopenharmony_ci * Re-read the data with the randomizer disabled to 99862306a36Sopenharmony_ci * identify bitflips in erased pages. 99962306a36Sopenharmony_ci * TODO: use DMA to read page in raw mode 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci if (randomized) 100262306a36Sopenharmony_ci nand_change_read_column_op(nand, data_off, 100362306a36Sopenharmony_ci data, ecc->size, 100462306a36Sopenharmony_ci false); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* TODO: use DMA to retrieve OOB */ 100762306a36Sopenharmony_ci nand_change_read_column_op(nand, 100862306a36Sopenharmony_ci mtd->writesize + oob_off, 100962306a36Sopenharmony_ci oob, ecc->bytes + 4, false); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, ecc->size, 101262306a36Sopenharmony_ci oob, ecc->bytes + 4, 101362306a36Sopenharmony_ci NULL, 0, 101462306a36Sopenharmony_ci ecc->strength); 101562306a36Sopenharmony_ci if (ret >= 0) 101662306a36Sopenharmony_ci raw_mode = 1; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci if (oob_required) 102362306a36Sopenharmony_ci sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, 102462306a36Sopenharmony_ci NULL, !raw_mode, 102562306a36Sopenharmony_ci page); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return max_bitflips; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, 103162306a36Sopenharmony_ci const u8 *data, int data_off, 103262306a36Sopenharmony_ci const u8 *oob, int oob_off, 103362306a36Sopenharmony_ci int *cur_off, bool bbm, 103462306a36Sopenharmony_ci int page) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 103762306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 103862306a36Sopenharmony_ci int ret; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (data_off != *cur_off) 104162306a36Sopenharmony_ci nand_change_write_column_op(nand, data_off, NULL, 0, false); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci sunxi_nfc_randomizer_write_buf(nand, data, ecc->size, false, page); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (data_off + ecc->size != oob_off) 104662306a36Sopenharmony_ci nand_change_write_column_op(nand, oob_off, NULL, 0, false); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 104962306a36Sopenharmony_ci if (ret) 105062306a36Sopenharmony_ci return ret; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 105362306a36Sopenharmony_ci sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | 105662306a36Sopenharmony_ci NFC_ACCESS_DIR | NFC_ECC_OP, 105762306a36Sopenharmony_ci nfc->regs + NFC_REG_CMD); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 106062306a36Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 106162306a36Sopenharmony_ci if (ret) 106262306a36Sopenharmony_ci return ret; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci *cur_off = oob_off + ecc->bytes + 4; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci return 0; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, 107062306a36Sopenharmony_ci u8 *oob, int *cur_off, 107162306a36Sopenharmony_ci int page) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 107462306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 107562306a36Sopenharmony_ci int offset = ((ecc->bytes + 4) * ecc->steps); 107662306a36Sopenharmony_ci int len = mtd->oobsize - offset; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (len <= 0) 107962306a36Sopenharmony_ci return; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (!cur_off || *cur_off != offset) 108262306a36Sopenharmony_ci nand_change_write_column_op(nand, offset + mtd->writesize, 108362306a36Sopenharmony_ci NULL, 0, false); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci sunxi_nfc_randomizer_write_buf(nand, oob + offset, len, false, page); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (cur_off) 108862306a36Sopenharmony_ci *cur_off = mtd->oobsize + mtd->writesize; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, 109262306a36Sopenharmony_ci int oob_required, int page) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 109562306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 109662306a36Sopenharmony_ci unsigned int max_bitflips = 0; 109762306a36Sopenharmony_ci int ret, i, cur_off = 0; 109862306a36Sopenharmony_ci bool raw_mode = false; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 110762306a36Sopenharmony_ci int data_off = i * ecc->size; 110862306a36Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 110962306a36Sopenharmony_ci u8 *data = buf + data_off; 111062306a36Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob, 111362306a36Sopenharmony_ci oob_off + mtd->writesize, 111462306a36Sopenharmony_ci &cur_off, &max_bitflips, 111562306a36Sopenharmony_ci !i, oob_required, page); 111662306a36Sopenharmony_ci if (ret < 0) 111762306a36Sopenharmony_ci return ret; 111862306a36Sopenharmony_ci else if (ret) 111962306a36Sopenharmony_ci raw_mode = true; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (oob_required) 112362306a36Sopenharmony_ci sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, &cur_off, 112462306a36Sopenharmony_ci !raw_mode, page); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci return max_bitflips; 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf, 113262306a36Sopenharmony_ci int oob_required, int page) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci int ret; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page, 114162306a36Sopenharmony_ci nand->ecc.steps); 114262306a36Sopenharmony_ci if (ret >= 0) 114362306a36Sopenharmony_ci return ret; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Fallback to PIO mode */ 114662306a36Sopenharmony_ci return sunxi_nfc_hw_ecc_read_page(nand, buf, oob_required, page); 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, 115062306a36Sopenharmony_ci u32 data_offs, u32 readlen, 115162306a36Sopenharmony_ci u8 *bufpoi, int page) 115262306a36Sopenharmony_ci{ 115362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 115462306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 115562306a36Sopenharmony_ci int ret, i, cur_off = 0; 115662306a36Sopenharmony_ci unsigned int max_bitflips = 0; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci for (i = data_offs / ecc->size; 116562306a36Sopenharmony_ci i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { 116662306a36Sopenharmony_ci int data_off = i * ecc->size; 116762306a36Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 116862306a36Sopenharmony_ci u8 *data = bufpoi + data_off; 116962306a36Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, 117262306a36Sopenharmony_ci oob, 117362306a36Sopenharmony_ci oob_off + mtd->writesize, 117462306a36Sopenharmony_ci &cur_off, &max_bitflips, !i, 117562306a36Sopenharmony_ci false, page); 117662306a36Sopenharmony_ci if (ret < 0) 117762306a36Sopenharmony_ci return ret; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return max_bitflips; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand, 118662306a36Sopenharmony_ci u32 data_offs, u32 readlen, 118762306a36Sopenharmony_ci u8 *buf, int page) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size); 119062306a36Sopenharmony_ci int ret; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks); 119762306a36Sopenharmony_ci if (ret >= 0) 119862306a36Sopenharmony_ci return ret; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci /* Fallback to PIO mode */ 120162306a36Sopenharmony_ci return sunxi_nfc_hw_ecc_read_subpage(nand, data_offs, readlen, 120262306a36Sopenharmony_ci buf, page); 120362306a36Sopenharmony_ci} 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, 120662306a36Sopenharmony_ci const uint8_t *buf, int oob_required, 120762306a36Sopenharmony_ci int page) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 121062306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 121162306a36Sopenharmony_ci int ret, i, cur_off = 0; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci nand_prog_page_begin_op(nand, page, 0, NULL, 0); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 122062306a36Sopenharmony_ci int data_off = i * ecc->size; 122162306a36Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 122262306a36Sopenharmony_ci const u8 *data = buf + data_off; 122362306a36Sopenharmony_ci const u8 *oob = nand->oob_poi + oob_off; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, 122662306a36Sopenharmony_ci oob_off + mtd->writesize, 122762306a36Sopenharmony_ci &cur_off, !i, page); 122862306a36Sopenharmony_ci if (ret) 122962306a36Sopenharmony_ci return ret; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (oob_required || (nand->options & NAND_NEED_SCRAMBLING)) 123362306a36Sopenharmony_ci sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi, 123462306a36Sopenharmony_ci &cur_off, page); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return nand_prog_page_end_op(nand); 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, 124262306a36Sopenharmony_ci u32 data_offs, u32 data_len, 124362306a36Sopenharmony_ci const u8 *buf, int oob_required, 124462306a36Sopenharmony_ci int page) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 124762306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 124862306a36Sopenharmony_ci int ret, i, cur_off = 0; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci nand_prog_page_begin_op(nand, page, 0, NULL, 0); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci for (i = data_offs / ecc->size; 125762306a36Sopenharmony_ci i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { 125862306a36Sopenharmony_ci int data_off = i * ecc->size; 125962306a36Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 126062306a36Sopenharmony_ci const u8 *data = buf + data_off; 126162306a36Sopenharmony_ci const u8 *oob = nand->oob_poi + oob_off; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, 126462306a36Sopenharmony_ci oob_off + mtd->writesize, 126562306a36Sopenharmony_ci &cur_off, !i, page); 126662306a36Sopenharmony_ci if (ret) 126762306a36Sopenharmony_ci return ret; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return nand_prog_page_end_op(nand); 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, 127662306a36Sopenharmony_ci const u8 *buf, 127762306a36Sopenharmony_ci int oob_required, 127862306a36Sopenharmony_ci int page) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 128162306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 128262306a36Sopenharmony_ci struct scatterlist sg; 128362306a36Sopenharmony_ci u32 wait; 128462306a36Sopenharmony_ci int ret, i; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 128962306a36Sopenharmony_ci if (ret) 129062306a36Sopenharmony_ci return ret; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps, 129362306a36Sopenharmony_ci DMA_TO_DEVICE, &sg); 129462306a36Sopenharmony_ci if (ret) 129562306a36Sopenharmony_ci goto pio_fallback; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 129862306a36Sopenharmony_ci const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page); 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci nand_prog_page_begin_op(nand, page, 0, NULL, 0); 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 130662306a36Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, false); 130762306a36Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, 131062306a36Sopenharmony_ci nfc->regs + NFC_REG_WCMD_SET); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci wait = NFC_CMD_INT_FLAG; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci if (nfc->caps->has_mdma) 131562306a36Sopenharmony_ci wait |= NFC_DMA_INT_FLAG; 131662306a36Sopenharmony_ci else 131762306a36Sopenharmony_ci dma_async_issue_pending(nfc->dmac); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | 132062306a36Sopenharmony_ci NFC_DATA_TRANS | NFC_ACCESS_DIR, 132162306a36Sopenharmony_ci nfc->regs + NFC_REG_CMD); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, wait, false, 0); 132462306a36Sopenharmony_ci if (ret && !nfc->caps->has_mdma) 132562306a36Sopenharmony_ci dmaengine_terminate_all(nfc->dmac); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 132862306a36Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (ret) 133362306a36Sopenharmony_ci return ret; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (oob_required || (nand->options & NAND_NEED_SCRAMBLING)) 133662306a36Sopenharmony_ci /* TODO: use DMA to transfer extra OOB bytes ? */ 133762306a36Sopenharmony_ci sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi, 133862306a36Sopenharmony_ci NULL, page); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci return nand_prog_page_end_op(nand); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_cipio_fallback: 134362306a36Sopenharmony_ci return sunxi_nfc_hw_ecc_write_page(nand, buf, oob_required, page); 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci u8 *buf = nand_get_data_buf(nand); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci return nand->ecc.read_page(nand, buf, 1, page); 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 135662306a36Sopenharmony_ci u8 *buf = nand_get_data_buf(nand); 135762306a36Sopenharmony_ci int ret; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci memset(buf, 0xff, mtd->writesize); 136062306a36Sopenharmony_ci ret = nand->ecc.write_page(nand, buf, 1, page); 136162306a36Sopenharmony_ci if (ret) 136262306a36Sopenharmony_ci return ret; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Send command to program the OOB data */ 136562306a36Sopenharmony_ci return nand_prog_page_end_op(nand); 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic const s32 tWB_lut[] = {6, 12, 16, 20}; 136962306a36Sopenharmony_cistatic const s32 tRHW_lut[] = {4, 8, 12, 20}; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, 137262306a36Sopenharmony_ci u32 clk_period) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); 137562306a36Sopenharmony_ci int i; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci for (i = 0; i < lut_size; i++) { 137862306a36Sopenharmony_ci if (clk_cycles <= lut[i]) 137962306a36Sopenharmony_ci return i; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci /* Doesn't fit */ 138362306a36Sopenharmony_ci return -EINVAL; 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci#define sunxi_nand_lookup_timing(l, p, c) \ 138762306a36Sopenharmony_ci _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_cistatic int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, 139062306a36Sopenharmony_ci const struct nand_interface_config *conf) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 139362306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 139462306a36Sopenharmony_ci const struct nand_sdr_timings *timings; 139562306a36Sopenharmony_ci u32 min_clk_period = 0; 139662306a36Sopenharmony_ci s32 tWB, tADL, tWHR, tRHW, tCAD; 139762306a36Sopenharmony_ci long real_clk_rate; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci timings = nand_get_sdr_timings(conf); 140062306a36Sopenharmony_ci if (IS_ERR(timings)) 140162306a36Sopenharmony_ci return -ENOTSUPP; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* T1 <=> tCLS */ 140462306a36Sopenharmony_ci if (timings->tCLS_min > min_clk_period) 140562306a36Sopenharmony_ci min_clk_period = timings->tCLS_min; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* T2 <=> tCLH */ 140862306a36Sopenharmony_ci if (timings->tCLH_min > min_clk_period) 140962306a36Sopenharmony_ci min_clk_period = timings->tCLH_min; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* T3 <=> tCS */ 141262306a36Sopenharmony_ci if (timings->tCS_min > min_clk_period) 141362306a36Sopenharmony_ci min_clk_period = timings->tCS_min; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci /* T4 <=> tCH */ 141662306a36Sopenharmony_ci if (timings->tCH_min > min_clk_period) 141762306a36Sopenharmony_ci min_clk_period = timings->tCH_min; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci /* T5 <=> tWP */ 142062306a36Sopenharmony_ci if (timings->tWP_min > min_clk_period) 142162306a36Sopenharmony_ci min_clk_period = timings->tWP_min; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci /* T6 <=> tWH */ 142462306a36Sopenharmony_ci if (timings->tWH_min > min_clk_period) 142562306a36Sopenharmony_ci min_clk_period = timings->tWH_min; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci /* T7 <=> tALS */ 142862306a36Sopenharmony_ci if (timings->tALS_min > min_clk_period) 142962306a36Sopenharmony_ci min_clk_period = timings->tALS_min; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci /* T8 <=> tDS */ 143262306a36Sopenharmony_ci if (timings->tDS_min > min_clk_period) 143362306a36Sopenharmony_ci min_clk_period = timings->tDS_min; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* T9 <=> tDH */ 143662306a36Sopenharmony_ci if (timings->tDH_min > min_clk_period) 143762306a36Sopenharmony_ci min_clk_period = timings->tDH_min; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci /* T10 <=> tRR */ 144062306a36Sopenharmony_ci if (timings->tRR_min > (min_clk_period * 3)) 144162306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci /* T11 <=> tALH */ 144462306a36Sopenharmony_ci if (timings->tALH_min > min_clk_period) 144562306a36Sopenharmony_ci min_clk_period = timings->tALH_min; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci /* T12 <=> tRP */ 144862306a36Sopenharmony_ci if (timings->tRP_min > min_clk_period) 144962306a36Sopenharmony_ci min_clk_period = timings->tRP_min; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* T13 <=> tREH */ 145262306a36Sopenharmony_ci if (timings->tREH_min > min_clk_period) 145362306a36Sopenharmony_ci min_clk_period = timings->tREH_min; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* T14 <=> tRC */ 145662306a36Sopenharmony_ci if (timings->tRC_min > (min_clk_period * 2)) 145762306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci /* T15 <=> tWC */ 146062306a36Sopenharmony_ci if (timings->tWC_min > (min_clk_period * 2)) 146162306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* T16 - T19 + tCAD */ 146462306a36Sopenharmony_ci if (timings->tWB_max > (min_clk_period * 20)) 146562306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci if (timings->tADL_min > (min_clk_period * 32)) 146862306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci if (timings->tWHR_min > (min_clk_period * 32)) 147162306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci if (timings->tRHW_min > (min_clk_period * 20)) 147462306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* 147762306a36Sopenharmony_ci * In non-EDO, tREA should be less than tRP to guarantee that the 147862306a36Sopenharmony_ci * controller does not sample the IO lines too early. Unfortunately, 147962306a36Sopenharmony_ci * the sunxi NAND controller does not allow us to have different 148062306a36Sopenharmony_ci * values for tRP and tREH (tRP = tREH = tRW / 2). 148162306a36Sopenharmony_ci * 148262306a36Sopenharmony_ci * We have 2 options to overcome this limitation: 148362306a36Sopenharmony_ci * 148462306a36Sopenharmony_ci * 1/ Extend tRC to fulfil the tREA <= tRC / 2 constraint 148562306a36Sopenharmony_ci * 2/ Use EDO mode (only works if timings->tRLOH > 0) 148662306a36Sopenharmony_ci */ 148762306a36Sopenharmony_ci if (timings->tREA_max > min_clk_period && !timings->tRLOH_min) 148862306a36Sopenharmony_ci min_clk_period = timings->tREA_max; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, 149162306a36Sopenharmony_ci min_clk_period); 149262306a36Sopenharmony_ci if (tWB < 0) { 149362306a36Sopenharmony_ci dev_err(nfc->dev, "unsupported tWB\n"); 149462306a36Sopenharmony_ci return tWB; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; 149862306a36Sopenharmony_ci if (tADL > 3) { 149962306a36Sopenharmony_ci dev_err(nfc->dev, "unsupported tADL\n"); 150062306a36Sopenharmony_ci return -EINVAL; 150162306a36Sopenharmony_ci } 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; 150462306a36Sopenharmony_ci if (tWHR > 3) { 150562306a36Sopenharmony_ci dev_err(nfc->dev, "unsupported tWHR\n"); 150662306a36Sopenharmony_ci return -EINVAL; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, 151062306a36Sopenharmony_ci min_clk_period); 151162306a36Sopenharmony_ci if (tRHW < 0) { 151262306a36Sopenharmony_ci dev_err(nfc->dev, "unsupported tRHW\n"); 151362306a36Sopenharmony_ci return tRHW; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 151762306a36Sopenharmony_ci return 0; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci /* 152062306a36Sopenharmony_ci * TODO: according to ONFI specs this value only applies for DDR NAND, 152162306a36Sopenharmony_ci * but Allwinner seems to set this to 0x7. Mimic them for now. 152262306a36Sopenharmony_ci */ 152362306a36Sopenharmony_ci tCAD = 0x7; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ 152662306a36Sopenharmony_ci sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci /* Convert min_clk_period from picoseconds to nanoseconds */ 152962306a36Sopenharmony_ci min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci /* 153262306a36Sopenharmony_ci * Unlike what is stated in Allwinner datasheet, the clk_rate should 153362306a36Sopenharmony_ci * be set to (1 / min_clk_period), and not (2 / min_clk_period). 153462306a36Sopenharmony_ci * This new formula was verified with a scope and validated by 153562306a36Sopenharmony_ci * Allwinner engineers. 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_ci sunxi_nand->clk_rate = NSEC_PER_SEC / min_clk_period; 153862306a36Sopenharmony_ci real_clk_rate = clk_round_rate(nfc->mod_clk, sunxi_nand->clk_rate); 153962306a36Sopenharmony_ci if (real_clk_rate <= 0) { 154062306a36Sopenharmony_ci dev_err(nfc->dev, "Unable to round clk %lu\n", 154162306a36Sopenharmony_ci sunxi_nand->clk_rate); 154262306a36Sopenharmony_ci return -EINVAL; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci sunxi_nand->timing_ctl = 0; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* 154862306a36Sopenharmony_ci * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data 154962306a36Sopenharmony_ci * output cycle timings shall be used if the host drives tRC less than 155062306a36Sopenharmony_ci * 30 ns. We should also use EDO mode if tREA is bigger than tRP. 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_ci min_clk_period = NSEC_PER_SEC / real_clk_rate; 155362306a36Sopenharmony_ci if (min_clk_period * 2 < 30 || min_clk_period * 1000 < timings->tREA_max) 155462306a36Sopenharmony_ci sunxi_nand->timing_ctl = NFC_TIMING_CTL_EDO; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci return 0; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, 156062306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 156162306a36Sopenharmony_ci{ 156262306a36Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 156362306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (section >= ecc->steps) 156662306a36Sopenharmony_ci return -ERANGE; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci oobregion->offset = section * (ecc->bytes + 4) + 4; 156962306a36Sopenharmony_ci oobregion->length = ecc->bytes; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return 0; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_cistatic int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, 157562306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 157862306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (section > ecc->steps) 158162306a36Sopenharmony_ci return -ERANGE; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci /* 158462306a36Sopenharmony_ci * The first 2 bytes are used for BB markers, hence we 158562306a36Sopenharmony_ci * only have 2 bytes available in the first user data 158662306a36Sopenharmony_ci * section. 158762306a36Sopenharmony_ci */ 158862306a36Sopenharmony_ci if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { 158962306a36Sopenharmony_ci oobregion->offset = 2; 159062306a36Sopenharmony_ci oobregion->length = 2; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci return 0; 159362306a36Sopenharmony_ci } 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci /* 159662306a36Sopenharmony_ci * The controller does not provide access to OOB bytes 159762306a36Sopenharmony_ci * past the end of the ECC data. 159862306a36Sopenharmony_ci */ 159962306a36Sopenharmony_ci if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) 160062306a36Sopenharmony_ci return -ERANGE; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci oobregion->offset = section * (ecc->bytes + 4); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (section < ecc->steps) 160562306a36Sopenharmony_ci oobregion->length = 4; 160662306a36Sopenharmony_ci else 160762306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return 0; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { 161362306a36Sopenharmony_ci .ecc = sunxi_nand_ooblayout_ecc, 161462306a36Sopenharmony_ci .free = sunxi_nand_ooblayout_free, 161562306a36Sopenharmony_ci}; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_cistatic int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, 161862306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc, 161962306a36Sopenharmony_ci struct device_node *np) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; 162262306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 162362306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 162462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 162562306a36Sopenharmony_ci struct nand_device *nanddev = mtd_to_nanddev(mtd); 162662306a36Sopenharmony_ci int nsectors; 162762306a36Sopenharmony_ci int i; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { 163062306a36Sopenharmony_ci int bytes; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci ecc->size = 1024; 163362306a36Sopenharmony_ci nsectors = mtd->writesize / ecc->size; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci /* Reserve 2 bytes for the BBM */ 163662306a36Sopenharmony_ci bytes = (mtd->oobsize - 2) / nsectors; 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci /* 4 non-ECC bytes are added before each ECC bytes section */ 163962306a36Sopenharmony_ci bytes -= 4; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci /* and bytes has to be even. */ 164262306a36Sopenharmony_ci if (bytes % 2) 164362306a36Sopenharmony_ci bytes--; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci ecc->strength = bytes * 8 / fls(8 * ecc->size); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(strengths); i++) { 164862306a36Sopenharmony_ci if (strengths[i] > ecc->strength) 164962306a36Sopenharmony_ci break; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!i) 165362306a36Sopenharmony_ci ecc->strength = 0; 165462306a36Sopenharmony_ci else 165562306a36Sopenharmony_ci ecc->strength = strengths[i - 1]; 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci if (ecc->size != 512 && ecc->size != 1024) 165962306a36Sopenharmony_ci return -EINVAL; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci /* Prefer 1k ECC chunk over 512 ones */ 166262306a36Sopenharmony_ci if (ecc->size == 512 && mtd->writesize > 512) { 166362306a36Sopenharmony_ci ecc->size = 1024; 166462306a36Sopenharmony_ci ecc->strength *= 2; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci /* Add ECC info retrieval from DT */ 166862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(strengths); i++) { 166962306a36Sopenharmony_ci if (ecc->strength <= strengths[i]) { 167062306a36Sopenharmony_ci /* 167162306a36Sopenharmony_ci * Update ecc->strength value with the actual strength 167262306a36Sopenharmony_ci * that will be used by the ECC engine. 167362306a36Sopenharmony_ci */ 167462306a36Sopenharmony_ci ecc->strength = strengths[i]; 167562306a36Sopenharmony_ci break; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci if (i >= ARRAY_SIZE(strengths)) { 168062306a36Sopenharmony_ci dev_err(nfc->dev, "unsupported strength\n"); 168162306a36Sopenharmony_ci return -ENOTSUPP; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* HW ECC always request ECC bytes for 1024 bytes blocks */ 168562306a36Sopenharmony_ci ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci /* HW ECC always work with even numbers of ECC bytes */ 168862306a36Sopenharmony_ci ecc->bytes = ALIGN(ecc->bytes, 2); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci nsectors = mtd->writesize / ecc->size; 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) 169362306a36Sopenharmony_ci return -EINVAL; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; 169662306a36Sopenharmony_ci ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; 169762306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci if (nfc->dmac || nfc->caps->has_mdma) { 170062306a36Sopenharmony_ci ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; 170162306a36Sopenharmony_ci ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; 170262306a36Sopenharmony_ci ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; 170362306a36Sopenharmony_ci nand->options |= NAND_USES_DMA; 170462306a36Sopenharmony_ci } else { 170562306a36Sopenharmony_ci ecc->read_page = sunxi_nfc_hw_ecc_read_page; 170662306a36Sopenharmony_ci ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; 170762306a36Sopenharmony_ci ecc->write_page = sunxi_nfc_hw_ecc_write_page; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci /* TODO: support DMA for raw accesses and subpage write */ 171162306a36Sopenharmony_ci ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; 171262306a36Sopenharmony_ci ecc->read_oob_raw = nand_read_oob_std; 171362306a36Sopenharmony_ci ecc->write_oob_raw = nand_write_oob_std; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION | 171662306a36Sopenharmony_ci NFC_ECC_PIPELINE | NFC_ECC_EN; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (ecc->size == 512) 171962306a36Sopenharmony_ci sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci return 0; 172262306a36Sopenharmony_ci} 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_cistatic int sunxi_nand_attach_chip(struct nand_chip *nand) 172562306a36Sopenharmony_ci{ 172662306a36Sopenharmony_ci const struct nand_ecc_props *requirements = 172762306a36Sopenharmony_ci nanddev_get_ecc_requirements(&nand->base); 172862306a36Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 172962306a36Sopenharmony_ci struct device_node *np = nand_get_flash_node(nand); 173062306a36Sopenharmony_ci int ret; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci if (nand->bbt_options & NAND_BBT_USE_FLASH) 173362306a36Sopenharmony_ci nand->bbt_options |= NAND_BBT_NO_OOB; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) 173662306a36Sopenharmony_ci nand->options |= NAND_NO_SUBPAGE_WRITE; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci nand->options |= NAND_SUBPAGE_READ; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (!ecc->size) { 174162306a36Sopenharmony_ci ecc->size = requirements->step_size; 174262306a36Sopenharmony_ci ecc->strength = requirements->strength; 174362306a36Sopenharmony_ci } 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci if (!ecc->size || !ecc->strength) 174662306a36Sopenharmony_ci return -EINVAL; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci switch (ecc->engine_type) { 174962306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 175062306a36Sopenharmony_ci ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np); 175162306a36Sopenharmony_ci if (ret) 175262306a36Sopenharmony_ci return ret; 175362306a36Sopenharmony_ci break; 175462306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 175562306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 175662306a36Sopenharmony_ci break; 175762306a36Sopenharmony_ci default: 175862306a36Sopenharmony_ci return -EINVAL; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci return 0; 176262306a36Sopenharmony_ci} 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_cistatic int sunxi_nfc_exec_subop(struct nand_chip *nand, 176562306a36Sopenharmony_ci const struct nand_subop *subop) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 176862306a36Sopenharmony_ci u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { }; 176962306a36Sopenharmony_ci unsigned int i, j, remaining, start; 177062306a36Sopenharmony_ci void *inbuf = NULL; 177162306a36Sopenharmony_ci int ret; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci for (i = 0; i < subop->ninstrs; i++) { 177462306a36Sopenharmony_ci const struct nand_op_instr *instr = &subop->instrs[i]; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci switch (instr->type) { 177762306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 177862306a36Sopenharmony_ci if (cmd & NFC_SEND_CMD1) { 177962306a36Sopenharmony_ci if (WARN_ON(cmd & NFC_SEND_CMD2)) 178062306a36Sopenharmony_ci return -EINVAL; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci cmd |= NFC_SEND_CMD2; 178362306a36Sopenharmony_ci extcmd |= instr->ctx.cmd.opcode; 178462306a36Sopenharmony_ci } else { 178562306a36Sopenharmony_ci cmd |= NFC_SEND_CMD1 | 178662306a36Sopenharmony_ci NFC_CMD(instr->ctx.cmd.opcode); 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci break; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 179162306a36Sopenharmony_ci remaining = nand_subop_get_num_addr_cyc(subop, i); 179262306a36Sopenharmony_ci start = nand_subop_get_addr_start_off(subop, i); 179362306a36Sopenharmony_ci for (j = 0; j < 8 && j + start < remaining; j++) { 179462306a36Sopenharmony_ci u32 addr = instr->ctx.addr.addrs[j + start]; 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci addrs[j / 4] |= addr << (j % 4) * 8; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci if (j) 180062306a36Sopenharmony_ci cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci break; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 180562306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 180662306a36Sopenharmony_ci start = nand_subop_get_data_start_off(subop, i); 180762306a36Sopenharmony_ci remaining = nand_subop_get_data_len(subop, i); 180862306a36Sopenharmony_ci cnt = min_t(u32, remaining, NFC_SRAM_SIZE); 180962306a36Sopenharmony_ci cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci if (instr->type == NAND_OP_DATA_OUT_INSTR) { 181262306a36Sopenharmony_ci cmd |= NFC_ACCESS_DIR; 181362306a36Sopenharmony_ci memcpy_toio(nfc->regs + NFC_RAM0_BASE, 181462306a36Sopenharmony_ci instr->ctx.data.buf.out + start, 181562306a36Sopenharmony_ci cnt); 181662306a36Sopenharmony_ci } else { 181762306a36Sopenharmony_ci inbuf = instr->ctx.data.buf.in + start; 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci break; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 182362306a36Sopenharmony_ci cmd |= NFC_WAIT_FLAG; 182462306a36Sopenharmony_ci break; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 182962306a36Sopenharmony_ci if (ret) 183062306a36Sopenharmony_ci return ret; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci if (cmd & NFC_SEND_ADR) { 183362306a36Sopenharmony_ci writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW); 183462306a36Sopenharmony_ci writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH); 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci if (cmd & NFC_SEND_CMD2) 183862306a36Sopenharmony_ci writel(extcmd, 183962306a36Sopenharmony_ci nfc->regs + 184062306a36Sopenharmony_ci (cmd & NFC_ACCESS_DIR ? 184162306a36Sopenharmony_ci NFC_REG_WCMD_SET : NFC_REG_RCMD_SET)); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (cmd & NFC_DATA_TRANS) 184462306a36Sopenharmony_ci writel(cnt, nfc->regs + NFC_REG_CNT); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci writel(cmd, nfc->regs + NFC_REG_CMD); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, 184962306a36Sopenharmony_ci !(cmd & NFC_WAIT_FLAG) && cnt < 64, 185062306a36Sopenharmony_ci 0); 185162306a36Sopenharmony_ci if (ret) 185262306a36Sopenharmony_ci return ret; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci if (inbuf) 185562306a36Sopenharmony_ci memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci return 0; 185862306a36Sopenharmony_ci} 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_cistatic int sunxi_nfc_soft_waitrdy(struct nand_chip *nand, 186162306a36Sopenharmony_ci const struct nand_subop *subop) 186262306a36Sopenharmony_ci{ 186362306a36Sopenharmony_ci return nand_soft_waitrdy(nand, 186462306a36Sopenharmony_ci subop->instrs[0].ctx.waitrdy.timeout_ms); 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER( 186862306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 186962306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 187062306a36Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 187162306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 187262306a36Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), 187362306a36Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)), 187462306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 187562306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 187662306a36Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 187762306a36Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024), 187862306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 187962306a36Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), 188062306a36Sopenharmony_ci); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER( 188362306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 188462306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 188562306a36Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 188662306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 188762306a36Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)), 188862306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 188962306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 189062306a36Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 189162306a36Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024), 189262306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true)), 189362306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy, 189462306a36Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 189562306a36Sopenharmony_ci); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_cistatic int sunxi_nfc_exec_op(struct nand_chip *nand, 189862306a36Sopenharmony_ci const struct nand_operation *op, bool check_only) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 190162306a36Sopenharmony_ci const struct nand_op_parser *parser; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci if (!check_only) 190462306a36Sopenharmony_ci sunxi_nfc_select_chip(nand, op->cs); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci if (sunxi_nand->sels[op->cs].rb >= 0) 190762306a36Sopenharmony_ci parser = &sunxi_nfc_op_parser; 190862306a36Sopenharmony_ci else 190962306a36Sopenharmony_ci parser = &sunxi_nfc_norb_op_parser; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci return nand_op_parser_exec_op(nand, parser, op, check_only); 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic const struct nand_controller_ops sunxi_nand_controller_ops = { 191562306a36Sopenharmony_ci .attach_chip = sunxi_nand_attach_chip, 191662306a36Sopenharmony_ci .setup_interface = sunxi_nfc_setup_interface, 191762306a36Sopenharmony_ci .exec_op = sunxi_nfc_exec_op, 191862306a36Sopenharmony_ci}; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand; 192362306a36Sopenharmony_ci struct nand_chip *chip; 192462306a36Sopenharmony_ci int ret; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci while (!list_empty(&nfc->chips)) { 192762306a36Sopenharmony_ci sunxi_nand = list_first_entry(&nfc->chips, 192862306a36Sopenharmony_ci struct sunxi_nand_chip, 192962306a36Sopenharmony_ci node); 193062306a36Sopenharmony_ci chip = &sunxi_nand->nand; 193162306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 193262306a36Sopenharmony_ci WARN_ON(ret); 193362306a36Sopenharmony_ci nand_cleanup(chip); 193462306a36Sopenharmony_ci list_del(&sunxi_nand->node); 193562306a36Sopenharmony_ci } 193662306a36Sopenharmony_ci} 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_cistatic int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, 193962306a36Sopenharmony_ci struct device_node *np) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand; 194262306a36Sopenharmony_ci struct mtd_info *mtd; 194362306a36Sopenharmony_ci struct nand_chip *nand; 194462306a36Sopenharmony_ci int nsels; 194562306a36Sopenharmony_ci int ret; 194662306a36Sopenharmony_ci int i; 194762306a36Sopenharmony_ci u32 tmp; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (!of_get_property(np, "reg", &nsels)) 195062306a36Sopenharmony_ci return -EINVAL; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci nsels /= sizeof(u32); 195362306a36Sopenharmony_ci if (!nsels) { 195462306a36Sopenharmony_ci dev_err(dev, "invalid reg property size\n"); 195562306a36Sopenharmony_ci return -EINVAL; 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels), 195962306a36Sopenharmony_ci GFP_KERNEL); 196062306a36Sopenharmony_ci if (!sunxi_nand) 196162306a36Sopenharmony_ci return -ENOMEM; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci sunxi_nand->nsels = nsels; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci for (i = 0; i < nsels; i++) { 196662306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "reg", i, &tmp); 196762306a36Sopenharmony_ci if (ret) { 196862306a36Sopenharmony_ci dev_err(dev, "could not retrieve reg property: %d\n", 196962306a36Sopenharmony_ci ret); 197062306a36Sopenharmony_ci return ret; 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci if (tmp > NFC_MAX_CS) { 197462306a36Sopenharmony_ci dev_err(dev, 197562306a36Sopenharmony_ci "invalid reg value: %u (max CS = 7)\n", 197662306a36Sopenharmony_ci tmp); 197762306a36Sopenharmony_ci return -EINVAL; 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci if (test_and_set_bit(tmp, &nfc->assigned_cs)) { 198162306a36Sopenharmony_ci dev_err(dev, "CS %d already assigned\n", tmp); 198262306a36Sopenharmony_ci return -EINVAL; 198362306a36Sopenharmony_ci } 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci sunxi_nand->sels[i].cs = tmp; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && 198862306a36Sopenharmony_ci tmp < 2) 198962306a36Sopenharmony_ci sunxi_nand->sels[i].rb = tmp; 199062306a36Sopenharmony_ci else 199162306a36Sopenharmony_ci sunxi_nand->sels[i].rb = -1; 199262306a36Sopenharmony_ci } 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci nand = &sunxi_nand->nand; 199562306a36Sopenharmony_ci /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ 199662306a36Sopenharmony_ci nand->controller = &nfc->controller; 199762306a36Sopenharmony_ci nand->controller->ops = &sunxi_nand_controller_ops; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci /* 200062306a36Sopenharmony_ci * Set the ECC mode to the default value in case nothing is specified 200162306a36Sopenharmony_ci * in the DT. 200262306a36Sopenharmony_ci */ 200362306a36Sopenharmony_ci nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 200462306a36Sopenharmony_ci nand_set_flash_node(nand, np); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci mtd = nand_to_mtd(nand); 200762306a36Sopenharmony_ci mtd->dev.parent = dev; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci ret = nand_scan(nand, nsels); 201062306a36Sopenharmony_ci if (ret) 201162306a36Sopenharmony_ci return ret; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 201462306a36Sopenharmony_ci if (ret) { 201562306a36Sopenharmony_ci dev_err(dev, "failed to register mtd device: %d\n", ret); 201662306a36Sopenharmony_ci nand_cleanup(nand); 201762306a36Sopenharmony_ci return ret; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci list_add_tail(&sunxi_nand->node, &nfc->chips); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci return 0; 202362306a36Sopenharmony_ci} 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cistatic int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) 202662306a36Sopenharmony_ci{ 202762306a36Sopenharmony_ci struct device_node *np = dev->of_node; 202862306a36Sopenharmony_ci struct device_node *nand_np; 202962306a36Sopenharmony_ci int ret; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci for_each_child_of_node(np, nand_np) { 203262306a36Sopenharmony_ci ret = sunxi_nand_chip_init(dev, nfc, nand_np); 203362306a36Sopenharmony_ci if (ret) { 203462306a36Sopenharmony_ci of_node_put(nand_np); 203562306a36Sopenharmony_ci sunxi_nand_chips_cleanup(nfc); 203662306a36Sopenharmony_ci return ret; 203762306a36Sopenharmony_ci } 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci return 0; 204162306a36Sopenharmony_ci} 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_cistatic int sunxi_nfc_dma_init(struct sunxi_nfc *nfc, struct resource *r) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci int ret; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci if (nfc->caps->has_mdma) 204862306a36Sopenharmony_ci return 0; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci nfc->dmac = dma_request_chan(nfc->dev, "rxtx"); 205162306a36Sopenharmony_ci if (IS_ERR(nfc->dmac)) { 205262306a36Sopenharmony_ci ret = PTR_ERR(nfc->dmac); 205362306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 205462306a36Sopenharmony_ci return ret; 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* Ignore errors to fall back to PIO mode */ 205762306a36Sopenharmony_ci dev_warn(nfc->dev, "failed to request rxtx DMA channel: %d\n", ret); 205862306a36Sopenharmony_ci nfc->dmac = NULL; 205962306a36Sopenharmony_ci } else { 206062306a36Sopenharmony_ci struct dma_slave_config dmac_cfg = { }; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; 206362306a36Sopenharmony_ci dmac_cfg.dst_addr = dmac_cfg.src_addr; 206462306a36Sopenharmony_ci dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 206562306a36Sopenharmony_ci dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; 206662306a36Sopenharmony_ci dmac_cfg.src_maxburst = nfc->caps->dma_maxburst; 206762306a36Sopenharmony_ci dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst; 206862306a36Sopenharmony_ci dmaengine_slave_config(nfc->dmac, &dmac_cfg); 206962306a36Sopenharmony_ci } 207062306a36Sopenharmony_ci return 0; 207162306a36Sopenharmony_ci} 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_cistatic int sunxi_nfc_probe(struct platform_device *pdev) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 207662306a36Sopenharmony_ci struct resource *r; 207762306a36Sopenharmony_ci struct sunxi_nfc *nfc; 207862306a36Sopenharmony_ci int irq; 207962306a36Sopenharmony_ci int ret; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); 208262306a36Sopenharmony_ci if (!nfc) 208362306a36Sopenharmony_ci return -ENOMEM; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci nfc->dev = dev; 208662306a36Sopenharmony_ci nand_controller_init(&nfc->controller); 208762306a36Sopenharmony_ci INIT_LIST_HEAD(&nfc->chips); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci nfc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); 209062306a36Sopenharmony_ci if (IS_ERR(nfc->regs)) 209162306a36Sopenharmony_ci return PTR_ERR(nfc->regs); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 209462306a36Sopenharmony_ci if (irq < 0) 209562306a36Sopenharmony_ci return irq; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb"); 209862306a36Sopenharmony_ci if (IS_ERR(nfc->ahb_clk)) { 209962306a36Sopenharmony_ci dev_err(dev, "failed to retrieve ahb clk\n"); 210062306a36Sopenharmony_ci return PTR_ERR(nfc->ahb_clk); 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci nfc->mod_clk = devm_clk_get_enabled(dev, "mod"); 210462306a36Sopenharmony_ci if (IS_ERR(nfc->mod_clk)) { 210562306a36Sopenharmony_ci dev_err(dev, "failed to retrieve mod clk\n"); 210662306a36Sopenharmony_ci return PTR_ERR(nfc->mod_clk); 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); 211062306a36Sopenharmony_ci if (IS_ERR(nfc->reset)) 211162306a36Sopenharmony_ci return PTR_ERR(nfc->reset); 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci ret = reset_control_deassert(nfc->reset); 211462306a36Sopenharmony_ci if (ret) { 211562306a36Sopenharmony_ci dev_err(dev, "reset err %d\n", ret); 211662306a36Sopenharmony_ci return ret; 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci nfc->caps = of_device_get_match_data(&pdev->dev); 212062306a36Sopenharmony_ci if (!nfc->caps) { 212162306a36Sopenharmony_ci ret = -EINVAL; 212262306a36Sopenharmony_ci goto out_ahb_reset_reassert; 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci ret = sunxi_nfc_rst(nfc); 212662306a36Sopenharmony_ci if (ret) 212762306a36Sopenharmony_ci goto out_ahb_reset_reassert; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci writel(0, nfc->regs + NFC_REG_INT); 213062306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, 213162306a36Sopenharmony_ci 0, "sunxi-nand", nfc); 213262306a36Sopenharmony_ci if (ret) 213362306a36Sopenharmony_ci goto out_ahb_reset_reassert; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci ret = sunxi_nfc_dma_init(nfc, r); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci if (ret) 213862306a36Sopenharmony_ci goto out_ahb_reset_reassert; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci platform_set_drvdata(pdev, nfc); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci ret = sunxi_nand_chips_init(dev, nfc); 214362306a36Sopenharmony_ci if (ret) { 214462306a36Sopenharmony_ci dev_err(dev, "failed to init nand chips\n"); 214562306a36Sopenharmony_ci goto out_release_dmac; 214662306a36Sopenharmony_ci } 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci return 0; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ciout_release_dmac: 215162306a36Sopenharmony_ci if (nfc->dmac) 215262306a36Sopenharmony_ci dma_release_channel(nfc->dmac); 215362306a36Sopenharmony_ciout_ahb_reset_reassert: 215462306a36Sopenharmony_ci reset_control_assert(nfc->reset); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci return ret; 215762306a36Sopenharmony_ci} 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_cistatic void sunxi_nfc_remove(struct platform_device *pdev) 216062306a36Sopenharmony_ci{ 216162306a36Sopenharmony_ci struct sunxi_nfc *nfc = platform_get_drvdata(pdev); 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci sunxi_nand_chips_cleanup(nfc); 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci reset_control_assert(nfc->reset); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci if (nfc->dmac) 216862306a36Sopenharmony_ci dma_release_channel(nfc->dmac); 216962306a36Sopenharmony_ci} 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_cistatic const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { 217262306a36Sopenharmony_ci .reg_io_data = NFC_REG_A10_IO_DATA, 217362306a36Sopenharmony_ci .dma_maxburst = 4, 217462306a36Sopenharmony_ci}; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_cistatic const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { 217762306a36Sopenharmony_ci .has_mdma = true, 217862306a36Sopenharmony_ci .reg_io_data = NFC_REG_A23_IO_DATA, 217962306a36Sopenharmony_ci .dma_maxburst = 8, 218062306a36Sopenharmony_ci}; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_cistatic const struct of_device_id sunxi_nfc_ids[] = { 218362306a36Sopenharmony_ci { 218462306a36Sopenharmony_ci .compatible = "allwinner,sun4i-a10-nand", 218562306a36Sopenharmony_ci .data = &sunxi_nfc_a10_caps, 218662306a36Sopenharmony_ci }, 218762306a36Sopenharmony_ci { 218862306a36Sopenharmony_ci .compatible = "allwinner,sun8i-a23-nand-controller", 218962306a36Sopenharmony_ci .data = &sunxi_nfc_a23_caps, 219062306a36Sopenharmony_ci }, 219162306a36Sopenharmony_ci { /* sentinel */ } 219262306a36Sopenharmony_ci}; 219362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sunxi_nfc_ids); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_cistatic struct platform_driver sunxi_nfc_driver = { 219662306a36Sopenharmony_ci .driver = { 219762306a36Sopenharmony_ci .name = "sunxi_nand", 219862306a36Sopenharmony_ci .of_match_table = sunxi_nfc_ids, 219962306a36Sopenharmony_ci }, 220062306a36Sopenharmony_ci .probe = sunxi_nfc_probe, 220162306a36Sopenharmony_ci .remove_new = sunxi_nfc_remove, 220262306a36Sopenharmony_ci}; 220362306a36Sopenharmony_cimodule_platform_driver(sunxi_nfc_driver); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 220662306a36Sopenharmony_ciMODULE_AUTHOR("Boris BREZILLON"); 220762306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); 220862306a36Sopenharmony_ciMODULE_ALIAS("platform:sunxi_nand"); 2209