18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Derived from: 68c2ecf20Sopenharmony_ci * https://github.com/yuq/sunxi-nfc-mtd 78c2ecf20Sopenharmony_ci * Copyright (C) 2013 Qiang Yu <yuq825@gmail.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * https://github.com/hno/Allwinner-Info 108c2ecf20Sopenharmony_ci * Copyright (C) 2013 Henrik Nordström <Henrik Nordström> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com> 138c2ecf20Sopenharmony_ci * Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org> 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/of_device.h> 238c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 248c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 258c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 268c2ecf20Sopenharmony_ci#include <linux/clk.h> 278c2ecf20Sopenharmony_ci#include <linux/delay.h> 288c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 298c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 308c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 318c2ecf20Sopenharmony_ci#include <linux/reset.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define NFC_REG_CTL 0x0000 348c2ecf20Sopenharmony_ci#define NFC_REG_ST 0x0004 358c2ecf20Sopenharmony_ci#define NFC_REG_INT 0x0008 368c2ecf20Sopenharmony_ci#define NFC_REG_TIMING_CTL 0x000C 378c2ecf20Sopenharmony_ci#define NFC_REG_TIMING_CFG 0x0010 388c2ecf20Sopenharmony_ci#define NFC_REG_ADDR_LOW 0x0014 398c2ecf20Sopenharmony_ci#define NFC_REG_ADDR_HIGH 0x0018 408c2ecf20Sopenharmony_ci#define NFC_REG_SECTOR_NUM 0x001C 418c2ecf20Sopenharmony_ci#define NFC_REG_CNT 0x0020 428c2ecf20Sopenharmony_ci#define NFC_REG_CMD 0x0024 438c2ecf20Sopenharmony_ci#define NFC_REG_RCMD_SET 0x0028 448c2ecf20Sopenharmony_ci#define NFC_REG_WCMD_SET 0x002C 458c2ecf20Sopenharmony_ci#define NFC_REG_A10_IO_DATA 0x0030 468c2ecf20Sopenharmony_ci#define NFC_REG_A23_IO_DATA 0x0300 478c2ecf20Sopenharmony_ci#define NFC_REG_ECC_CTL 0x0034 488c2ecf20Sopenharmony_ci#define NFC_REG_ECC_ST 0x0038 498c2ecf20Sopenharmony_ci#define NFC_REG_DEBUG 0x003C 508c2ecf20Sopenharmony_ci#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3) 518c2ecf20Sopenharmony_ci#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4)) 528c2ecf20Sopenharmony_ci#define NFC_REG_SPARE_AREA 0x00A0 538c2ecf20Sopenharmony_ci#define NFC_REG_PAT_ID 0x00A4 548c2ecf20Sopenharmony_ci#define NFC_REG_MDMA_CNT 0x00C4 558c2ecf20Sopenharmony_ci#define NFC_RAM0_BASE 0x0400 568c2ecf20Sopenharmony_ci#define NFC_RAM1_BASE 0x0800 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* define bit use in NFC_CTL */ 598c2ecf20Sopenharmony_ci#define NFC_EN BIT(0) 608c2ecf20Sopenharmony_ci#define NFC_RESET BIT(1) 618c2ecf20Sopenharmony_ci#define NFC_BUS_WIDTH_MSK BIT(2) 628c2ecf20Sopenharmony_ci#define NFC_BUS_WIDTH_8 (0 << 2) 638c2ecf20Sopenharmony_ci#define NFC_BUS_WIDTH_16 (1 << 2) 648c2ecf20Sopenharmony_ci#define NFC_RB_SEL_MSK BIT(3) 658c2ecf20Sopenharmony_ci#define NFC_RB_SEL(x) ((x) << 3) 668c2ecf20Sopenharmony_ci#define NFC_CE_SEL_MSK GENMASK(26, 24) 678c2ecf20Sopenharmony_ci#define NFC_CE_SEL(x) ((x) << 24) 688c2ecf20Sopenharmony_ci#define NFC_CE_CTL BIT(6) 698c2ecf20Sopenharmony_ci#define NFC_PAGE_SHIFT_MSK GENMASK(11, 8) 708c2ecf20Sopenharmony_ci#define NFC_PAGE_SHIFT(x) (((x) < 10 ? 0 : (x) - 10) << 8) 718c2ecf20Sopenharmony_ci#define NFC_SAM BIT(12) 728c2ecf20Sopenharmony_ci#define NFC_RAM_METHOD BIT(14) 738c2ecf20Sopenharmony_ci#define NFC_DMA_TYPE_NORMAL BIT(15) 748c2ecf20Sopenharmony_ci#define NFC_DEBUG_CTL BIT(31) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* define bit use in NFC_ST */ 778c2ecf20Sopenharmony_ci#define NFC_RB_B2R BIT(0) 788c2ecf20Sopenharmony_ci#define NFC_CMD_INT_FLAG BIT(1) 798c2ecf20Sopenharmony_ci#define NFC_DMA_INT_FLAG BIT(2) 808c2ecf20Sopenharmony_ci#define NFC_CMD_FIFO_STATUS BIT(3) 818c2ecf20Sopenharmony_ci#define NFC_STA BIT(4) 828c2ecf20Sopenharmony_ci#define NFC_NATCH_INT_FLAG BIT(5) 838c2ecf20Sopenharmony_ci#define NFC_RB_STATE(x) BIT(x + 8) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* define bit use in NFC_INT */ 868c2ecf20Sopenharmony_ci#define NFC_B2R_INT_ENABLE BIT(0) 878c2ecf20Sopenharmony_ci#define NFC_CMD_INT_ENABLE BIT(1) 888c2ecf20Sopenharmony_ci#define NFC_DMA_INT_ENABLE BIT(2) 898c2ecf20Sopenharmony_ci#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \ 908c2ecf20Sopenharmony_ci NFC_CMD_INT_ENABLE | \ 918c2ecf20Sopenharmony_ci NFC_DMA_INT_ENABLE) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* define bit use in NFC_TIMING_CTL */ 948c2ecf20Sopenharmony_ci#define NFC_TIMING_CTL_EDO BIT(8) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* define NFC_TIMING_CFG register layout */ 978c2ecf20Sopenharmony_ci#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \ 988c2ecf20Sopenharmony_ci (((tWB) & 0x3) | (((tADL) & 0x3) << 2) | \ 998c2ecf20Sopenharmony_ci (((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \ 1008c2ecf20Sopenharmony_ci (((tCAD) & 0x7) << 8)) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* define bit use in NFC_CMD */ 1038c2ecf20Sopenharmony_ci#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0) 1048c2ecf20Sopenharmony_ci#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) 1058c2ecf20Sopenharmony_ci#define NFC_CMD(x) (x) 1068c2ecf20Sopenharmony_ci#define NFC_ADR_NUM_MSK GENMASK(18, 16) 1078c2ecf20Sopenharmony_ci#define NFC_ADR_NUM(x) (((x) - 1) << 16) 1088c2ecf20Sopenharmony_ci#define NFC_SEND_ADR BIT(19) 1098c2ecf20Sopenharmony_ci#define NFC_ACCESS_DIR BIT(20) 1108c2ecf20Sopenharmony_ci#define NFC_DATA_TRANS BIT(21) 1118c2ecf20Sopenharmony_ci#define NFC_SEND_CMD1 BIT(22) 1128c2ecf20Sopenharmony_ci#define NFC_WAIT_FLAG BIT(23) 1138c2ecf20Sopenharmony_ci#define NFC_SEND_CMD2 BIT(24) 1148c2ecf20Sopenharmony_ci#define NFC_SEQ BIT(25) 1158c2ecf20Sopenharmony_ci#define NFC_DATA_SWAP_METHOD BIT(26) 1168c2ecf20Sopenharmony_ci#define NFC_ROW_AUTO_INC BIT(27) 1178c2ecf20Sopenharmony_ci#define NFC_SEND_CMD3 BIT(28) 1188c2ecf20Sopenharmony_ci#define NFC_SEND_CMD4 BIT(29) 1198c2ecf20Sopenharmony_ci#define NFC_CMD_TYPE_MSK GENMASK(31, 30) 1208c2ecf20Sopenharmony_ci#define NFC_NORMAL_OP (0 << 30) 1218c2ecf20Sopenharmony_ci#define NFC_ECC_OP (1 << 30) 1228c2ecf20Sopenharmony_ci#define NFC_PAGE_OP (2U << 30) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* define bit use in NFC_RCMD_SET */ 1258c2ecf20Sopenharmony_ci#define NFC_READ_CMD_MSK GENMASK(7, 0) 1268c2ecf20Sopenharmony_ci#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8) 1278c2ecf20Sopenharmony_ci#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* define bit use in NFC_WCMD_SET */ 1308c2ecf20Sopenharmony_ci#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0) 1318c2ecf20Sopenharmony_ci#define NFC_RND_WRITE_CMD_MSK GENMASK(15, 8) 1328c2ecf20Sopenharmony_ci#define NFC_READ_CMD0_MSK GENMASK(23, 16) 1338c2ecf20Sopenharmony_ci#define NFC_READ_CMD1_MSK GENMASK(31, 24) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* define bit use in NFC_ECC_CTL */ 1368c2ecf20Sopenharmony_ci#define NFC_ECC_EN BIT(0) 1378c2ecf20Sopenharmony_ci#define NFC_ECC_PIPELINE BIT(3) 1388c2ecf20Sopenharmony_ci#define NFC_ECC_EXCEPTION BIT(4) 1398c2ecf20Sopenharmony_ci#define NFC_ECC_BLOCK_SIZE_MSK BIT(5) 1408c2ecf20Sopenharmony_ci#define NFC_ECC_BLOCK_512 BIT(5) 1418c2ecf20Sopenharmony_ci#define NFC_RANDOM_EN BIT(9) 1428c2ecf20Sopenharmony_ci#define NFC_RANDOM_DIRECTION BIT(10) 1438c2ecf20Sopenharmony_ci#define NFC_ECC_MODE_MSK GENMASK(15, 12) 1448c2ecf20Sopenharmony_ci#define NFC_ECC_MODE(x) ((x) << 12) 1458c2ecf20Sopenharmony_ci#define NFC_RANDOM_SEED_MSK GENMASK(30, 16) 1468c2ecf20Sopenharmony_ci#define NFC_RANDOM_SEED(x) ((x) << 16) 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* define bit use in NFC_ECC_ST */ 1498c2ecf20Sopenharmony_ci#define NFC_ECC_ERR(x) BIT(x) 1508c2ecf20Sopenharmony_ci#define NFC_ECC_ERR_MSK GENMASK(15, 0) 1518c2ecf20Sopenharmony_ci#define NFC_ECC_PAT_FOUND(x) BIT(x + 16) 1528c2ecf20Sopenharmony_ci#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff) 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#define NFC_DEFAULT_TIMEOUT_MS 1000 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci#define NFC_SRAM_SIZE 1024 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#define NFC_MAX_CS 7 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * struct sunxi_nand_chip_sel - stores information related to NAND Chip Select 1628c2ecf20Sopenharmony_ci * 1638c2ecf20Sopenharmony_ci * @cs: the NAND CS id used to communicate with a NAND Chip 1648c2ecf20Sopenharmony_ci * @rb: the Ready/Busy pin ID. -1 means no R/B pin connected to the NFC 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistruct sunxi_nand_chip_sel { 1678c2ecf20Sopenharmony_ci u8 cs; 1688c2ecf20Sopenharmony_ci s8 rb; 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/** 1728c2ecf20Sopenharmony_ci * struct sunxi_nand_hw_ecc - stores information related to HW ECC support 1738c2ecf20Sopenharmony_ci * 1748c2ecf20Sopenharmony_ci * @mode: the sunxi ECC mode field deduced from ECC requirements 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistruct sunxi_nand_hw_ecc { 1778c2ecf20Sopenharmony_ci int mode; 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/** 1818c2ecf20Sopenharmony_ci * struct sunxi_nand_chip - stores NAND chip device related information 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * @node: used to store NAND chips into a list 1848c2ecf20Sopenharmony_ci * @nand: base NAND chip structure 1858c2ecf20Sopenharmony_ci * @clk_rate: clk_rate required for this NAND chip 1868c2ecf20Sopenharmony_ci * @timing_cfg: TIMING_CFG register value for this NAND chip 1878c2ecf20Sopenharmony_ci * @timing_ctl: TIMING_CTL register value for this NAND chip 1888c2ecf20Sopenharmony_ci * @nsels: number of CS lines required by the NAND chip 1898c2ecf20Sopenharmony_ci * @sels: array of CS lines descriptions 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistruct sunxi_nand_chip { 1928c2ecf20Sopenharmony_ci struct list_head node; 1938c2ecf20Sopenharmony_ci struct nand_chip nand; 1948c2ecf20Sopenharmony_ci unsigned long clk_rate; 1958c2ecf20Sopenharmony_ci u32 timing_cfg; 1968c2ecf20Sopenharmony_ci u32 timing_ctl; 1978c2ecf20Sopenharmony_ci int nsels; 1988c2ecf20Sopenharmony_ci struct sunxi_nand_chip_sel sels[]; 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci return container_of(nand, struct sunxi_nand_chip, nand); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* 2078c2ecf20Sopenharmony_ci * NAND Controller capabilities structure: stores NAND controller capabilities 2088c2ecf20Sopenharmony_ci * for distinction between compatible strings. 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * @extra_mbus_conf: Contrary to A10, A10s and A13, accessing internal RAM 2118c2ecf20Sopenharmony_ci * through MBUS on A23/A33 needs extra configuration. 2128c2ecf20Sopenharmony_ci * @reg_io_data: I/O data register 2138c2ecf20Sopenharmony_ci * @dma_maxburst: DMA maxburst 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_cistruct sunxi_nfc_caps { 2168c2ecf20Sopenharmony_ci bool extra_mbus_conf; 2178c2ecf20Sopenharmony_ci unsigned int reg_io_data; 2188c2ecf20Sopenharmony_ci unsigned int dma_maxburst; 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * struct sunxi_nfc - stores sunxi NAND controller information 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * @controller: base controller structure 2258c2ecf20Sopenharmony_ci * @dev: parent device (used to print error messages) 2268c2ecf20Sopenharmony_ci * @regs: NAND controller registers 2278c2ecf20Sopenharmony_ci * @ahb_clk: NAND controller AHB clock 2288c2ecf20Sopenharmony_ci * @mod_clk: NAND controller mod clock 2298c2ecf20Sopenharmony_ci * @reset: NAND controller reset line 2308c2ecf20Sopenharmony_ci * @assigned_cs: bitmask describing already assigned CS lines 2318c2ecf20Sopenharmony_ci * @clk_rate: NAND controller current clock rate 2328c2ecf20Sopenharmony_ci * @chips: a list containing all the NAND chips attached to this NAND 2338c2ecf20Sopenharmony_ci * controller 2348c2ecf20Sopenharmony_ci * @complete: a completion object used to wait for NAND controller events 2358c2ecf20Sopenharmony_ci * @dmac: the DMA channel attached to the NAND controller 2368c2ecf20Sopenharmony_ci */ 2378c2ecf20Sopenharmony_cistruct sunxi_nfc { 2388c2ecf20Sopenharmony_ci struct nand_controller controller; 2398c2ecf20Sopenharmony_ci struct device *dev; 2408c2ecf20Sopenharmony_ci void __iomem *regs; 2418c2ecf20Sopenharmony_ci struct clk *ahb_clk; 2428c2ecf20Sopenharmony_ci struct clk *mod_clk; 2438c2ecf20Sopenharmony_ci struct reset_control *reset; 2448c2ecf20Sopenharmony_ci unsigned long assigned_cs; 2458c2ecf20Sopenharmony_ci unsigned long clk_rate; 2468c2ecf20Sopenharmony_ci struct list_head chips; 2478c2ecf20Sopenharmony_ci struct completion complete; 2488c2ecf20Sopenharmony_ci struct dma_chan *dmac; 2498c2ecf20Sopenharmony_ci const struct sunxi_nfc_caps *caps; 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline struct sunxi_nfc *to_sunxi_nfc(struct nand_controller *ctrl) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci return container_of(ctrl, struct sunxi_nfc, controller); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = dev_id; 2608c2ecf20Sopenharmony_ci u32 st = readl(nfc->regs + NFC_REG_ST); 2618c2ecf20Sopenharmony_ci u32 ien = readl(nfc->regs + NFC_REG_INT); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (!(ien & st)) 2648c2ecf20Sopenharmony_ci return IRQ_NONE; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if ((ien & st) == ien) 2678c2ecf20Sopenharmony_ci complete(&nfc->complete); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST); 2708c2ecf20Sopenharmony_ci writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, 2768c2ecf20Sopenharmony_ci bool use_polling, unsigned int timeout_ms) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci int ret; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (events & ~NFC_INT_MASK) 2818c2ecf20Sopenharmony_ci return -EINVAL; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!timeout_ms) 2848c2ecf20Sopenharmony_ci timeout_ms = NFC_DEFAULT_TIMEOUT_MS; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!use_polling) { 2878c2ecf20Sopenharmony_ci init_completion(&nfc->complete); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci writel(events, nfc->regs + NFC_REG_INT); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&nfc->complete, 2928c2ecf20Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 2938c2ecf20Sopenharmony_ci if (!ret) 2948c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 2958c2ecf20Sopenharmony_ci else 2968c2ecf20Sopenharmony_ci ret = 0; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci writel(0, nfc->regs + NFC_REG_INT); 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci u32 status; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, 3038c2ecf20Sopenharmony_ci (status & events) == events, 1, 3048c2ecf20Sopenharmony_ci timeout_ms * 1000); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci writel(events & NFC_INT_MASK, nfc->regs + NFC_REG_ST); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (ret) 3108c2ecf20Sopenharmony_ci dev_err(nfc->dev, "wait interrupt timedout\n"); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci u32 status; 3188c2ecf20Sopenharmony_ci int ret; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = readl_poll_timeout(nfc->regs + NFC_REG_ST, status, 3218c2ecf20Sopenharmony_ci !(status & NFC_CMD_FIFO_STATUS), 1, 3228c2ecf20Sopenharmony_ci NFC_DEFAULT_TIMEOUT_MS * 1000); 3238c2ecf20Sopenharmony_ci if (ret) 3248c2ecf20Sopenharmony_ci dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n"); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return ret; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int sunxi_nfc_rst(struct sunxi_nfc *nfc) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci u32 ctl; 3328c2ecf20Sopenharmony_ci int ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci writel(0, nfc->regs + NFC_REG_ECC_CTL); 3358c2ecf20Sopenharmony_ci writel(NFC_RESET, nfc->regs + NFC_REG_CTL); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = readl_poll_timeout(nfc->regs + NFC_REG_CTL, ctl, 3388c2ecf20Sopenharmony_ci !(ctl & NFC_RESET), 1, 3398c2ecf20Sopenharmony_ci NFC_DEFAULT_TIMEOUT_MS * 1000); 3408c2ecf20Sopenharmony_ci if (ret) 3418c2ecf20Sopenharmony_ci dev_err(nfc->dev, "wait for NAND controller reset timedout\n"); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return ret; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int sunxi_nfc_dma_op_prepare(struct sunxi_nfc *nfc, const void *buf, 3478c2ecf20Sopenharmony_ci int chunksize, int nchunks, 3488c2ecf20Sopenharmony_ci enum dma_data_direction ddir, 3498c2ecf20Sopenharmony_ci struct scatterlist *sg) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *dmad; 3528c2ecf20Sopenharmony_ci enum dma_transfer_direction tdir; 3538c2ecf20Sopenharmony_ci dma_cookie_t dmat; 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (ddir == DMA_FROM_DEVICE) 3578c2ecf20Sopenharmony_ci tdir = DMA_DEV_TO_MEM; 3588c2ecf20Sopenharmony_ci else 3598c2ecf20Sopenharmony_ci tdir = DMA_MEM_TO_DEV; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci sg_init_one(sg, buf, nchunks * chunksize); 3628c2ecf20Sopenharmony_ci ret = dma_map_sg(nfc->dev, sg, 1, ddir); 3638c2ecf20Sopenharmony_ci if (!ret) 3648c2ecf20Sopenharmony_ci return -ENOMEM; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci dmad = dmaengine_prep_slave_sg(nfc->dmac, sg, 1, tdir, DMA_CTRL_ACK); 3678c2ecf20Sopenharmony_ci if (!dmad) { 3688c2ecf20Sopenharmony_ci ret = -EINVAL; 3698c2ecf20Sopenharmony_ci goto err_unmap_buf; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RAM_METHOD, 3738c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CTL); 3748c2ecf20Sopenharmony_ci writel(nchunks, nfc->regs + NFC_REG_SECTOR_NUM); 3758c2ecf20Sopenharmony_ci writel(chunksize, nfc->regs + NFC_REG_CNT); 3768c2ecf20Sopenharmony_ci if (nfc->caps->extra_mbus_conf) 3778c2ecf20Sopenharmony_ci writel(chunksize * nchunks, nfc->regs + NFC_REG_MDMA_CNT); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci dmat = dmaengine_submit(dmad); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci ret = dma_submit_error(dmat); 3828c2ecf20Sopenharmony_ci if (ret) 3838c2ecf20Sopenharmony_ci goto err_clr_dma_flag; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cierr_clr_dma_flag: 3888c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, 3898c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CTL); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cierr_unmap_buf: 3928c2ecf20Sopenharmony_ci dma_unmap_sg(nfc->dev, sg, 1, ddir); 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void sunxi_nfc_dma_op_cleanup(struct sunxi_nfc *nfc, 3978c2ecf20Sopenharmony_ci enum dma_data_direction ddir, 3988c2ecf20Sopenharmony_ci struct scatterlist *sg) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci dma_unmap_sg(nfc->dev, sg, 1, ddir); 4018c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) & ~NFC_RAM_METHOD, 4028c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CTL); 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 4088c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 4098c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 4108c2ecf20Sopenharmony_ci struct sunxi_nand_chip_sel *sel; 4118c2ecf20Sopenharmony_ci u32 ctl; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (cs > 0 && cs >= sunxi_nand->nsels) 4148c2ecf20Sopenharmony_ci return; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ctl = readl(nfc->regs + NFC_REG_CTL) & 4178c2ecf20Sopenharmony_ci ~(NFC_PAGE_SHIFT_MSK | NFC_CE_SEL_MSK | NFC_RB_SEL_MSK | NFC_EN); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci sel = &sunxi_nand->sels[cs]; 4208c2ecf20Sopenharmony_ci ctl |= NFC_CE_SEL(sel->cs) | NFC_EN | NFC_PAGE_SHIFT(nand->page_shift); 4218c2ecf20Sopenharmony_ci if (sel->rb >= 0) 4228c2ecf20Sopenharmony_ci ctl |= NFC_RB_SEL(sel->rb); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (nfc->clk_rate != sunxi_nand->clk_rate) { 4278c2ecf20Sopenharmony_ci clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate); 4288c2ecf20Sopenharmony_ci nfc->clk_rate = sunxi_nand->clk_rate; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci writel(sunxi_nand->timing_ctl, nfc->regs + NFC_REG_TIMING_CTL); 4328c2ecf20Sopenharmony_ci writel(sunxi_nand->timing_cfg, nfc->regs + NFC_REG_TIMING_CFG); 4338c2ecf20Sopenharmony_ci writel(ctl, nfc->regs + NFC_REG_CTL); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 4398c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 4408c2ecf20Sopenharmony_ci int ret; 4418c2ecf20Sopenharmony_ci int cnt; 4428c2ecf20Sopenharmony_ci int offs = 0; 4438c2ecf20Sopenharmony_ci u32 tmp; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci while (len > offs) { 4468c2ecf20Sopenharmony_ci bool poll = false; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci cnt = min(len - offs, NFC_SRAM_SIZE); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 4518c2ecf20Sopenharmony_ci if (ret) 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci writel(cnt, nfc->regs + NFC_REG_CNT); 4558c2ecf20Sopenharmony_ci tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; 4568c2ecf20Sopenharmony_ci writel(tmp, nfc->regs + NFC_REG_CMD); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* Arbitrary limit for polling mode */ 4598c2ecf20Sopenharmony_ci if (cnt < 64) 4608c2ecf20Sopenharmony_ci poll = true; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); 4638c2ecf20Sopenharmony_ci if (ret) 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (buf) 4678c2ecf20Sopenharmony_ci memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, 4688c2ecf20Sopenharmony_ci cnt); 4698c2ecf20Sopenharmony_ci offs += cnt; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf, 4748c2ecf20Sopenharmony_ci int len) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 4778c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 4788c2ecf20Sopenharmony_ci int ret; 4798c2ecf20Sopenharmony_ci int cnt; 4808c2ecf20Sopenharmony_ci int offs = 0; 4818c2ecf20Sopenharmony_ci u32 tmp; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci while (len > offs) { 4848c2ecf20Sopenharmony_ci bool poll = false; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci cnt = min(len - offs, NFC_SRAM_SIZE); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 4898c2ecf20Sopenharmony_ci if (ret) 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci writel(cnt, nfc->regs + NFC_REG_CNT); 4938c2ecf20Sopenharmony_ci memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt); 4948c2ecf20Sopenharmony_ci tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | 4958c2ecf20Sopenharmony_ci NFC_ACCESS_DIR; 4968c2ecf20Sopenharmony_ci writel(tmp, nfc->regs + NFC_REG_CMD); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Arbitrary limit for polling mode */ 4998c2ecf20Sopenharmony_ci if (cnt < 64) 5008c2ecf20Sopenharmony_ci poll = true; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci offs += cnt; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* These seed values have been extracted from Allwinner's BSP */ 5118c2ecf20Sopenharmony_cistatic const u16 sunxi_nfc_randomizer_page_seeds[] = { 5128c2ecf20Sopenharmony_ci 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, 5138c2ecf20Sopenharmony_ci 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, 5148c2ecf20Sopenharmony_ci 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, 5158c2ecf20Sopenharmony_ci 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, 5168c2ecf20Sopenharmony_ci 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, 5178c2ecf20Sopenharmony_ci 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, 5188c2ecf20Sopenharmony_ci 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, 5198c2ecf20Sopenharmony_ci 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, 5208c2ecf20Sopenharmony_ci 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, 5218c2ecf20Sopenharmony_ci 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, 5228c2ecf20Sopenharmony_ci 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, 5238c2ecf20Sopenharmony_ci 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, 5248c2ecf20Sopenharmony_ci 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, 5258c2ecf20Sopenharmony_ci 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, 5268c2ecf20Sopenharmony_ci 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, 5278c2ecf20Sopenharmony_ci 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, 5288c2ecf20Sopenharmony_ci}; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/* 5318c2ecf20Sopenharmony_ci * sunxi_nfc_randomizer_ecc512_seeds and sunxi_nfc_randomizer_ecc1024_seeds 5328c2ecf20Sopenharmony_ci * have been generated using 5338c2ecf20Sopenharmony_ci * sunxi_nfc_randomizer_step(seed, (step_size * 8) + 15), which is what 5348c2ecf20Sopenharmony_ci * the randomizer engine does internally before de/scrambling OOB data. 5358c2ecf20Sopenharmony_ci * 5368c2ecf20Sopenharmony_ci * Those tables are statically defined to avoid calculating randomizer state 5378c2ecf20Sopenharmony_ci * at runtime. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_cistatic const u16 sunxi_nfc_randomizer_ecc512_seeds[] = { 5408c2ecf20Sopenharmony_ci 0x3346, 0x367f, 0x1f18, 0x769a, 0x4f64, 0x068c, 0x2ef1, 0x6b64, 5418c2ecf20Sopenharmony_ci 0x28a9, 0x15d7, 0x30f8, 0x3659, 0x53db, 0x7c5f, 0x71d4, 0x4409, 5428c2ecf20Sopenharmony_ci 0x26eb, 0x03cc, 0x655d, 0x47d4, 0x4daa, 0x0877, 0x712d, 0x3617, 5438c2ecf20Sopenharmony_ci 0x3264, 0x49aa, 0x7f9e, 0x588e, 0x4fbc, 0x7176, 0x7f91, 0x6c6d, 5448c2ecf20Sopenharmony_ci 0x4b95, 0x5fb7, 0x3844, 0x4037, 0x0184, 0x081b, 0x0ee8, 0x5b91, 5458c2ecf20Sopenharmony_ci 0x293d, 0x1f71, 0x0e6f, 0x402b, 0x5122, 0x1e52, 0x22be, 0x3d2d, 5468c2ecf20Sopenharmony_ci 0x75bc, 0x7c60, 0x6291, 0x1a2f, 0x61d4, 0x74aa, 0x4140, 0x29ab, 5478c2ecf20Sopenharmony_ci 0x472d, 0x2852, 0x017e, 0x15e8, 0x5ec2, 0x17cf, 0x7d0f, 0x06b8, 5488c2ecf20Sopenharmony_ci 0x117a, 0x6b94, 0x789b, 0x3126, 0x6ac5, 0x5be7, 0x150f, 0x51f8, 5498c2ecf20Sopenharmony_ci 0x7889, 0x0aa5, 0x663d, 0x77e8, 0x0b87, 0x3dcb, 0x360d, 0x218b, 5508c2ecf20Sopenharmony_ci 0x512f, 0x7dc9, 0x6a4d, 0x630a, 0x3547, 0x1dd2, 0x5aea, 0x69a5, 5518c2ecf20Sopenharmony_ci 0x7bfa, 0x5e4f, 0x1519, 0x6430, 0x3a0e, 0x5eb3, 0x5425, 0x0c7a, 5528c2ecf20Sopenharmony_ci 0x5540, 0x3670, 0x63c1, 0x31e9, 0x5a39, 0x2de7, 0x5979, 0x2891, 5538c2ecf20Sopenharmony_ci 0x1562, 0x014b, 0x5b05, 0x2756, 0x5a34, 0x13aa, 0x6cb5, 0x2c36, 5548c2ecf20Sopenharmony_ci 0x5e72, 0x1306, 0x0861, 0x15ef, 0x1ee8, 0x5a37, 0x7ac4, 0x45dd, 5558c2ecf20Sopenharmony_ci 0x44c4, 0x7266, 0x2f41, 0x3ccc, 0x045e, 0x7d40, 0x7c66, 0x0fa0, 5568c2ecf20Sopenharmony_ci}; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic const u16 sunxi_nfc_randomizer_ecc1024_seeds[] = { 5598c2ecf20Sopenharmony_ci 0x2cf5, 0x35f1, 0x63a4, 0x5274, 0x2bd2, 0x778b, 0x7285, 0x32b6, 5608c2ecf20Sopenharmony_ci 0x6a5c, 0x70d6, 0x757d, 0x6769, 0x5375, 0x1e81, 0x0cf3, 0x3982, 5618c2ecf20Sopenharmony_ci 0x6787, 0x042a, 0x6c49, 0x1925, 0x56a8, 0x40a9, 0x063e, 0x7bd9, 5628c2ecf20Sopenharmony_ci 0x4dbf, 0x55ec, 0x672e, 0x7334, 0x5185, 0x4d00, 0x232a, 0x7e07, 5638c2ecf20Sopenharmony_ci 0x445d, 0x6b92, 0x528f, 0x4255, 0x53ba, 0x7d82, 0x2a2e, 0x3a4e, 5648c2ecf20Sopenharmony_ci 0x75eb, 0x450c, 0x6844, 0x1b5d, 0x581a, 0x4cc6, 0x0379, 0x37b2, 5658c2ecf20Sopenharmony_ci 0x419f, 0x0e92, 0x6b27, 0x5624, 0x01e3, 0x07c1, 0x44a5, 0x130c, 5668c2ecf20Sopenharmony_ci 0x13e8, 0x5910, 0x0876, 0x60c5, 0x54e3, 0x5b7f, 0x2269, 0x509f, 5678c2ecf20Sopenharmony_ci 0x7665, 0x36fd, 0x3e9a, 0x0579, 0x6295, 0x14ef, 0x0a81, 0x1bcc, 5688c2ecf20Sopenharmony_ci 0x4b16, 0x64db, 0x0514, 0x4f07, 0x0591, 0x3576, 0x6853, 0x0d9e, 5698c2ecf20Sopenharmony_ci 0x259f, 0x38b7, 0x64fb, 0x3094, 0x4693, 0x6ddd, 0x29bb, 0x0bc8, 5708c2ecf20Sopenharmony_ci 0x3f47, 0x490e, 0x0c0e, 0x7933, 0x3c9e, 0x5840, 0x398d, 0x3e68, 5718c2ecf20Sopenharmony_ci 0x4af1, 0x71f5, 0x57cf, 0x1121, 0x64eb, 0x3579, 0x15ac, 0x584d, 5728c2ecf20Sopenharmony_ci 0x5f2a, 0x47e2, 0x6528, 0x6eac, 0x196e, 0x6b96, 0x0450, 0x0179, 5738c2ecf20Sopenharmony_ci 0x609c, 0x06e1, 0x4626, 0x42c7, 0x273e, 0x486f, 0x0705, 0x1601, 5748c2ecf20Sopenharmony_ci 0x145b, 0x407e, 0x062b, 0x57a5, 0x53f9, 0x5659, 0x4410, 0x3ccd, 5758c2ecf20Sopenharmony_ci}; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic u16 sunxi_nfc_randomizer_step(u16 state, int count) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci state &= 0x7fff; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * This loop is just a simple implementation of a Fibonacci LFSR using 5838c2ecf20Sopenharmony_ci * the x16 + x15 + 1 polynomial. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci while (count--) 5868c2ecf20Sopenharmony_ci state = ((state >> 1) | 5878c2ecf20Sopenharmony_ci (((state ^ (state >> 1)) & 1) << 14)) & 0x7fff; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return state; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic u16 sunxi_nfc_randomizer_state(struct nand_chip *nand, int page, 5938c2ecf20Sopenharmony_ci bool ecc) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 5968c2ecf20Sopenharmony_ci const u16 *seeds = sunxi_nfc_randomizer_page_seeds; 5978c2ecf20Sopenharmony_ci int mod = mtd_div_by_ws(mtd->erasesize, mtd); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (mod > ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds)) 6008c2ecf20Sopenharmony_ci mod = ARRAY_SIZE(sunxi_nfc_randomizer_page_seeds); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (ecc) { 6038c2ecf20Sopenharmony_ci if (mtd->ecc_step_size == 512) 6048c2ecf20Sopenharmony_ci seeds = sunxi_nfc_randomizer_ecc512_seeds; 6058c2ecf20Sopenharmony_ci else 6068c2ecf20Sopenharmony_ci seeds = sunxi_nfc_randomizer_ecc1024_seeds; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return seeds[page % mod]; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page, 6138c2ecf20Sopenharmony_ci bool ecc) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6168c2ecf20Sopenharmony_ci u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 6178c2ecf20Sopenharmony_ci u16 state; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (!(nand->options & NAND_NEED_SCRAMBLING)) 6208c2ecf20Sopenharmony_ci return; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 6238c2ecf20Sopenharmony_ci state = sunxi_nfc_randomizer_state(nand, page, ecc); 6248c2ecf20Sopenharmony_ci ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK; 6258c2ecf20Sopenharmony_ci writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic void sunxi_nfc_randomizer_enable(struct nand_chip *nand) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (!(nand->options & NAND_NEED_SCRAMBLING)) 6338c2ecf20Sopenharmony_ci return; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN, 6368c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_ECC_CTL); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic void sunxi_nfc_randomizer_disable(struct nand_chip *nand) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (!(nand->options & NAND_NEED_SCRAMBLING)) 6448c2ecf20Sopenharmony_ci return; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN, 6478c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_ECC_CTL); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic void sunxi_nfc_randomize_bbm(struct nand_chip *nand, int page, u8 *bbm) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci u16 state = sunxi_nfc_randomizer_state(nand, page, true); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci bbm[0] ^= state; 6558c2ecf20Sopenharmony_ci bbm[1] ^= sunxi_nfc_randomizer_step(state, 8); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic void sunxi_nfc_randomizer_write_buf(struct nand_chip *nand, 6598c2ecf20Sopenharmony_ci const uint8_t *buf, int len, 6608c2ecf20Sopenharmony_ci bool ecc, int page) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, ecc); 6638c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 6648c2ecf20Sopenharmony_ci sunxi_nfc_write_buf(nand, buf, len); 6658c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic void sunxi_nfc_randomizer_read_buf(struct nand_chip *nand, uint8_t *buf, 6698c2ecf20Sopenharmony_ci int len, bool ecc, int page) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, ecc); 6728c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 6738c2ecf20Sopenharmony_ci sunxi_nfc_read_buf(nand, buf, len); 6748c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_enable(struct nand_chip *nand) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6808c2ecf20Sopenharmony_ci struct sunxi_nand_hw_ecc *data = nand->ecc.priv; 6818c2ecf20Sopenharmony_ci u32 ecc_ctl; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL); 6848c2ecf20Sopenharmony_ci ecc_ctl &= ~(NFC_ECC_MODE_MSK | NFC_ECC_PIPELINE | 6858c2ecf20Sopenharmony_ci NFC_ECC_BLOCK_SIZE_MSK); 6868c2ecf20Sopenharmony_ci ecc_ctl |= NFC_ECC_EN | NFC_ECC_MODE(data->mode) | NFC_ECC_EXCEPTION | 6878c2ecf20Sopenharmony_ci NFC_ECC_PIPELINE; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (nand->ecc.size == 512) 6908c2ecf20Sopenharmony_ci ecc_ctl |= NFC_ECC_BLOCK_512; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci writel(ecc_ctl, nfc->regs + NFC_REG_ECC_CTL); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_disable(struct nand_chip *nand) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN, 7008c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_ECC_CTL); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci buf[0] = user_data; 7068c2ecf20Sopenharmony_ci buf[1] = user_data >> 8; 7078c2ecf20Sopenharmony_ci buf[2] = user_data >> 16; 7088c2ecf20Sopenharmony_ci buf[3] = user_data >> 24; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, 7178c2ecf20Sopenharmony_ci int step, bool bbm, int page) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), 7228c2ecf20Sopenharmony_ci oob); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* De-randomize the Bad Block Marker. */ 7258c2ecf20Sopenharmony_ci if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) 7268c2ecf20Sopenharmony_ci sunxi_nfc_randomize_bbm(nand, page, oob); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand, 7308c2ecf20Sopenharmony_ci const u8 *oob, int step, 7318c2ecf20Sopenharmony_ci bool bbm, int page) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 7348c2ecf20Sopenharmony_ci u8 user_data[4]; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* Randomize the Bad Block Marker. */ 7378c2ecf20Sopenharmony_ci if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { 7388c2ecf20Sopenharmony_ci memcpy(user_data, oob, sizeof(user_data)); 7398c2ecf20Sopenharmony_ci sunxi_nfc_randomize_bbm(nand, page, user_data); 7408c2ecf20Sopenharmony_ci oob = user_data; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci writel(sunxi_nfc_buf_to_user_data(oob), 7448c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_USER_DATA(step)); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand, 7488c2ecf20Sopenharmony_ci unsigned int *max_bitflips, int ret) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (ret < 0) { 7538c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 7548c2ecf20Sopenharmony_ci } else { 7558c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ret; 7568c2ecf20Sopenharmony_ci *max_bitflips = max_t(unsigned int, *max_bitflips, ret); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob, 7618c2ecf20Sopenharmony_ci int step, u32 status, bool *erased) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 7648c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 7658c2ecf20Sopenharmony_ci u32 tmp; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci *erased = false; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (status & NFC_ECC_ERR(step)) 7708c2ecf20Sopenharmony_ci return -EBADMSG; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (status & NFC_ECC_PAT_FOUND(step)) { 7738c2ecf20Sopenharmony_ci u8 pattern; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { 7768c2ecf20Sopenharmony_ci pattern = 0x0; 7778c2ecf20Sopenharmony_ci } else { 7788c2ecf20Sopenharmony_ci pattern = 0xff; 7798c2ecf20Sopenharmony_ci *erased = true; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (data) 7838c2ecf20Sopenharmony_ci memset(data, pattern, ecc->size); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (oob) 7868c2ecf20Sopenharmony_ci memset(oob, pattern, ecc->bytes + 4); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci return NFC_ECC_ERR_CNT(step, tmp); 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand, 7978c2ecf20Sopenharmony_ci u8 *data, int data_off, 7988c2ecf20Sopenharmony_ci u8 *oob, int oob_off, 7998c2ecf20Sopenharmony_ci int *cur_off, 8008c2ecf20Sopenharmony_ci unsigned int *max_bitflips, 8018c2ecf20Sopenharmony_ci bool bbm, bool oob_required, int page) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 8048c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 8058c2ecf20Sopenharmony_ci int raw_mode = 0; 8068c2ecf20Sopenharmony_ci bool erased; 8078c2ecf20Sopenharmony_ci int ret; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci if (*cur_off != data_off) 8108c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, data_off, NULL, 0, false); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_read_buf(nand, NULL, ecc->size, false, page); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (data_off + ecc->size != oob_off) 8158c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, oob_off, NULL, 0, false); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 8188c2ecf20Sopenharmony_ci if (ret) 8198c2ecf20Sopenharmony_ci return ret; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 8228c2ecf20Sopenharmony_ci writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, 8238c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CMD); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 8268c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 8278c2ecf20Sopenharmony_ci if (ret) 8288c2ecf20Sopenharmony_ci return ret; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci *cur_off = oob_off + ecc->bytes + 4; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0, 8338c2ecf20Sopenharmony_ci readl(nfc->regs + NFC_REG_ECC_ST), 8348c2ecf20Sopenharmony_ci &erased); 8358c2ecf20Sopenharmony_ci if (erased) 8368c2ecf20Sopenharmony_ci return 1; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (ret < 0) { 8398c2ecf20Sopenharmony_ci /* 8408c2ecf20Sopenharmony_ci * Re-read the data with the randomizer disabled to identify 8418c2ecf20Sopenharmony_ci * bitflips in erased pages. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) 8448c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, data_off, data, 8458c2ecf20Sopenharmony_ci ecc->size, false); 8468c2ecf20Sopenharmony_ci else 8478c2ecf20Sopenharmony_ci memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, 8488c2ecf20Sopenharmony_ci ecc->size); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4, 8518c2ecf20Sopenharmony_ci false); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, ecc->size, 8548c2ecf20Sopenharmony_ci oob, ecc->bytes + 4, 8558c2ecf20Sopenharmony_ci NULL, 0, ecc->strength); 8568c2ecf20Sopenharmony_ci if (ret >= 0) 8578c2ecf20Sopenharmony_ci raw_mode = 1; 8588c2ecf20Sopenharmony_ci } else { 8598c2ecf20Sopenharmony_ci memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (oob_required) { 8628c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, oob_off, NULL, 0, 8638c2ecf20Sopenharmony_ci false); 8648c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4, 8658c2ecf20Sopenharmony_ci true, page); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0, 8688c2ecf20Sopenharmony_ci bbm, page); 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_update_stats(nand, max_bitflips, ret); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return raw_mode; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_read_extra_oob(struct nand_chip *nand, 8788c2ecf20Sopenharmony_ci u8 *oob, int *cur_off, 8798c2ecf20Sopenharmony_ci bool randomize, int page) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 8828c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 8838c2ecf20Sopenharmony_ci int offset = ((ecc->bytes + 4) * ecc->steps); 8848c2ecf20Sopenharmony_ci int len = mtd->oobsize - offset; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (len <= 0) 8878c2ecf20Sopenharmony_ci return; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (!cur_off || *cur_off != offset) 8908c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, mtd->writesize, NULL, 0, 8918c2ecf20Sopenharmony_ci false); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (!randomize) 8948c2ecf20Sopenharmony_ci sunxi_nfc_read_buf(nand, oob + offset, len); 8958c2ecf20Sopenharmony_ci else 8968c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_read_buf(nand, oob + offset, len, 8978c2ecf20Sopenharmony_ci false, page); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (cur_off) 9008c2ecf20Sopenharmony_ci *cur_off = mtd->oobsize + mtd->writesize; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf, 9048c2ecf20Sopenharmony_ci int oob_required, int page, 9058c2ecf20Sopenharmony_ci int nchunks) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci bool randomized = nand->options & NAND_NEED_SCRAMBLING; 9088c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 9098c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 9108c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 9118c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 9128c2ecf20Sopenharmony_ci int ret, i, raw_mode = 0; 9138c2ecf20Sopenharmony_ci struct scatterlist sg; 9148c2ecf20Sopenharmony_ci u32 status; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 9178c2ecf20Sopenharmony_ci if (ret) 9188c2ecf20Sopenharmony_ci return ret; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, nchunks, 9218c2ecf20Sopenharmony_ci DMA_FROM_DEVICE, &sg); 9228c2ecf20Sopenharmony_ci if (ret) 9238c2ecf20Sopenharmony_ci return ret; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 9268c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, false); 9278c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci writel((NAND_CMD_RNDOUTSTART << 16) | (NAND_CMD_RNDOUT << 8) | 9308c2ecf20Sopenharmony_ci NAND_CMD_READSTART, nfc->regs + NFC_REG_RCMD_SET); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci dma_async_issue_pending(nfc->dmac); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, 9358c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CMD); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 9388c2ecf20Sopenharmony_ci if (ret) 9398c2ecf20Sopenharmony_ci dmaengine_terminate_all(nfc->dmac); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 9428c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci sunxi_nfc_dma_op_cleanup(nfc, DMA_FROM_DEVICE, &sg); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci if (ret) 9478c2ecf20Sopenharmony_ci return ret; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci status = readl(nfc->regs + NFC_REG_ECC_ST); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci for (i = 0; i < nchunks; i++) { 9528c2ecf20Sopenharmony_ci int data_off = i * ecc->size; 9538c2ecf20Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 9548c2ecf20Sopenharmony_ci u8 *data = buf + data_off; 9558c2ecf20Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 9568c2ecf20Sopenharmony_ci bool erased; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL, 9598c2ecf20Sopenharmony_ci oob_required ? oob : NULL, 9608c2ecf20Sopenharmony_ci i, status, &erased); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* ECC errors are handled in the second loop. */ 9638c2ecf20Sopenharmony_ci if (ret < 0) 9648c2ecf20Sopenharmony_ci continue; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (oob_required && !erased) { 9678c2ecf20Sopenharmony_ci /* TODO: use DMA to retrieve OOB */ 9688c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, 9698c2ecf20Sopenharmony_ci mtd->writesize + oob_off, 9708c2ecf20Sopenharmony_ci oob, ecc->bytes + 4, false); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i, 9738c2ecf20Sopenharmony_ci !i, page); 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci if (erased) 9778c2ecf20Sopenharmony_ci raw_mode = 1; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (status & NFC_ECC_ERR_MSK) { 9838c2ecf20Sopenharmony_ci for (i = 0; i < nchunks; i++) { 9848c2ecf20Sopenharmony_ci int data_off = i * ecc->size; 9858c2ecf20Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 9868c2ecf20Sopenharmony_ci u8 *data = buf + data_off; 9878c2ecf20Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (!(status & NFC_ECC_ERR(i))) 9908c2ecf20Sopenharmony_ci continue; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* 9938c2ecf20Sopenharmony_ci * Re-read the data with the randomizer disabled to 9948c2ecf20Sopenharmony_ci * identify bitflips in erased pages. 9958c2ecf20Sopenharmony_ci * TODO: use DMA to read page in raw mode 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ci if (randomized) 9988c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, data_off, 9998c2ecf20Sopenharmony_ci data, ecc->size, 10008c2ecf20Sopenharmony_ci false); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* TODO: use DMA to retrieve OOB */ 10038c2ecf20Sopenharmony_ci nand_change_read_column_op(nand, 10048c2ecf20Sopenharmony_ci mtd->writesize + oob_off, 10058c2ecf20Sopenharmony_ci oob, ecc->bytes + 4, false); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, ecc->size, 10088c2ecf20Sopenharmony_ci oob, ecc->bytes + 4, 10098c2ecf20Sopenharmony_ci NULL, 0, 10108c2ecf20Sopenharmony_ci ecc->strength); 10118c2ecf20Sopenharmony_ci if (ret >= 0) 10128c2ecf20Sopenharmony_ci raw_mode = 1; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (oob_required) 10198c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, 10208c2ecf20Sopenharmony_ci NULL, !raw_mode, 10218c2ecf20Sopenharmony_ci page); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci return max_bitflips; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand, 10278c2ecf20Sopenharmony_ci const u8 *data, int data_off, 10288c2ecf20Sopenharmony_ci const u8 *oob, int oob_off, 10298c2ecf20Sopenharmony_ci int *cur_off, bool bbm, 10308c2ecf20Sopenharmony_ci int page) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 10338c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 10348c2ecf20Sopenharmony_ci int ret; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (data_off != *cur_off) 10378c2ecf20Sopenharmony_ci nand_change_write_column_op(nand, data_off, NULL, 0, false); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_write_buf(nand, data, ecc->size, false, page); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (data_off + ecc->size != oob_off) 10428c2ecf20Sopenharmony_ci nand_change_write_column_op(nand, oob_off, NULL, 0, false); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 10458c2ecf20Sopenharmony_ci if (ret) 10468c2ecf20Sopenharmony_ci return ret; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 10498c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | 10528c2ecf20Sopenharmony_ci NFC_ACCESS_DIR | NFC_ECC_OP, 10538c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CMD); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 10568c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 10578c2ecf20Sopenharmony_ci if (ret) 10588c2ecf20Sopenharmony_ci return ret; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci *cur_off = oob_off + ecc->bytes + 4; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci return 0; 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand, 10668c2ecf20Sopenharmony_ci u8 *oob, int *cur_off, 10678c2ecf20Sopenharmony_ci int page) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 10708c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 10718c2ecf20Sopenharmony_ci int offset = ((ecc->bytes + 4) * ecc->steps); 10728c2ecf20Sopenharmony_ci int len = mtd->oobsize - offset; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (len <= 0) 10758c2ecf20Sopenharmony_ci return; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (!cur_off || *cur_off != offset) 10788c2ecf20Sopenharmony_ci nand_change_write_column_op(nand, offset + mtd->writesize, 10798c2ecf20Sopenharmony_ci NULL, 0, false); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_write_buf(nand, oob + offset, len, false, page); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (cur_off) 10848c2ecf20Sopenharmony_ci *cur_off = mtd->oobsize + mtd->writesize; 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf, 10888c2ecf20Sopenharmony_ci int oob_required, int page) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 10918c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 10928c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 10938c2ecf20Sopenharmony_ci int ret, i, cur_off = 0; 10948c2ecf20Sopenharmony_ci bool raw_mode = false; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 11038c2ecf20Sopenharmony_ci int data_off = i * ecc->size; 11048c2ecf20Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 11058c2ecf20Sopenharmony_ci u8 *data = buf + data_off; 11068c2ecf20Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, oob, 11098c2ecf20Sopenharmony_ci oob_off + mtd->writesize, 11108c2ecf20Sopenharmony_ci &cur_off, &max_bitflips, 11118c2ecf20Sopenharmony_ci !i, oob_required, page); 11128c2ecf20Sopenharmony_ci if (ret < 0) 11138c2ecf20Sopenharmony_ci return ret; 11148c2ecf20Sopenharmony_ci else if (ret) 11158c2ecf20Sopenharmony_ci raw_mode = true; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (oob_required) 11198c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_read_extra_oob(nand, nand->oob_poi, &cur_off, 11208c2ecf20Sopenharmony_ci !raw_mode, page); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return max_bitflips; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_page_dma(struct nand_chip *nand, u8 *buf, 11288c2ecf20Sopenharmony_ci int oob_required, int page) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci int ret; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, oob_required, page, 11378c2ecf20Sopenharmony_ci nand->ecc.steps); 11388c2ecf20Sopenharmony_ci if (ret >= 0) 11398c2ecf20Sopenharmony_ci return ret; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Fallback to PIO mode */ 11428c2ecf20Sopenharmony_ci return sunxi_nfc_hw_ecc_read_page(nand, buf, oob_required, page); 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand, 11468c2ecf20Sopenharmony_ci u32 data_offs, u32 readlen, 11478c2ecf20Sopenharmony_ci u8 *bufpoi, int page) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 11508c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 11518c2ecf20Sopenharmony_ci int ret, i, cur_off = 0; 11528c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci for (i = data_offs / ecc->size; 11618c2ecf20Sopenharmony_ci i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { 11628c2ecf20Sopenharmony_ci int data_off = i * ecc->size; 11638c2ecf20Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 11648c2ecf20Sopenharmony_ci u8 *data = bufpoi + data_off; 11658c2ecf20Sopenharmony_ci u8 *oob = nand->oob_poi + oob_off; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunk(nand, data, data_off, 11688c2ecf20Sopenharmony_ci oob, 11698c2ecf20Sopenharmony_ci oob_off + mtd->writesize, 11708c2ecf20Sopenharmony_ci &cur_off, &max_bitflips, !i, 11718c2ecf20Sopenharmony_ci false, page); 11728c2ecf20Sopenharmony_ci if (ret < 0) 11738c2ecf20Sopenharmony_ci return ret; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci return max_bitflips; 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_subpage_dma(struct nand_chip *nand, 11828c2ecf20Sopenharmony_ci u32 data_offs, u32 readlen, 11838c2ecf20Sopenharmony_ci u8 *buf, int page) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci int nchunks = DIV_ROUND_UP(data_offs + readlen, nand->ecc.size); 11868c2ecf20Sopenharmony_ci int ret; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci nand_read_page_op(nand, page, 0, NULL, 0); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_read_chunks_dma(nand, buf, false, page, nchunks); 11938c2ecf20Sopenharmony_ci if (ret >= 0) 11948c2ecf20Sopenharmony_ci return ret; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* Fallback to PIO mode */ 11978c2ecf20Sopenharmony_ci return sunxi_nfc_hw_ecc_read_subpage(nand, data_offs, readlen, 11988c2ecf20Sopenharmony_ci buf, page); 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand, 12028c2ecf20Sopenharmony_ci const uint8_t *buf, int oob_required, 12038c2ecf20Sopenharmony_ci int page) 12048c2ecf20Sopenharmony_ci{ 12058c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 12068c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 12078c2ecf20Sopenharmony_ci int ret, i, cur_off = 0; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci nand_prog_page_begin_op(nand, page, 0, NULL, 0); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 12168c2ecf20Sopenharmony_ci int data_off = i * ecc->size; 12178c2ecf20Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 12188c2ecf20Sopenharmony_ci const u8 *data = buf + data_off; 12198c2ecf20Sopenharmony_ci const u8 *oob = nand->oob_poi + oob_off; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, 12228c2ecf20Sopenharmony_ci oob_off + mtd->writesize, 12238c2ecf20Sopenharmony_ci &cur_off, !i, page); 12248c2ecf20Sopenharmony_ci if (ret) 12258c2ecf20Sopenharmony_ci return ret; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (oob_required || (nand->options & NAND_NEED_SCRAMBLING)) 12298c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi, 12308c2ecf20Sopenharmony_ci &cur_off, page); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci return nand_prog_page_end_op(nand); 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand, 12388c2ecf20Sopenharmony_ci u32 data_offs, u32 data_len, 12398c2ecf20Sopenharmony_ci const u8 *buf, int oob_required, 12408c2ecf20Sopenharmony_ci int page) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 12438c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 12448c2ecf20Sopenharmony_ci int ret, i, cur_off = 0; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci nand_prog_page_begin_op(nand, page, 0, NULL, 0); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci for (i = data_offs / ecc->size; 12538c2ecf20Sopenharmony_ci i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { 12548c2ecf20Sopenharmony_ci int data_off = i * ecc->size; 12558c2ecf20Sopenharmony_ci int oob_off = i * (ecc->bytes + 4); 12568c2ecf20Sopenharmony_ci const u8 *data = buf + data_off; 12578c2ecf20Sopenharmony_ci const u8 *oob = nand->oob_poi + oob_off; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci ret = sunxi_nfc_hw_ecc_write_chunk(nand, data, data_off, oob, 12608c2ecf20Sopenharmony_ci oob_off + mtd->writesize, 12618c2ecf20Sopenharmony_ci &cur_off, !i, page); 12628c2ecf20Sopenharmony_ci if (ret) 12638c2ecf20Sopenharmony_ci return ret; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci return nand_prog_page_end_op(nand); 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand, 12728c2ecf20Sopenharmony_ci const u8 *buf, 12738c2ecf20Sopenharmony_ci int oob_required, 12748c2ecf20Sopenharmony_ci int page) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 12778c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 12788c2ecf20Sopenharmony_ci struct scatterlist sg; 12798c2ecf20Sopenharmony_ci int ret, i; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, nand->cur_cs); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 12848c2ecf20Sopenharmony_ci if (ret) 12858c2ecf20Sopenharmony_ci return ret; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci ret = sunxi_nfc_dma_op_prepare(nfc, buf, ecc->size, ecc->steps, 12888c2ecf20Sopenharmony_ci DMA_TO_DEVICE, &sg); 12898c2ecf20Sopenharmony_ci if (ret) 12908c2ecf20Sopenharmony_ci goto pio_fallback; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 12938c2ecf20Sopenharmony_ci const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4)); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page); 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci nand_prog_page_begin_op(nand, page, 0, NULL, 0); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_enable(nand); 13018c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_config(nand, page, false); 13028c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_enable(nand); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG, 13058c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_WCMD_SET); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci dma_async_issue_pending(nfc->dmac); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | 13108c2ecf20Sopenharmony_ci NFC_DATA_TRANS | NFC_ACCESS_DIR, 13118c2ecf20Sopenharmony_ci nfc->regs + NFC_REG_CMD); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0); 13148c2ecf20Sopenharmony_ci if (ret) 13158c2ecf20Sopenharmony_ci dmaengine_terminate_all(nfc->dmac); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci sunxi_nfc_randomizer_disable(nand); 13188c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_disable(nand); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci sunxi_nfc_dma_op_cleanup(nfc, DMA_TO_DEVICE, &sg); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (ret) 13238c2ecf20Sopenharmony_ci return ret; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (oob_required || (nand->options & NAND_NEED_SCRAMBLING)) 13268c2ecf20Sopenharmony_ci /* TODO: use DMA to transfer extra OOB bytes ? */ 13278c2ecf20Sopenharmony_ci sunxi_nfc_hw_ecc_write_extra_oob(nand, nand->oob_poi, 13288c2ecf20Sopenharmony_ci NULL, page); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci return nand_prog_page_end_op(nand); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cipio_fallback: 13338c2ecf20Sopenharmony_ci return sunxi_nfc_hw_ecc_write_page(nand, buf, oob_required, page); 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_read_oob(struct nand_chip *nand, int page) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci u8 *buf = nand_get_data_buf(nand); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci return nand->ecc.read_page(nand, buf, 1, page); 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_cistatic int sunxi_nfc_hw_ecc_write_oob(struct nand_chip *nand, int page) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 13468c2ecf20Sopenharmony_ci u8 *buf = nand_get_data_buf(nand); 13478c2ecf20Sopenharmony_ci int ret; 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci memset(buf, 0xff, mtd->writesize); 13508c2ecf20Sopenharmony_ci ret = nand->ecc.write_page(nand, buf, 1, page); 13518c2ecf20Sopenharmony_ci if (ret) 13528c2ecf20Sopenharmony_ci return ret; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* Send command to program the OOB data */ 13558c2ecf20Sopenharmony_ci return nand_prog_page_end_op(nand); 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic const s32 tWB_lut[] = {6, 12, 16, 20}; 13598c2ecf20Sopenharmony_cistatic const s32 tRHW_lut[] = {4, 8, 12, 20}; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic int _sunxi_nand_lookup_timing(const s32 *lut, int lut_size, u32 duration, 13628c2ecf20Sopenharmony_ci u32 clk_period) 13638c2ecf20Sopenharmony_ci{ 13648c2ecf20Sopenharmony_ci u32 clk_cycles = DIV_ROUND_UP(duration, clk_period); 13658c2ecf20Sopenharmony_ci int i; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci for (i = 0; i < lut_size; i++) { 13688c2ecf20Sopenharmony_ci if (clk_cycles <= lut[i]) 13698c2ecf20Sopenharmony_ci return i; 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci /* Doesn't fit */ 13738c2ecf20Sopenharmony_ci return -EINVAL; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci#define sunxi_nand_lookup_timing(l, p, c) \ 13778c2ecf20Sopenharmony_ci _sunxi_nand_lookup_timing(l, ARRAY_SIZE(l), p, c) 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_cistatic int sunxi_nfc_setup_interface(struct nand_chip *nand, int csline, 13808c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 13838c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); 13848c2ecf20Sopenharmony_ci const struct nand_sdr_timings *timings; 13858c2ecf20Sopenharmony_ci u32 min_clk_period = 0; 13868c2ecf20Sopenharmony_ci s32 tWB, tADL, tWHR, tRHW, tCAD; 13878c2ecf20Sopenharmony_ci long real_clk_rate; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci timings = nand_get_sdr_timings(conf); 13908c2ecf20Sopenharmony_ci if (IS_ERR(timings)) 13918c2ecf20Sopenharmony_ci return -ENOTSUPP; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci /* T1 <=> tCLS */ 13948c2ecf20Sopenharmony_ci if (timings->tCLS_min > min_clk_period) 13958c2ecf20Sopenharmony_ci min_clk_period = timings->tCLS_min; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* T2 <=> tCLH */ 13988c2ecf20Sopenharmony_ci if (timings->tCLH_min > min_clk_period) 13998c2ecf20Sopenharmony_ci min_clk_period = timings->tCLH_min; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* T3 <=> tCS */ 14028c2ecf20Sopenharmony_ci if (timings->tCS_min > min_clk_period) 14038c2ecf20Sopenharmony_ci min_clk_period = timings->tCS_min; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci /* T4 <=> tCH */ 14068c2ecf20Sopenharmony_ci if (timings->tCH_min > min_clk_period) 14078c2ecf20Sopenharmony_ci min_clk_period = timings->tCH_min; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci /* T5 <=> tWP */ 14108c2ecf20Sopenharmony_ci if (timings->tWP_min > min_clk_period) 14118c2ecf20Sopenharmony_ci min_clk_period = timings->tWP_min; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci /* T6 <=> tWH */ 14148c2ecf20Sopenharmony_ci if (timings->tWH_min > min_clk_period) 14158c2ecf20Sopenharmony_ci min_clk_period = timings->tWH_min; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* T7 <=> tALS */ 14188c2ecf20Sopenharmony_ci if (timings->tALS_min > min_clk_period) 14198c2ecf20Sopenharmony_ci min_clk_period = timings->tALS_min; 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci /* T8 <=> tDS */ 14228c2ecf20Sopenharmony_ci if (timings->tDS_min > min_clk_period) 14238c2ecf20Sopenharmony_ci min_clk_period = timings->tDS_min; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci /* T9 <=> tDH */ 14268c2ecf20Sopenharmony_ci if (timings->tDH_min > min_clk_period) 14278c2ecf20Sopenharmony_ci min_clk_period = timings->tDH_min; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* T10 <=> tRR */ 14308c2ecf20Sopenharmony_ci if (timings->tRR_min > (min_clk_period * 3)) 14318c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci /* T11 <=> tALH */ 14348c2ecf20Sopenharmony_ci if (timings->tALH_min > min_clk_period) 14358c2ecf20Sopenharmony_ci min_clk_period = timings->tALH_min; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* T12 <=> tRP */ 14388c2ecf20Sopenharmony_ci if (timings->tRP_min > min_clk_period) 14398c2ecf20Sopenharmony_ci min_clk_period = timings->tRP_min; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* T13 <=> tREH */ 14428c2ecf20Sopenharmony_ci if (timings->tREH_min > min_clk_period) 14438c2ecf20Sopenharmony_ci min_clk_period = timings->tREH_min; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* T14 <=> tRC */ 14468c2ecf20Sopenharmony_ci if (timings->tRC_min > (min_clk_period * 2)) 14478c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* T15 <=> tWC */ 14508c2ecf20Sopenharmony_ci if (timings->tWC_min > (min_clk_period * 2)) 14518c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci /* T16 - T19 + tCAD */ 14548c2ecf20Sopenharmony_ci if (timings->tWB_max > (min_clk_period * 20)) 14558c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tWB_max, 20); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci if (timings->tADL_min > (min_clk_period * 32)) 14588c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tADL_min, 32); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (timings->tWHR_min > (min_clk_period * 32)) 14618c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tWHR_min, 32); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (timings->tRHW_min > (min_clk_period * 20)) 14648c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(timings->tRHW_min, 20); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci /* 14678c2ecf20Sopenharmony_ci * In non-EDO, tREA should be less than tRP to guarantee that the 14688c2ecf20Sopenharmony_ci * controller does not sample the IO lines too early. Unfortunately, 14698c2ecf20Sopenharmony_ci * the sunxi NAND controller does not allow us to have different 14708c2ecf20Sopenharmony_ci * values for tRP and tREH (tRP = tREH = tRW / 2). 14718c2ecf20Sopenharmony_ci * 14728c2ecf20Sopenharmony_ci * We have 2 options to overcome this limitation: 14738c2ecf20Sopenharmony_ci * 14748c2ecf20Sopenharmony_ci * 1/ Extend tRC to fulfil the tREA <= tRC / 2 constraint 14758c2ecf20Sopenharmony_ci * 2/ Use EDO mode (only works if timings->tRLOH > 0) 14768c2ecf20Sopenharmony_ci */ 14778c2ecf20Sopenharmony_ci if (timings->tREA_max > min_clk_period && !timings->tRLOH_min) 14788c2ecf20Sopenharmony_ci min_clk_period = timings->tREA_max; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci tWB = sunxi_nand_lookup_timing(tWB_lut, timings->tWB_max, 14818c2ecf20Sopenharmony_ci min_clk_period); 14828c2ecf20Sopenharmony_ci if (tWB < 0) { 14838c2ecf20Sopenharmony_ci dev_err(nfc->dev, "unsupported tWB\n"); 14848c2ecf20Sopenharmony_ci return tWB; 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci tADL = DIV_ROUND_UP(timings->tADL_min, min_clk_period) >> 3; 14888c2ecf20Sopenharmony_ci if (tADL > 3) { 14898c2ecf20Sopenharmony_ci dev_err(nfc->dev, "unsupported tADL\n"); 14908c2ecf20Sopenharmony_ci return -EINVAL; 14918c2ecf20Sopenharmony_ci } 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci tWHR = DIV_ROUND_UP(timings->tWHR_min, min_clk_period) >> 3; 14948c2ecf20Sopenharmony_ci if (tWHR > 3) { 14958c2ecf20Sopenharmony_ci dev_err(nfc->dev, "unsupported tWHR\n"); 14968c2ecf20Sopenharmony_ci return -EINVAL; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci tRHW = sunxi_nand_lookup_timing(tRHW_lut, timings->tRHW_min, 15008c2ecf20Sopenharmony_ci min_clk_period); 15018c2ecf20Sopenharmony_ci if (tRHW < 0) { 15028c2ecf20Sopenharmony_ci dev_err(nfc->dev, "unsupported tRHW\n"); 15038c2ecf20Sopenharmony_ci return tRHW; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 15078c2ecf20Sopenharmony_ci return 0; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci /* 15108c2ecf20Sopenharmony_ci * TODO: according to ONFI specs this value only applies for DDR NAND, 15118c2ecf20Sopenharmony_ci * but Allwinner seems to set this to 0x7. Mimic them for now. 15128c2ecf20Sopenharmony_ci */ 15138c2ecf20Sopenharmony_ci tCAD = 0x7; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci /* TODO: A83 has some more bits for CDQSS, CS, CLHZ, CCS, WC */ 15168c2ecf20Sopenharmony_ci sunxi_nand->timing_cfg = NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* Convert min_clk_period from picoseconds to nanoseconds */ 15198c2ecf20Sopenharmony_ci min_clk_period = DIV_ROUND_UP(min_clk_period, 1000); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* 15228c2ecf20Sopenharmony_ci * Unlike what is stated in Allwinner datasheet, the clk_rate should 15238c2ecf20Sopenharmony_ci * be set to (1 / min_clk_period), and not (2 / min_clk_period). 15248c2ecf20Sopenharmony_ci * This new formula was verified with a scope and validated by 15258c2ecf20Sopenharmony_ci * Allwinner engineers. 15268c2ecf20Sopenharmony_ci */ 15278c2ecf20Sopenharmony_ci sunxi_nand->clk_rate = NSEC_PER_SEC / min_clk_period; 15288c2ecf20Sopenharmony_ci real_clk_rate = clk_round_rate(nfc->mod_clk, sunxi_nand->clk_rate); 15298c2ecf20Sopenharmony_ci if (real_clk_rate <= 0) { 15308c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Unable to round clk %lu\n", 15318c2ecf20Sopenharmony_ci sunxi_nand->clk_rate); 15328c2ecf20Sopenharmony_ci return -EINVAL; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci sunxi_nand->timing_ctl = 0; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci /* 15388c2ecf20Sopenharmony_ci * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data 15398c2ecf20Sopenharmony_ci * output cycle timings shall be used if the host drives tRC less than 15408c2ecf20Sopenharmony_ci * 30 ns. We should also use EDO mode if tREA is bigger than tRP. 15418c2ecf20Sopenharmony_ci */ 15428c2ecf20Sopenharmony_ci min_clk_period = NSEC_PER_SEC / real_clk_rate; 15438c2ecf20Sopenharmony_ci if (min_clk_period * 2 < 30 || min_clk_period * 1000 < timings->tREA_max) 15448c2ecf20Sopenharmony_ci sunxi_nand->timing_ctl = NFC_TIMING_CTL_EDO; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci return 0; 15478c2ecf20Sopenharmony_ci} 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section, 15508c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 15538c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (section >= ecc->steps) 15568c2ecf20Sopenharmony_ci return -ERANGE; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci oobregion->offset = section * (ecc->bytes + 4) + 4; 15598c2ecf20Sopenharmony_ci oobregion->length = ecc->bytes; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci return 0; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_cistatic int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section, 15658c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct nand_chip *nand = mtd_to_nand(mtd); 15688c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if (section > ecc->steps) 15718c2ecf20Sopenharmony_ci return -ERANGE; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* 15748c2ecf20Sopenharmony_ci * The first 2 bytes are used for BB markers, hence we 15758c2ecf20Sopenharmony_ci * only have 2 bytes available in the first user data 15768c2ecf20Sopenharmony_ci * section. 15778c2ecf20Sopenharmony_ci */ 15788c2ecf20Sopenharmony_ci if (!section && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { 15798c2ecf20Sopenharmony_ci oobregion->offset = 2; 15808c2ecf20Sopenharmony_ci oobregion->length = 2; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci return 0; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci oobregion->offset = section * (ecc->bytes + 4); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci if (section < ecc->steps) 15888c2ecf20Sopenharmony_ci oobregion->length = 4; 15898c2ecf20Sopenharmony_ci else 15908c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci return 0; 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops sunxi_nand_ooblayout_ops = { 15968c2ecf20Sopenharmony_ci .ecc = sunxi_nand_ooblayout_ecc, 15978c2ecf20Sopenharmony_ci .free = sunxi_nand_ooblayout_free, 15988c2ecf20Sopenharmony_ci}; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_cistatic void sunxi_nand_hw_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) 16018c2ecf20Sopenharmony_ci{ 16028c2ecf20Sopenharmony_ci kfree(ecc->priv); 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_cistatic int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand, 16068c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc, 16078c2ecf20Sopenharmony_ci struct device_node *np) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; 16108c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 16118c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(nand); 16128c2ecf20Sopenharmony_ci struct nand_device *nanddev = mtd_to_nanddev(mtd); 16138c2ecf20Sopenharmony_ci struct sunxi_nand_hw_ecc *data; 16148c2ecf20Sopenharmony_ci int nsectors; 16158c2ecf20Sopenharmony_ci int ret; 16168c2ecf20Sopenharmony_ci int i; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) { 16198c2ecf20Sopenharmony_ci int bytes; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci ecc->size = 1024; 16228c2ecf20Sopenharmony_ci nsectors = mtd->writesize / ecc->size; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci /* Reserve 2 bytes for the BBM */ 16258c2ecf20Sopenharmony_ci bytes = (mtd->oobsize - 2) / nsectors; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci /* 4 non-ECC bytes are added before each ECC bytes section */ 16288c2ecf20Sopenharmony_ci bytes -= 4; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci /* and bytes has to be even. */ 16318c2ecf20Sopenharmony_ci if (bytes % 2) 16328c2ecf20Sopenharmony_ci bytes--; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci ecc->strength = bytes * 8 / fls(8 * ecc->size); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(strengths); i++) { 16378c2ecf20Sopenharmony_ci if (strengths[i] > ecc->strength) 16388c2ecf20Sopenharmony_ci break; 16398c2ecf20Sopenharmony_ci } 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (!i) 16428c2ecf20Sopenharmony_ci ecc->strength = 0; 16438c2ecf20Sopenharmony_ci else 16448c2ecf20Sopenharmony_ci ecc->strength = strengths[i - 1]; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci if (ecc->size != 512 && ecc->size != 1024) 16488c2ecf20Sopenharmony_ci return -EINVAL; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 16518c2ecf20Sopenharmony_ci if (!data) 16528c2ecf20Sopenharmony_ci return -ENOMEM; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci /* Prefer 1k ECC chunk over 512 ones */ 16558c2ecf20Sopenharmony_ci if (ecc->size == 512 && mtd->writesize > 512) { 16568c2ecf20Sopenharmony_ci ecc->size = 1024; 16578c2ecf20Sopenharmony_ci ecc->strength *= 2; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci /* Add ECC info retrieval from DT */ 16618c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(strengths); i++) { 16628c2ecf20Sopenharmony_ci if (ecc->strength <= strengths[i]) { 16638c2ecf20Sopenharmony_ci /* 16648c2ecf20Sopenharmony_ci * Update ecc->strength value with the actual strength 16658c2ecf20Sopenharmony_ci * that will be used by the ECC engine. 16668c2ecf20Sopenharmony_ci */ 16678c2ecf20Sopenharmony_ci ecc->strength = strengths[i]; 16688c2ecf20Sopenharmony_ci break; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(strengths)) { 16738c2ecf20Sopenharmony_ci dev_err(nfc->dev, "unsupported strength\n"); 16748c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 16758c2ecf20Sopenharmony_ci goto err; 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci data->mode = i; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci /* HW ECC always request ECC bytes for 1024 bytes blocks */ 16818c2ecf20Sopenharmony_ci ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci /* HW ECC always work with even numbers of ECC bytes */ 16848c2ecf20Sopenharmony_ci ecc->bytes = ALIGN(ecc->bytes, 2); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci nsectors = mtd->writesize / ecc->size; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { 16898c2ecf20Sopenharmony_ci ret = -EINVAL; 16908c2ecf20Sopenharmony_ci goto err; 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci ecc->read_oob = sunxi_nfc_hw_ecc_read_oob; 16948c2ecf20Sopenharmony_ci ecc->write_oob = sunxi_nfc_hw_ecc_write_oob; 16958c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &sunxi_nand_ooblayout_ops); 16968c2ecf20Sopenharmony_ci ecc->priv = data; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (nfc->dmac) { 16998c2ecf20Sopenharmony_ci ecc->read_page = sunxi_nfc_hw_ecc_read_page_dma; 17008c2ecf20Sopenharmony_ci ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage_dma; 17018c2ecf20Sopenharmony_ci ecc->write_page = sunxi_nfc_hw_ecc_write_page_dma; 17028c2ecf20Sopenharmony_ci nand->options |= NAND_USES_DMA; 17038c2ecf20Sopenharmony_ci } else { 17048c2ecf20Sopenharmony_ci ecc->read_page = sunxi_nfc_hw_ecc_read_page; 17058c2ecf20Sopenharmony_ci ecc->read_subpage = sunxi_nfc_hw_ecc_read_subpage; 17068c2ecf20Sopenharmony_ci ecc->write_page = sunxi_nfc_hw_ecc_write_page; 17078c2ecf20Sopenharmony_ci } 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci /* TODO: support DMA for raw accesses and subpage write */ 17108c2ecf20Sopenharmony_ci ecc->write_subpage = sunxi_nfc_hw_ecc_write_subpage; 17118c2ecf20Sopenharmony_ci ecc->read_oob_raw = nand_read_oob_std; 17128c2ecf20Sopenharmony_ci ecc->write_oob_raw = nand_write_oob_std; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci return 0; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_cierr: 17178c2ecf20Sopenharmony_ci kfree(data); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci return ret; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_cistatic void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) 17238c2ecf20Sopenharmony_ci{ 17248c2ecf20Sopenharmony_ci switch (ecc->engine_type) { 17258c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 17268c2ecf20Sopenharmony_ci sunxi_nand_hw_ecc_ctrl_cleanup(ecc); 17278c2ecf20Sopenharmony_ci break; 17288c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 17298c2ecf20Sopenharmony_ci default: 17308c2ecf20Sopenharmony_ci break; 17318c2ecf20Sopenharmony_ci } 17328c2ecf20Sopenharmony_ci} 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_cistatic int sunxi_nand_attach_chip(struct nand_chip *nand) 17358c2ecf20Sopenharmony_ci{ 17368c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 17378c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&nand->base); 17388c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &nand->ecc; 17398c2ecf20Sopenharmony_ci struct device_node *np = nand_get_flash_node(nand); 17408c2ecf20Sopenharmony_ci int ret; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if (nand->bbt_options & NAND_BBT_USE_FLASH) 17438c2ecf20Sopenharmony_ci nand->bbt_options |= NAND_BBT_NO_OOB; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (nand->options & NAND_NEED_SCRAMBLING) 17468c2ecf20Sopenharmony_ci nand->options |= NAND_NO_SUBPAGE_WRITE; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci nand->options |= NAND_SUBPAGE_READ; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci if (!ecc->size) { 17518c2ecf20Sopenharmony_ci ecc->size = requirements->step_size; 17528c2ecf20Sopenharmony_ci ecc->strength = requirements->strength; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (!ecc->size || !ecc->strength) 17568c2ecf20Sopenharmony_ci return -EINVAL; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci switch (ecc->engine_type) { 17598c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 17608c2ecf20Sopenharmony_ci ret = sunxi_nand_hw_ecc_ctrl_init(nand, ecc, np); 17618c2ecf20Sopenharmony_ci if (ret) 17628c2ecf20Sopenharmony_ci return ret; 17638c2ecf20Sopenharmony_ci break; 17648c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 17658c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 17668c2ecf20Sopenharmony_ci break; 17678c2ecf20Sopenharmony_ci default: 17688c2ecf20Sopenharmony_ci return -EINVAL; 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci return 0; 17728c2ecf20Sopenharmony_ci} 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_cistatic int sunxi_nfc_exec_subop(struct nand_chip *nand, 17758c2ecf20Sopenharmony_ci const struct nand_subop *subop) 17768c2ecf20Sopenharmony_ci{ 17778c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); 17788c2ecf20Sopenharmony_ci u32 cmd = 0, extcmd = 0, cnt = 0, addrs[2] = { }; 17798c2ecf20Sopenharmony_ci unsigned int i, j, remaining, start; 17808c2ecf20Sopenharmony_ci void *inbuf = NULL; 17818c2ecf20Sopenharmony_ci int ret; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci for (i = 0; i < subop->ninstrs; i++) { 17848c2ecf20Sopenharmony_ci const struct nand_op_instr *instr = &subop->instrs[i]; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci switch (instr->type) { 17878c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 17888c2ecf20Sopenharmony_ci if (cmd & NFC_SEND_CMD1) { 17898c2ecf20Sopenharmony_ci if (WARN_ON(cmd & NFC_SEND_CMD2)) 17908c2ecf20Sopenharmony_ci return -EINVAL; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci cmd |= NFC_SEND_CMD2; 17938c2ecf20Sopenharmony_ci extcmd |= instr->ctx.cmd.opcode; 17948c2ecf20Sopenharmony_ci } else { 17958c2ecf20Sopenharmony_ci cmd |= NFC_SEND_CMD1 | 17968c2ecf20Sopenharmony_ci NFC_CMD(instr->ctx.cmd.opcode); 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci break; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 18018c2ecf20Sopenharmony_ci remaining = nand_subop_get_num_addr_cyc(subop, i); 18028c2ecf20Sopenharmony_ci start = nand_subop_get_addr_start_off(subop, i); 18038c2ecf20Sopenharmony_ci for (j = 0; j < 8 && j + start < remaining; j++) { 18048c2ecf20Sopenharmony_ci u32 addr = instr->ctx.addr.addrs[j + start]; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci addrs[j / 4] |= addr << (j % 4) * 8; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (j) 18108c2ecf20Sopenharmony_ci cmd |= NFC_SEND_ADR | NFC_ADR_NUM(j); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci break; 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 18158c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 18168c2ecf20Sopenharmony_ci start = nand_subop_get_data_start_off(subop, i); 18178c2ecf20Sopenharmony_ci remaining = nand_subop_get_data_len(subop, i); 18188c2ecf20Sopenharmony_ci cnt = min_t(u32, remaining, NFC_SRAM_SIZE); 18198c2ecf20Sopenharmony_ci cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (instr->type == NAND_OP_DATA_OUT_INSTR) { 18228c2ecf20Sopenharmony_ci cmd |= NFC_ACCESS_DIR; 18238c2ecf20Sopenharmony_ci memcpy_toio(nfc->regs + NFC_RAM0_BASE, 18248c2ecf20Sopenharmony_ci instr->ctx.data.buf.out + start, 18258c2ecf20Sopenharmony_ci cnt); 18268c2ecf20Sopenharmony_ci } else { 18278c2ecf20Sopenharmony_ci inbuf = instr->ctx.data.buf.in + start; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci break; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 18338c2ecf20Sopenharmony_ci cmd |= NFC_WAIT_FLAG; 18348c2ecf20Sopenharmony_ci break; 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); 18398c2ecf20Sopenharmony_ci if (ret) 18408c2ecf20Sopenharmony_ci return ret; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (cmd & NFC_SEND_ADR) { 18438c2ecf20Sopenharmony_ci writel(addrs[0], nfc->regs + NFC_REG_ADDR_LOW); 18448c2ecf20Sopenharmony_ci writel(addrs[1], nfc->regs + NFC_REG_ADDR_HIGH); 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci if (cmd & NFC_SEND_CMD2) 18488c2ecf20Sopenharmony_ci writel(extcmd, 18498c2ecf20Sopenharmony_ci nfc->regs + 18508c2ecf20Sopenharmony_ci (cmd & NFC_ACCESS_DIR ? 18518c2ecf20Sopenharmony_ci NFC_REG_WCMD_SET : NFC_REG_RCMD_SET)); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci if (cmd & NFC_DATA_TRANS) 18548c2ecf20Sopenharmony_ci writel(cnt, nfc->regs + NFC_REG_CNT); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci writel(cmd, nfc->regs + NFC_REG_CMD); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, 18598c2ecf20Sopenharmony_ci !(cmd & NFC_WAIT_FLAG) && cnt < 64, 18608c2ecf20Sopenharmony_ci 0); 18618c2ecf20Sopenharmony_ci if (ret) 18628c2ecf20Sopenharmony_ci return ret; 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (inbuf) 18658c2ecf20Sopenharmony_ci memcpy_fromio(inbuf, nfc->regs + NFC_RAM0_BASE, cnt); 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci return 0; 18688c2ecf20Sopenharmony_ci} 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistatic int sunxi_nfc_soft_waitrdy(struct nand_chip *nand, 18718c2ecf20Sopenharmony_ci const struct nand_subop *subop) 18728c2ecf20Sopenharmony_ci{ 18738c2ecf20Sopenharmony_ci return nand_soft_waitrdy(nand, 18748c2ecf20Sopenharmony_ci subop->instrs[0].ctx.waitrdy.timeout_ms); 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic const struct nand_op_parser sunxi_nfc_op_parser = NAND_OP_PARSER( 18788c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 18798c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 18808c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 18818c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 18828c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), 18838c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)), 18848c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 18858c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 18868c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 18878c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024), 18888c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 18898c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), 18908c2ecf20Sopenharmony_ci); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_cistatic const struct nand_op_parser sunxi_nfc_norb_op_parser = NAND_OP_PARSER( 18938c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 18948c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 18958c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 18968c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 18978c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 1024)), 18988c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_exec_subop, 18998c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 19008c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 19018c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(true, 1024), 19028c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true)), 19038c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(sunxi_nfc_soft_waitrdy, 19048c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 19058c2ecf20Sopenharmony_ci); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_cistatic int sunxi_nfc_exec_op(struct nand_chip *nand, 19088c2ecf20Sopenharmony_ci const struct nand_operation *op, bool check_only) 19098c2ecf20Sopenharmony_ci{ 19108c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); 19118c2ecf20Sopenharmony_ci const struct nand_op_parser *parser; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (!check_only) 19148c2ecf20Sopenharmony_ci sunxi_nfc_select_chip(nand, op->cs); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci if (sunxi_nand->sels[op->cs].rb >= 0) 19178c2ecf20Sopenharmony_ci parser = &sunxi_nfc_op_parser; 19188c2ecf20Sopenharmony_ci else 19198c2ecf20Sopenharmony_ci parser = &sunxi_nfc_norb_op_parser; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci return nand_op_parser_exec_op(nand, parser, op, check_only); 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic const struct nand_controller_ops sunxi_nand_controller_ops = { 19258c2ecf20Sopenharmony_ci .attach_chip = sunxi_nand_attach_chip, 19268c2ecf20Sopenharmony_ci .setup_interface = sunxi_nfc_setup_interface, 19278c2ecf20Sopenharmony_ci .exec_op = sunxi_nfc_exec_op, 19288c2ecf20Sopenharmony_ci}; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_cistatic int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc, 19318c2ecf20Sopenharmony_ci struct device_node *np) 19328c2ecf20Sopenharmony_ci{ 19338c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand; 19348c2ecf20Sopenharmony_ci struct mtd_info *mtd; 19358c2ecf20Sopenharmony_ci struct nand_chip *nand; 19368c2ecf20Sopenharmony_ci int nsels; 19378c2ecf20Sopenharmony_ci int ret; 19388c2ecf20Sopenharmony_ci int i; 19398c2ecf20Sopenharmony_ci u32 tmp; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci if (!of_get_property(np, "reg", &nsels)) 19428c2ecf20Sopenharmony_ci return -EINVAL; 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci nsels /= sizeof(u32); 19458c2ecf20Sopenharmony_ci if (!nsels) { 19468c2ecf20Sopenharmony_ci dev_err(dev, "invalid reg property size\n"); 19478c2ecf20Sopenharmony_ci return -EINVAL; 19488c2ecf20Sopenharmony_ci } 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci sunxi_nand = devm_kzalloc(dev, struct_size(sunxi_nand, sels, nsels), 19518c2ecf20Sopenharmony_ci GFP_KERNEL); 19528c2ecf20Sopenharmony_ci if (!sunxi_nand) { 19538c2ecf20Sopenharmony_ci dev_err(dev, "could not allocate chip\n"); 19548c2ecf20Sopenharmony_ci return -ENOMEM; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci sunxi_nand->nsels = nsels; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci for (i = 0; i < nsels; i++) { 19608c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, "reg", i, &tmp); 19618c2ecf20Sopenharmony_ci if (ret) { 19628c2ecf20Sopenharmony_ci dev_err(dev, "could not retrieve reg property: %d\n", 19638c2ecf20Sopenharmony_ci ret); 19648c2ecf20Sopenharmony_ci return ret; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (tmp > NFC_MAX_CS) { 19688c2ecf20Sopenharmony_ci dev_err(dev, 19698c2ecf20Sopenharmony_ci "invalid reg value: %u (max CS = 7)\n", 19708c2ecf20Sopenharmony_ci tmp); 19718c2ecf20Sopenharmony_ci return -EINVAL; 19728c2ecf20Sopenharmony_ci } 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if (test_and_set_bit(tmp, &nfc->assigned_cs)) { 19758c2ecf20Sopenharmony_ci dev_err(dev, "CS %d already assigned\n", tmp); 19768c2ecf20Sopenharmony_ci return -EINVAL; 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci sunxi_nand->sels[i].cs = tmp; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) && 19828c2ecf20Sopenharmony_ci tmp < 2) 19838c2ecf20Sopenharmony_ci sunxi_nand->sels[i].rb = tmp; 19848c2ecf20Sopenharmony_ci else 19858c2ecf20Sopenharmony_ci sunxi_nand->sels[i].rb = -1; 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci nand = &sunxi_nand->nand; 19898c2ecf20Sopenharmony_ci /* Default tR value specified in the ONFI spec (chapter 4.15.1) */ 19908c2ecf20Sopenharmony_ci nand->controller = &nfc->controller; 19918c2ecf20Sopenharmony_ci nand->controller->ops = &sunxi_nand_controller_ops; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci /* 19948c2ecf20Sopenharmony_ci * Set the ECC mode to the default value in case nothing is specified 19958c2ecf20Sopenharmony_ci * in the DT. 19968c2ecf20Sopenharmony_ci */ 19978c2ecf20Sopenharmony_ci nand->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 19988c2ecf20Sopenharmony_ci nand_set_flash_node(nand, np); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci mtd = nand_to_mtd(nand); 20018c2ecf20Sopenharmony_ci mtd->dev.parent = dev; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci ret = nand_scan(nand, nsels); 20048c2ecf20Sopenharmony_ci if (ret) 20058c2ecf20Sopenharmony_ci return ret; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 20088c2ecf20Sopenharmony_ci if (ret) { 20098c2ecf20Sopenharmony_ci dev_err(dev, "failed to register mtd device: %d\n", ret); 20108c2ecf20Sopenharmony_ci nand_cleanup(nand); 20118c2ecf20Sopenharmony_ci return ret; 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci list_add_tail(&sunxi_nand->node, &nfc->chips); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci return 0; 20178c2ecf20Sopenharmony_ci} 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_cistatic int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc) 20208c2ecf20Sopenharmony_ci{ 20218c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 20228c2ecf20Sopenharmony_ci struct device_node *nand_np; 20238c2ecf20Sopenharmony_ci int nchips = of_get_child_count(np); 20248c2ecf20Sopenharmony_ci int ret; 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (nchips > 8) { 20278c2ecf20Sopenharmony_ci dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips); 20288c2ecf20Sopenharmony_ci return -EINVAL; 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci for_each_child_of_node(np, nand_np) { 20328c2ecf20Sopenharmony_ci ret = sunxi_nand_chip_init(dev, nfc, nand_np); 20338c2ecf20Sopenharmony_ci if (ret) { 20348c2ecf20Sopenharmony_ci of_node_put(nand_np); 20358c2ecf20Sopenharmony_ci return ret; 20368c2ecf20Sopenharmony_ci } 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci return 0; 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_cistatic void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct sunxi_nand_chip *sunxi_nand; 20458c2ecf20Sopenharmony_ci struct nand_chip *chip; 20468c2ecf20Sopenharmony_ci int ret; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci while (!list_empty(&nfc->chips)) { 20498c2ecf20Sopenharmony_ci sunxi_nand = list_first_entry(&nfc->chips, 20508c2ecf20Sopenharmony_ci struct sunxi_nand_chip, 20518c2ecf20Sopenharmony_ci node); 20528c2ecf20Sopenharmony_ci chip = &sunxi_nand->nand; 20538c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 20548c2ecf20Sopenharmony_ci WARN_ON(ret); 20558c2ecf20Sopenharmony_ci nand_cleanup(chip); 20568c2ecf20Sopenharmony_ci sunxi_nand_ecc_cleanup(&chip->ecc); 20578c2ecf20Sopenharmony_ci list_del(&sunxi_nand->node); 20588c2ecf20Sopenharmony_ci } 20598c2ecf20Sopenharmony_ci} 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_cistatic int sunxi_nfc_probe(struct platform_device *pdev) 20628c2ecf20Sopenharmony_ci{ 20638c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 20648c2ecf20Sopenharmony_ci struct resource *r; 20658c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc; 20668c2ecf20Sopenharmony_ci int irq; 20678c2ecf20Sopenharmony_ci int ret; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL); 20708c2ecf20Sopenharmony_ci if (!nfc) 20718c2ecf20Sopenharmony_ci return -ENOMEM; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci nfc->dev = dev; 20748c2ecf20Sopenharmony_ci nand_controller_init(&nfc->controller); 20758c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nfc->chips); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 20788c2ecf20Sopenharmony_ci nfc->regs = devm_ioremap_resource(dev, r); 20798c2ecf20Sopenharmony_ci if (IS_ERR(nfc->regs)) 20808c2ecf20Sopenharmony_ci return PTR_ERR(nfc->regs); 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 20838c2ecf20Sopenharmony_ci if (irq < 0) 20848c2ecf20Sopenharmony_ci return irq; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci nfc->ahb_clk = devm_clk_get(dev, "ahb"); 20878c2ecf20Sopenharmony_ci if (IS_ERR(nfc->ahb_clk)) { 20888c2ecf20Sopenharmony_ci dev_err(dev, "failed to retrieve ahb clk\n"); 20898c2ecf20Sopenharmony_ci return PTR_ERR(nfc->ahb_clk); 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->ahb_clk); 20938c2ecf20Sopenharmony_ci if (ret) 20948c2ecf20Sopenharmony_ci return ret; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci nfc->mod_clk = devm_clk_get(dev, "mod"); 20978c2ecf20Sopenharmony_ci if (IS_ERR(nfc->mod_clk)) { 20988c2ecf20Sopenharmony_ci dev_err(dev, "failed to retrieve mod clk\n"); 20998c2ecf20Sopenharmony_ci ret = PTR_ERR(nfc->mod_clk); 21008c2ecf20Sopenharmony_ci goto out_ahb_clk_unprepare; 21018c2ecf20Sopenharmony_ci } 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->mod_clk); 21048c2ecf20Sopenharmony_ci if (ret) 21058c2ecf20Sopenharmony_ci goto out_ahb_clk_unprepare; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); 21088c2ecf20Sopenharmony_ci if (IS_ERR(nfc->reset)) { 21098c2ecf20Sopenharmony_ci ret = PTR_ERR(nfc->reset); 21108c2ecf20Sopenharmony_ci goto out_mod_clk_unprepare; 21118c2ecf20Sopenharmony_ci } 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci ret = reset_control_deassert(nfc->reset); 21148c2ecf20Sopenharmony_ci if (ret) { 21158c2ecf20Sopenharmony_ci dev_err(dev, "reset err %d\n", ret); 21168c2ecf20Sopenharmony_ci goto out_mod_clk_unprepare; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci nfc->caps = of_device_get_match_data(&pdev->dev); 21208c2ecf20Sopenharmony_ci if (!nfc->caps) { 21218c2ecf20Sopenharmony_ci ret = -EINVAL; 21228c2ecf20Sopenharmony_ci goto out_ahb_reset_reassert; 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci ret = sunxi_nfc_rst(nfc); 21268c2ecf20Sopenharmony_ci if (ret) 21278c2ecf20Sopenharmony_ci goto out_ahb_reset_reassert; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci writel(0, nfc->regs + NFC_REG_INT); 21308c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt, 21318c2ecf20Sopenharmony_ci 0, "sunxi-nand", nfc); 21328c2ecf20Sopenharmony_ci if (ret) 21338c2ecf20Sopenharmony_ci goto out_ahb_reset_reassert; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci nfc->dmac = dma_request_chan(dev, "rxtx"); 21368c2ecf20Sopenharmony_ci if (IS_ERR(nfc->dmac)) { 21378c2ecf20Sopenharmony_ci ret = PTR_ERR(nfc->dmac); 21388c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 21398c2ecf20Sopenharmony_ci goto out_ahb_reset_reassert; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci /* Ignore errors to fall back to PIO mode */ 21428c2ecf20Sopenharmony_ci dev_warn(dev, "failed to request rxtx DMA channel: %d\n", ret); 21438c2ecf20Sopenharmony_ci nfc->dmac = NULL; 21448c2ecf20Sopenharmony_ci } else { 21458c2ecf20Sopenharmony_ci struct dma_slave_config dmac_cfg = { }; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci dmac_cfg.src_addr = r->start + nfc->caps->reg_io_data; 21488c2ecf20Sopenharmony_ci dmac_cfg.dst_addr = dmac_cfg.src_addr; 21498c2ecf20Sopenharmony_ci dmac_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 21508c2ecf20Sopenharmony_ci dmac_cfg.dst_addr_width = dmac_cfg.src_addr_width; 21518c2ecf20Sopenharmony_ci dmac_cfg.src_maxburst = nfc->caps->dma_maxburst; 21528c2ecf20Sopenharmony_ci dmac_cfg.dst_maxburst = nfc->caps->dma_maxburst; 21538c2ecf20Sopenharmony_ci dmaengine_slave_config(nfc->dmac, &dmac_cfg); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci if (nfc->caps->extra_mbus_conf) 21568c2ecf20Sopenharmony_ci writel(readl(nfc->regs + NFC_REG_CTL) | 21578c2ecf20Sopenharmony_ci NFC_DMA_TYPE_NORMAL, nfc->regs + NFC_REG_CTL); 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, nfc); 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci ret = sunxi_nand_chips_init(dev, nfc); 21638c2ecf20Sopenharmony_ci if (ret) { 21648c2ecf20Sopenharmony_ci dev_err(dev, "failed to init nand chips\n"); 21658c2ecf20Sopenharmony_ci goto out_release_dmac; 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci return 0; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ciout_release_dmac: 21718c2ecf20Sopenharmony_ci if (nfc->dmac) 21728c2ecf20Sopenharmony_ci dma_release_channel(nfc->dmac); 21738c2ecf20Sopenharmony_ciout_ahb_reset_reassert: 21748c2ecf20Sopenharmony_ci reset_control_assert(nfc->reset); 21758c2ecf20Sopenharmony_ciout_mod_clk_unprepare: 21768c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->mod_clk); 21778c2ecf20Sopenharmony_ciout_ahb_clk_unprepare: 21788c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->ahb_clk); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci return ret; 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cistatic int sunxi_nfc_remove(struct platform_device *pdev) 21848c2ecf20Sopenharmony_ci{ 21858c2ecf20Sopenharmony_ci struct sunxi_nfc *nfc = platform_get_drvdata(pdev); 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci sunxi_nand_chips_cleanup(nfc); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci reset_control_assert(nfc->reset); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (nfc->dmac) 21928c2ecf20Sopenharmony_ci dma_release_channel(nfc->dmac); 21938c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->mod_clk); 21948c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->ahb_clk); 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci return 0; 21978c2ecf20Sopenharmony_ci} 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_cistatic const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { 22008c2ecf20Sopenharmony_ci .reg_io_data = NFC_REG_A10_IO_DATA, 22018c2ecf20Sopenharmony_ci .dma_maxburst = 4, 22028c2ecf20Sopenharmony_ci}; 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_cistatic const struct sunxi_nfc_caps sunxi_nfc_a23_caps = { 22058c2ecf20Sopenharmony_ci .extra_mbus_conf = true, 22068c2ecf20Sopenharmony_ci .reg_io_data = NFC_REG_A23_IO_DATA, 22078c2ecf20Sopenharmony_ci .dma_maxburst = 8, 22088c2ecf20Sopenharmony_ci}; 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_cistatic const struct of_device_id sunxi_nfc_ids[] = { 22118c2ecf20Sopenharmony_ci { 22128c2ecf20Sopenharmony_ci .compatible = "allwinner,sun4i-a10-nand", 22138c2ecf20Sopenharmony_ci .data = &sunxi_nfc_a10_caps, 22148c2ecf20Sopenharmony_ci }, 22158c2ecf20Sopenharmony_ci { 22168c2ecf20Sopenharmony_ci .compatible = "allwinner,sun8i-a23-nand-controller", 22178c2ecf20Sopenharmony_ci .data = &sunxi_nfc_a23_caps, 22188c2ecf20Sopenharmony_ci }, 22198c2ecf20Sopenharmony_ci { /* sentinel */ } 22208c2ecf20Sopenharmony_ci}; 22218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sunxi_nfc_ids); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_cistatic struct platform_driver sunxi_nfc_driver = { 22248c2ecf20Sopenharmony_ci .driver = { 22258c2ecf20Sopenharmony_ci .name = "sunxi_nand", 22268c2ecf20Sopenharmony_ci .of_match_table = sunxi_nfc_ids, 22278c2ecf20Sopenharmony_ci }, 22288c2ecf20Sopenharmony_ci .probe = sunxi_nfc_probe, 22298c2ecf20Sopenharmony_ci .remove = sunxi_nfc_remove, 22308c2ecf20Sopenharmony_ci}; 22318c2ecf20Sopenharmony_cimodule_platform_driver(sunxi_nfc_driver); 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 22348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris BREZILLON"); 22358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); 22368c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sunxi_nand"); 2237