18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Arasan NAND Flash Controller Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 - 2020 Xilinx, Inc. 68c2ecf20Sopenharmony_ci * Author: 78c2ecf20Sopenharmony_ci * Miquel Raynal <miquel.raynal@bootlin.com> 88c2ecf20Sopenharmony_ci * Original work (fully rewritten): 98c2ecf20Sopenharmony_ci * Punnaiah Choudary Kalluri <punnaia@xilinx.com> 108c2ecf20Sopenharmony_ci * Naga Sureshkumar Relli <nagasure@xilinx.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/bch.h> 148c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 228c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 238c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define PKT_REG 0x00 298c2ecf20Sopenharmony_ci#define PKT_SIZE(x) FIELD_PREP(GENMASK(10, 0), (x)) 308c2ecf20Sopenharmony_ci#define PKT_STEPS(x) FIELD_PREP(GENMASK(23, 12), (x)) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MEM_ADDR1_REG 0x04 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MEM_ADDR2_REG 0x08 358c2ecf20Sopenharmony_ci#define ADDR2_STRENGTH(x) FIELD_PREP(GENMASK(27, 25), (x)) 368c2ecf20Sopenharmony_ci#define ADDR2_CS(x) FIELD_PREP(GENMASK(31, 30), (x)) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define CMD_REG 0x0C 398c2ecf20Sopenharmony_ci#define CMD_1(x) FIELD_PREP(GENMASK(7, 0), (x)) 408c2ecf20Sopenharmony_ci#define CMD_2(x) FIELD_PREP(GENMASK(15, 8), (x)) 418c2ecf20Sopenharmony_ci#define CMD_PAGE_SIZE(x) FIELD_PREP(GENMASK(25, 23), (x)) 428c2ecf20Sopenharmony_ci#define CMD_DMA_ENABLE BIT(27) 438c2ecf20Sopenharmony_ci#define CMD_NADDRS(x) FIELD_PREP(GENMASK(30, 28), (x)) 448c2ecf20Sopenharmony_ci#define CMD_ECC_ENABLE BIT(31) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define PROG_REG 0x10 478c2ecf20Sopenharmony_ci#define PROG_PGRD BIT(0) 488c2ecf20Sopenharmony_ci#define PROG_ERASE BIT(2) 498c2ecf20Sopenharmony_ci#define PROG_STATUS BIT(3) 508c2ecf20Sopenharmony_ci#define PROG_PGPROG BIT(4) 518c2ecf20Sopenharmony_ci#define PROG_RDID BIT(6) 528c2ecf20Sopenharmony_ci#define PROG_RDPARAM BIT(7) 538c2ecf20Sopenharmony_ci#define PROG_RST BIT(8) 548c2ecf20Sopenharmony_ci#define PROG_GET_FEATURE BIT(9) 558c2ecf20Sopenharmony_ci#define PROG_SET_FEATURE BIT(10) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define INTR_STS_EN_REG 0x14 588c2ecf20Sopenharmony_ci#define INTR_SIG_EN_REG 0x18 598c2ecf20Sopenharmony_ci#define INTR_STS_REG 0x1C 608c2ecf20Sopenharmony_ci#define WRITE_READY BIT(0) 618c2ecf20Sopenharmony_ci#define READ_READY BIT(1) 628c2ecf20Sopenharmony_ci#define XFER_COMPLETE BIT(2) 638c2ecf20Sopenharmony_ci#define DMA_BOUNDARY BIT(6) 648c2ecf20Sopenharmony_ci#define EVENT_MASK GENMASK(7, 0) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define READY_STS_REG 0x20 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define DMA_ADDR0_REG 0x50 698c2ecf20Sopenharmony_ci#define DMA_ADDR1_REG 0x24 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define FLASH_STS_REG 0x28 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define DATA_PORT_REG 0x30 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define ECC_CONF_REG 0x34 768c2ecf20Sopenharmony_ci#define ECC_CONF_COL(x) FIELD_PREP(GENMASK(15, 0), (x)) 778c2ecf20Sopenharmony_ci#define ECC_CONF_LEN(x) FIELD_PREP(GENMASK(26, 16), (x)) 788c2ecf20Sopenharmony_ci#define ECC_CONF_BCH_EN BIT(27) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define ECC_ERR_CNT_REG 0x38 818c2ecf20Sopenharmony_ci#define GET_PKT_ERR_CNT(x) FIELD_GET(GENMASK(7, 0), (x)) 828c2ecf20Sopenharmony_ci#define GET_PAGE_ERR_CNT(x) FIELD_GET(GENMASK(16, 8), (x)) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define ECC_SP_REG 0x3C 858c2ecf20Sopenharmony_ci#define ECC_SP_CMD1(x) FIELD_PREP(GENMASK(7, 0), (x)) 868c2ecf20Sopenharmony_ci#define ECC_SP_CMD2(x) FIELD_PREP(GENMASK(15, 8), (x)) 878c2ecf20Sopenharmony_ci#define ECC_SP_ADDRS(x) FIELD_PREP(GENMASK(30, 28), (x)) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define ECC_1ERR_CNT_REG 0x40 908c2ecf20Sopenharmony_ci#define ECC_2ERR_CNT_REG 0x44 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define DATA_INTERFACE_REG 0x6C 938c2ecf20Sopenharmony_ci#define DIFACE_SDR_MODE(x) FIELD_PREP(GENMASK(2, 0), (x)) 948c2ecf20Sopenharmony_ci#define DIFACE_DDR_MODE(x) FIELD_PREP(GENMASK(5, 3), (x)) 958c2ecf20Sopenharmony_ci#define DIFACE_SDR 0 968c2ecf20Sopenharmony_ci#define DIFACE_NVDDR BIT(9) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define ANFC_MAX_CS 2 998c2ecf20Sopenharmony_ci#define ANFC_DFLT_TIMEOUT_US 1000000 1008c2ecf20Sopenharmony_ci#define ANFC_MAX_CHUNK_SIZE SZ_1M 1018c2ecf20Sopenharmony_ci#define ANFC_MAX_PARAM_SIZE SZ_4K 1028c2ecf20Sopenharmony_ci#define ANFC_MAX_STEPS SZ_2K 1038c2ecf20Sopenharmony_ci#define ANFC_MAX_PKT_SIZE (SZ_2K - 1) 1048c2ecf20Sopenharmony_ci#define ANFC_MAX_ADDR_CYC 5U 1058c2ecf20Sopenharmony_ci#define ANFC_RSVD_ECC_BYTES 21 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define ANFC_XLNX_SDR_DFLT_CORE_CLK 100000000 1088c2ecf20Sopenharmony_ci#define ANFC_XLNX_SDR_HS_CORE_CLK 80000000 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * struct anfc_op - Defines how to execute an operation 1128c2ecf20Sopenharmony_ci * @pkt_reg: Packet register 1138c2ecf20Sopenharmony_ci * @addr1_reg: Memory address 1 register 1148c2ecf20Sopenharmony_ci * @addr2_reg: Memory address 2 register 1158c2ecf20Sopenharmony_ci * @cmd_reg: Command register 1168c2ecf20Sopenharmony_ci * @prog_reg: Program register 1178c2ecf20Sopenharmony_ci * @steps: Number of "packets" to read/write 1188c2ecf20Sopenharmony_ci * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin 1198c2ecf20Sopenharmony_ci * @len: Data transfer length 1208c2ecf20Sopenharmony_ci * @read: Data transfer direction from the controller point of view 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistruct anfc_op { 1238c2ecf20Sopenharmony_ci u32 pkt_reg; 1248c2ecf20Sopenharmony_ci u32 addr1_reg; 1258c2ecf20Sopenharmony_ci u32 addr2_reg; 1268c2ecf20Sopenharmony_ci u32 cmd_reg; 1278c2ecf20Sopenharmony_ci u32 prog_reg; 1288c2ecf20Sopenharmony_ci int steps; 1298c2ecf20Sopenharmony_ci unsigned int rdy_timeout_ms; 1308c2ecf20Sopenharmony_ci unsigned int len; 1318c2ecf20Sopenharmony_ci bool read; 1328c2ecf20Sopenharmony_ci u8 *buf; 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/** 1368c2ecf20Sopenharmony_ci * struct anand - Defines the NAND chip related information 1378c2ecf20Sopenharmony_ci * @node: Used to store NAND chips into a list 1388c2ecf20Sopenharmony_ci * @chip: NAND chip information structure 1398c2ecf20Sopenharmony_ci * @cs: Chip select line 1408c2ecf20Sopenharmony_ci * @rb: Ready-busy line 1418c2ecf20Sopenharmony_ci * @page_sz: Register value of the page_sz field to use 1428c2ecf20Sopenharmony_ci * @clk: Expected clock frequency to use 1438c2ecf20Sopenharmony_ci * @timings: Data interface timing mode to use 1448c2ecf20Sopenharmony_ci * @ecc_conf: Hardware ECC configuration value 1458c2ecf20Sopenharmony_ci * @strength: Register value of the ECC strength 1468c2ecf20Sopenharmony_ci * @raddr_cycles: Row address cycle information 1478c2ecf20Sopenharmony_ci * @caddr_cycles: Column address cycle information 1488c2ecf20Sopenharmony_ci * @ecc_bits: Exact number of ECC bits per syndrome 1498c2ecf20Sopenharmony_ci * @ecc_total: Total number of ECC bytes 1508c2ecf20Sopenharmony_ci * @errloc: Array of errors located with soft BCH 1518c2ecf20Sopenharmony_ci * @hw_ecc: Buffer to store syndromes computed by hardware 1528c2ecf20Sopenharmony_ci * @bch: BCH structure 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistruct anand { 1558c2ecf20Sopenharmony_ci struct list_head node; 1568c2ecf20Sopenharmony_ci struct nand_chip chip; 1578c2ecf20Sopenharmony_ci unsigned int cs; 1588c2ecf20Sopenharmony_ci unsigned int rb; 1598c2ecf20Sopenharmony_ci unsigned int page_sz; 1608c2ecf20Sopenharmony_ci unsigned long clk; 1618c2ecf20Sopenharmony_ci u32 timings; 1628c2ecf20Sopenharmony_ci u32 ecc_conf; 1638c2ecf20Sopenharmony_ci u32 strength; 1648c2ecf20Sopenharmony_ci u16 raddr_cycles; 1658c2ecf20Sopenharmony_ci u16 caddr_cycles; 1668c2ecf20Sopenharmony_ci unsigned int ecc_bits; 1678c2ecf20Sopenharmony_ci unsigned int ecc_total; 1688c2ecf20Sopenharmony_ci unsigned int *errloc; 1698c2ecf20Sopenharmony_ci u8 *hw_ecc; 1708c2ecf20Sopenharmony_ci struct bch_control *bch; 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/** 1748c2ecf20Sopenharmony_ci * struct arasan_nfc - Defines the Arasan NAND flash controller driver instance 1758c2ecf20Sopenharmony_ci * @dev: Pointer to the device structure 1768c2ecf20Sopenharmony_ci * @base: Remapped register area 1778c2ecf20Sopenharmony_ci * @controller_clk: Pointer to the system clock 1788c2ecf20Sopenharmony_ci * @bus_clk: Pointer to the flash clock 1798c2ecf20Sopenharmony_ci * @controller: Base controller structure 1808c2ecf20Sopenharmony_ci * @chips: List of all NAND chips attached to the controller 1818c2ecf20Sopenharmony_ci * @assigned_cs: Bitmask describing already assigned CS lines 1828c2ecf20Sopenharmony_ci * @cur_clk: Current clock rate 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_cistruct arasan_nfc { 1858c2ecf20Sopenharmony_ci struct device *dev; 1868c2ecf20Sopenharmony_ci void __iomem *base; 1878c2ecf20Sopenharmony_ci struct clk *controller_clk; 1888c2ecf20Sopenharmony_ci struct clk *bus_clk; 1898c2ecf20Sopenharmony_ci struct nand_controller controller; 1908c2ecf20Sopenharmony_ci struct list_head chips; 1918c2ecf20Sopenharmony_ci unsigned long assigned_cs; 1928c2ecf20Sopenharmony_ci unsigned int cur_clk; 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic struct anand *to_anand(struct nand_chip *nand) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci return container_of(nand, struct anand, chip); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic struct arasan_nfc *to_anfc(struct nand_controller *ctrl) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci return container_of(ctrl, struct arasan_nfc, controller); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int anfc_wait_for_event(struct arasan_nfc *nfc, unsigned int event) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci u32 val; 2088c2ecf20Sopenharmony_ci int ret; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(nfc->base + INTR_STS_REG, val, 2118c2ecf20Sopenharmony_ci val & event, 0, 2128c2ecf20Sopenharmony_ci ANFC_DFLT_TIMEOUT_US); 2138c2ecf20Sopenharmony_ci if (ret) { 2148c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Timeout waiting for event 0x%x\n", event); 2158c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci writel_relaxed(event, nfc->base + INTR_STS_REG); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int anfc_wait_for_rb(struct arasan_nfc *nfc, struct nand_chip *chip, 2248c2ecf20Sopenharmony_ci unsigned int timeout_ms) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 2278c2ecf20Sopenharmony_ci u32 val; 2288c2ecf20Sopenharmony_ci int ret; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* There is no R/B interrupt, we must poll a register */ 2318c2ecf20Sopenharmony_ci ret = readl_relaxed_poll_timeout(nfc->base + READY_STS_REG, val, 2328c2ecf20Sopenharmony_ci val & BIT(anand->rb), 2338c2ecf20Sopenharmony_ci 1, timeout_ms * 1000); 2348c2ecf20Sopenharmony_ci if (ret) { 2358c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Timeout waiting for R/B 0x%x\n", 2368c2ecf20Sopenharmony_ci readl_relaxed(nfc->base + READY_STS_REG)); 2378c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic void anfc_trigger_op(struct arasan_nfc *nfc, struct anfc_op *nfc_op) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci writel_relaxed(nfc_op->pkt_reg, nfc->base + PKT_REG); 2468c2ecf20Sopenharmony_ci writel_relaxed(nfc_op->addr1_reg, nfc->base + MEM_ADDR1_REG); 2478c2ecf20Sopenharmony_ci writel_relaxed(nfc_op->addr2_reg, nfc->base + MEM_ADDR2_REG); 2488c2ecf20Sopenharmony_ci writel_relaxed(nfc_op->cmd_reg, nfc->base + CMD_REG); 2498c2ecf20Sopenharmony_ci writel_relaxed(nfc_op->prog_reg, nfc->base + PROG_REG); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int anfc_pkt_len_config(unsigned int len, unsigned int *steps, 2538c2ecf20Sopenharmony_ci unsigned int *pktsize) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci unsigned int nb, sz; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (nb = 1; nb < ANFC_MAX_STEPS; nb *= 2) { 2588c2ecf20Sopenharmony_ci sz = len / nb; 2598c2ecf20Sopenharmony_ci if (sz <= ANFC_MAX_PKT_SIZE) 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (sz * nb != len) 2648c2ecf20Sopenharmony_ci return -ENOTSUPP; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (steps) 2678c2ecf20Sopenharmony_ci *steps = nb; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (pktsize) 2708c2ecf20Sopenharmony_ci *pktsize = sz; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int anfc_select_target(struct nand_chip *chip, int target) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 2788c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 2798c2ecf20Sopenharmony_ci int ret; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Update the controller timings and the potential ECC configuration */ 2828c2ecf20Sopenharmony_ci writel_relaxed(anand->timings, nfc->base + DATA_INTERFACE_REG); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* Update clock frequency */ 2858c2ecf20Sopenharmony_ci if (nfc->cur_clk != anand->clk) { 2868c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->bus_clk); 2878c2ecf20Sopenharmony_ci ret = clk_set_rate(nfc->bus_clk, anand->clk); 2888c2ecf20Sopenharmony_ci if (ret) { 2898c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Failed to change clock rate\n"); 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->bus_clk); 2948c2ecf20Sopenharmony_ci if (ret) { 2958c2ecf20Sopenharmony_ci dev_err(nfc->dev, 2968c2ecf20Sopenharmony_ci "Failed to re-enable the bus clock\n"); 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci nfc->cur_clk = anand->clk; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci * When using the embedded hardware ECC engine, the controller is in charge of 3088c2ecf20Sopenharmony_ci * feeding the engine with, first, the ECC residue present in the data array. 3098c2ecf20Sopenharmony_ci * A typical read operation is: 3108c2ecf20Sopenharmony_ci * 1/ Assert the read operation by sending the relevant command/address cycles 3118c2ecf20Sopenharmony_ci * but targeting the column of the first ECC bytes in the OOB area instead of 3128c2ecf20Sopenharmony_ci * the main data directly. 3138c2ecf20Sopenharmony_ci * 2/ After having read the relevant number of ECC bytes, the controller uses 3148c2ecf20Sopenharmony_ci * the RNDOUT/RNDSTART commands which are set into the "ECC Spare Command 3158c2ecf20Sopenharmony_ci * Register" to move the pointer back at the beginning of the main data. 3168c2ecf20Sopenharmony_ci * 3/ It will read the content of the main area for a given size (pktsize) and 3178c2ecf20Sopenharmony_ci * will feed the ECC engine with this buffer again. 3188c2ecf20Sopenharmony_ci * 4/ The ECC engine derives the ECC bytes for the given data and compare them 3198c2ecf20Sopenharmony_ci * with the ones already received. It eventually trigger status flags and 3208c2ecf20Sopenharmony_ci * then set the "Buffer Read Ready" flag. 3218c2ecf20Sopenharmony_ci * 5/ The corrected data is then available for reading from the data port 3228c2ecf20Sopenharmony_ci * register. 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * The hardware BCH ECC engine is known to be inconstent in BCH mode and never 3258c2ecf20Sopenharmony_ci * reports uncorrectable errors. Because of this bug, we have to use the 3268c2ecf20Sopenharmony_ci * software BCH implementation in the read path. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_cistatic int anfc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf, 3298c2ecf20Sopenharmony_ci int oob_required, int page) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 3328c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 3338c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 3348c2ecf20Sopenharmony_ci unsigned int len = mtd->writesize + (oob_required ? mtd->oobsize : 0); 3358c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 3368c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 3378c2ecf20Sopenharmony_ci int step, ret; 3388c2ecf20Sopenharmony_ci struct anfc_op nfc_op = { 3398c2ecf20Sopenharmony_ci .pkt_reg = 3408c2ecf20Sopenharmony_ci PKT_SIZE(chip->ecc.size) | 3418c2ecf20Sopenharmony_ci PKT_STEPS(chip->ecc.steps), 3428c2ecf20Sopenharmony_ci .addr1_reg = 3438c2ecf20Sopenharmony_ci (page & 0xFF) << (8 * (anand->caddr_cycles)) | 3448c2ecf20Sopenharmony_ci (((page >> 8) & 0xFF) << (8 * (1 + anand->caddr_cycles))), 3458c2ecf20Sopenharmony_ci .addr2_reg = 3468c2ecf20Sopenharmony_ci ((page >> 16) & 0xFF) | 3478c2ecf20Sopenharmony_ci ADDR2_STRENGTH(anand->strength) | 3488c2ecf20Sopenharmony_ci ADDR2_CS(anand->cs), 3498c2ecf20Sopenharmony_ci .cmd_reg = 3508c2ecf20Sopenharmony_ci CMD_1(NAND_CMD_READ0) | 3518c2ecf20Sopenharmony_ci CMD_2(NAND_CMD_READSTART) | 3528c2ecf20Sopenharmony_ci CMD_PAGE_SIZE(anand->page_sz) | 3538c2ecf20Sopenharmony_ci CMD_DMA_ENABLE | 3548c2ecf20Sopenharmony_ci CMD_NADDRS(anand->caddr_cycles + 3558c2ecf20Sopenharmony_ci anand->raddr_cycles), 3568c2ecf20Sopenharmony_ci .prog_reg = PROG_PGRD, 3578c2ecf20Sopenharmony_ci }; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dma_addr = dma_map_single(nfc->dev, (void *)buf, len, DMA_FROM_DEVICE); 3608c2ecf20Sopenharmony_ci if (dma_mapping_error(nfc->dev, dma_addr)) { 3618c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Buffer mapping error"); 3628c2ecf20Sopenharmony_ci return -EIO; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci writel_relaxed(lower_32_bits(dma_addr), nfc->base + DMA_ADDR0_REG); 3668c2ecf20Sopenharmony_ci writel_relaxed(upper_32_bits(dma_addr), nfc->base + DMA_ADDR1_REG); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci anfc_trigger_op(nfc, &nfc_op); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ret = anfc_wait_for_event(nfc, XFER_COMPLETE); 3718c2ecf20Sopenharmony_ci dma_unmap_single(nfc->dev, dma_addr, len, DMA_FROM_DEVICE); 3728c2ecf20Sopenharmony_ci if (ret) { 3738c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Error reading page %d\n", page); 3748c2ecf20Sopenharmony_ci return ret; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Store the raw OOB bytes as well */ 3788c2ecf20Sopenharmony_ci ret = nand_change_read_column_op(chip, mtd->writesize, chip->oob_poi, 3798c2ecf20Sopenharmony_ci mtd->oobsize, 0); 3808c2ecf20Sopenharmony_ci if (ret) 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * For each step, compute by softare the BCH syndrome over the raw data. 3858c2ecf20Sopenharmony_ci * Compare the theoretical amount of errors and compare with the 3868c2ecf20Sopenharmony_ci * hardware engine feedback. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci for (step = 0; step < chip->ecc.steps; step++) { 3898c2ecf20Sopenharmony_ci u8 *raw_buf = &buf[step * chip->ecc.size]; 3908c2ecf20Sopenharmony_ci unsigned int bit, byte; 3918c2ecf20Sopenharmony_ci int bf, i; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* Extract the syndrome, it is not necessarily aligned */ 3948c2ecf20Sopenharmony_ci memset(anand->hw_ecc, 0, chip->ecc.bytes); 3958c2ecf20Sopenharmony_ci nand_extract_bits(anand->hw_ecc, 0, 3968c2ecf20Sopenharmony_ci &chip->oob_poi[mtd->oobsize - anand->ecc_total], 3978c2ecf20Sopenharmony_ci anand->ecc_bits * step, anand->ecc_bits); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci bf = bch_decode(anand->bch, raw_buf, chip->ecc.size, 4008c2ecf20Sopenharmony_ci anand->hw_ecc, NULL, NULL, anand->errloc); 4018c2ecf20Sopenharmony_ci if (!bf) { 4028c2ecf20Sopenharmony_ci continue; 4038c2ecf20Sopenharmony_ci } else if (bf > 0) { 4048c2ecf20Sopenharmony_ci for (i = 0; i < bf; i++) { 4058c2ecf20Sopenharmony_ci /* Only correct the data, not the syndrome */ 4068c2ecf20Sopenharmony_ci if (anand->errloc[i] < (chip->ecc.size * 8)) { 4078c2ecf20Sopenharmony_ci bit = BIT(anand->errloc[i] & 7); 4088c2ecf20Sopenharmony_ci byte = anand->errloc[i] >> 3; 4098c2ecf20Sopenharmony_ci raw_buf[byte] ^= bit; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += bf; 4148c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, bf); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci continue; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci bf = nand_check_erased_ecc_chunk(raw_buf, chip->ecc.size, 4208c2ecf20Sopenharmony_ci NULL, 0, NULL, 0, 4218c2ecf20Sopenharmony_ci chip->ecc.strength); 4228c2ecf20Sopenharmony_ci if (bf > 0) { 4238c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += bf; 4248c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, bf); 4258c2ecf20Sopenharmony_ci memset(raw_buf, 0xFF, chip->ecc.size); 4268c2ecf20Sopenharmony_ci } else if (bf < 0) { 4278c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int anfc_sel_read_page_hw_ecc(struct nand_chip *chip, u8 *buf, 4358c2ecf20Sopenharmony_ci int oob_required, int page) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int ret; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci ret = anfc_select_target(chip, chip->cur_cs); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci return ret; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci return anfc_read_page_hw_ecc(chip, buf, oob_required, page); 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int anfc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf, 4478c2ecf20Sopenharmony_ci int oob_required, int page) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 4508c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 4518c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 4528c2ecf20Sopenharmony_ci unsigned int len = mtd->writesize + (oob_required ? mtd->oobsize : 0); 4538c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 4548c2ecf20Sopenharmony_ci u8 status; 4558c2ecf20Sopenharmony_ci int ret; 4568c2ecf20Sopenharmony_ci struct anfc_op nfc_op = { 4578c2ecf20Sopenharmony_ci .pkt_reg = 4588c2ecf20Sopenharmony_ci PKT_SIZE(chip->ecc.size) | 4598c2ecf20Sopenharmony_ci PKT_STEPS(chip->ecc.steps), 4608c2ecf20Sopenharmony_ci .addr1_reg = 4618c2ecf20Sopenharmony_ci (page & 0xFF) << (8 * (anand->caddr_cycles)) | 4628c2ecf20Sopenharmony_ci (((page >> 8) & 0xFF) << (8 * (1 + anand->caddr_cycles))), 4638c2ecf20Sopenharmony_ci .addr2_reg = 4648c2ecf20Sopenharmony_ci ((page >> 16) & 0xFF) | 4658c2ecf20Sopenharmony_ci ADDR2_STRENGTH(anand->strength) | 4668c2ecf20Sopenharmony_ci ADDR2_CS(anand->cs), 4678c2ecf20Sopenharmony_ci .cmd_reg = 4688c2ecf20Sopenharmony_ci CMD_1(NAND_CMD_SEQIN) | 4698c2ecf20Sopenharmony_ci CMD_2(NAND_CMD_PAGEPROG) | 4708c2ecf20Sopenharmony_ci CMD_PAGE_SIZE(anand->page_sz) | 4718c2ecf20Sopenharmony_ci CMD_DMA_ENABLE | 4728c2ecf20Sopenharmony_ci CMD_NADDRS(anand->caddr_cycles + 4738c2ecf20Sopenharmony_ci anand->raddr_cycles) | 4748c2ecf20Sopenharmony_ci CMD_ECC_ENABLE, 4758c2ecf20Sopenharmony_ci .prog_reg = PROG_PGPROG, 4768c2ecf20Sopenharmony_ci }; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci writel_relaxed(anand->ecc_conf, nfc->base + ECC_CONF_REG); 4798c2ecf20Sopenharmony_ci writel_relaxed(ECC_SP_CMD1(NAND_CMD_RNDIN) | 4808c2ecf20Sopenharmony_ci ECC_SP_ADDRS(anand->caddr_cycles), 4818c2ecf20Sopenharmony_ci nfc->base + ECC_SP_REG); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci dma_addr = dma_map_single(nfc->dev, (void *)buf, len, DMA_TO_DEVICE); 4848c2ecf20Sopenharmony_ci if (dma_mapping_error(nfc->dev, dma_addr)) { 4858c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Buffer mapping error"); 4868c2ecf20Sopenharmony_ci return -EIO; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci writel_relaxed(lower_32_bits(dma_addr), nfc->base + DMA_ADDR0_REG); 4908c2ecf20Sopenharmony_ci writel_relaxed(upper_32_bits(dma_addr), nfc->base + DMA_ADDR1_REG); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci anfc_trigger_op(nfc, &nfc_op); 4938c2ecf20Sopenharmony_ci ret = anfc_wait_for_event(nfc, XFER_COMPLETE); 4948c2ecf20Sopenharmony_ci dma_unmap_single(nfc->dev, dma_addr, len, DMA_TO_DEVICE); 4958c2ecf20Sopenharmony_ci if (ret) { 4968c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Error writing page %d\n", page); 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Spare data is not protected */ 5018c2ecf20Sopenharmony_ci if (oob_required) { 5028c2ecf20Sopenharmony_ci ret = nand_write_oob_std(chip, page); 5038c2ecf20Sopenharmony_ci if (ret) 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* Check write status on the chip side */ 5088c2ecf20Sopenharmony_ci ret = nand_status_op(chip, &status); 5098c2ecf20Sopenharmony_ci if (ret) 5108c2ecf20Sopenharmony_ci return ret; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (status & NAND_STATUS_FAIL) 5138c2ecf20Sopenharmony_ci return -EIO; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int anfc_sel_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf, 5198c2ecf20Sopenharmony_ci int oob_required, int page) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci int ret; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci ret = anfc_select_target(chip, chip->cur_cs); 5248c2ecf20Sopenharmony_ci if (ret) 5258c2ecf20Sopenharmony_ci return ret; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return anfc_write_page_hw_ecc(chip, buf, oob_required, page); 5288c2ecf20Sopenharmony_ci}; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/* NAND framework ->exec_op() hooks and related helpers */ 5318c2ecf20Sopenharmony_cistatic int anfc_parse_instructions(struct nand_chip *chip, 5328c2ecf20Sopenharmony_ci const struct nand_subop *subop, 5338c2ecf20Sopenharmony_ci struct anfc_op *nfc_op) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 5368c2ecf20Sopenharmony_ci const struct nand_op_instr *instr = NULL; 5378c2ecf20Sopenharmony_ci bool first_cmd = true; 5388c2ecf20Sopenharmony_ci unsigned int op_id; 5398c2ecf20Sopenharmony_ci int ret, i; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci memset(nfc_op, 0, sizeof(*nfc_op)); 5428c2ecf20Sopenharmony_ci nfc_op->addr2_reg = ADDR2_CS(anand->cs); 5438c2ecf20Sopenharmony_ci nfc_op->cmd_reg = CMD_PAGE_SIZE(anand->page_sz); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci for (op_id = 0; op_id < subop->ninstrs; op_id++) { 5468c2ecf20Sopenharmony_ci unsigned int offset, naddrs, pktsize; 5478c2ecf20Sopenharmony_ci const u8 *addrs; 5488c2ecf20Sopenharmony_ci u8 *buf; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci instr = &subop->instrs[op_id]; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci switch (instr->type) { 5538c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 5548c2ecf20Sopenharmony_ci if (first_cmd) 5558c2ecf20Sopenharmony_ci nfc_op->cmd_reg |= CMD_1(instr->ctx.cmd.opcode); 5568c2ecf20Sopenharmony_ci else 5578c2ecf20Sopenharmony_ci nfc_op->cmd_reg |= CMD_2(instr->ctx.cmd.opcode); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci first_cmd = false; 5608c2ecf20Sopenharmony_ci break; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 5638c2ecf20Sopenharmony_ci offset = nand_subop_get_addr_start_off(subop, op_id); 5648c2ecf20Sopenharmony_ci naddrs = nand_subop_get_num_addr_cyc(subop, op_id); 5658c2ecf20Sopenharmony_ci addrs = &instr->ctx.addr.addrs[offset]; 5668c2ecf20Sopenharmony_ci nfc_op->cmd_reg |= CMD_NADDRS(naddrs); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci for (i = 0; i < min(ANFC_MAX_ADDR_CYC, naddrs); i++) { 5698c2ecf20Sopenharmony_ci if (i < 4) 5708c2ecf20Sopenharmony_ci nfc_op->addr1_reg |= (u32)addrs[i] << i * 8; 5718c2ecf20Sopenharmony_ci else 5728c2ecf20Sopenharmony_ci nfc_op->addr2_reg |= addrs[i]; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 5778c2ecf20Sopenharmony_ci nfc_op->read = true; 5788c2ecf20Sopenharmony_ci fallthrough; 5798c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 5808c2ecf20Sopenharmony_ci offset = nand_subop_get_data_start_off(subop, op_id); 5818c2ecf20Sopenharmony_ci buf = instr->ctx.data.buf.in; 5828c2ecf20Sopenharmony_ci nfc_op->buf = &buf[offset]; 5838c2ecf20Sopenharmony_ci nfc_op->len = nand_subop_get_data_len(subop, op_id); 5848c2ecf20Sopenharmony_ci ret = anfc_pkt_len_config(nfc_op->len, &nfc_op->steps, 5858c2ecf20Sopenharmony_ci &pktsize); 5868c2ecf20Sopenharmony_ci if (ret) 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* 5908c2ecf20Sopenharmony_ci * Number of DATA cycles must be aligned on 4, this 5918c2ecf20Sopenharmony_ci * means the controller might read/write more than 5928c2ecf20Sopenharmony_ci * requested. This is harmless most of the time as extra 5938c2ecf20Sopenharmony_ci * DATA are discarded in the write path and read pointer 5948c2ecf20Sopenharmony_ci * adjusted in the read path. 5958c2ecf20Sopenharmony_ci * 5968c2ecf20Sopenharmony_ci * FIXME: The core should mark operations where 5978c2ecf20Sopenharmony_ci * reading/writing more is allowed so the exec_op() 5988c2ecf20Sopenharmony_ci * implementation can take the right decision when the 5998c2ecf20Sopenharmony_ci * alignment constraint is not met: adjust the number of 6008c2ecf20Sopenharmony_ci * DATA cycles when it's allowed, reject the operation 6018c2ecf20Sopenharmony_ci * otherwise. 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ci nfc_op->pkt_reg |= PKT_SIZE(round_up(pktsize, 4)) | 6048c2ecf20Sopenharmony_ci PKT_STEPS(nfc_op->steps); 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 6078c2ecf20Sopenharmony_ci nfc_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci return 0; 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int anfc_rw_pio_op(struct arasan_nfc *nfc, struct anfc_op *nfc_op) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci unsigned int dwords = (nfc_op->len / 4) / nfc_op->steps; 6188c2ecf20Sopenharmony_ci unsigned int last_len = nfc_op->len % 4; 6198c2ecf20Sopenharmony_ci unsigned int offset, dir; 6208c2ecf20Sopenharmony_ci u8 *buf = nfc_op->buf; 6218c2ecf20Sopenharmony_ci int ret, i; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci for (i = 0; i < nfc_op->steps; i++) { 6248c2ecf20Sopenharmony_ci dir = nfc_op->read ? READ_READY : WRITE_READY; 6258c2ecf20Sopenharmony_ci ret = anfc_wait_for_event(nfc, dir); 6268c2ecf20Sopenharmony_ci if (ret) { 6278c2ecf20Sopenharmony_ci dev_err(nfc->dev, "PIO %s ready signal not received\n", 6288c2ecf20Sopenharmony_ci nfc_op->read ? "Read" : "Write"); 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci offset = i * (dwords * 4); 6338c2ecf20Sopenharmony_ci if (nfc_op->read) 6348c2ecf20Sopenharmony_ci ioread32_rep(nfc->base + DATA_PORT_REG, &buf[offset], 6358c2ecf20Sopenharmony_ci dwords); 6368c2ecf20Sopenharmony_ci else 6378c2ecf20Sopenharmony_ci iowrite32_rep(nfc->base + DATA_PORT_REG, &buf[offset], 6388c2ecf20Sopenharmony_ci dwords); 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (last_len) { 6428c2ecf20Sopenharmony_ci u32 remainder; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci offset = nfc_op->len - last_len; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (nfc_op->read) { 6478c2ecf20Sopenharmony_ci remainder = readl_relaxed(nfc->base + DATA_PORT_REG); 6488c2ecf20Sopenharmony_ci memcpy(&buf[offset], &remainder, last_len); 6498c2ecf20Sopenharmony_ci } else { 6508c2ecf20Sopenharmony_ci memcpy(&remainder, &buf[offset], last_len); 6518c2ecf20Sopenharmony_ci writel_relaxed(remainder, nfc->base + DATA_PORT_REG); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return anfc_wait_for_event(nfc, XFER_COMPLETE); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int anfc_misc_data_type_exec(struct nand_chip *chip, 6598c2ecf20Sopenharmony_ci const struct nand_subop *subop, 6608c2ecf20Sopenharmony_ci u32 prog_reg) 6618c2ecf20Sopenharmony_ci{ 6628c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 6638c2ecf20Sopenharmony_ci struct anfc_op nfc_op = {}; 6648c2ecf20Sopenharmony_ci int ret; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci ret = anfc_parse_instructions(chip, subop, &nfc_op); 6678c2ecf20Sopenharmony_ci if (ret) 6688c2ecf20Sopenharmony_ci return ret; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci nfc_op.prog_reg = prog_reg; 6718c2ecf20Sopenharmony_ci anfc_trigger_op(nfc, &nfc_op); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (nfc_op.rdy_timeout_ms) { 6748c2ecf20Sopenharmony_ci ret = anfc_wait_for_rb(nfc, chip, nfc_op.rdy_timeout_ms); 6758c2ecf20Sopenharmony_ci if (ret) 6768c2ecf20Sopenharmony_ci return ret; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return anfc_rw_pio_op(nfc, &nfc_op); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int anfc_param_read_type_exec(struct nand_chip *chip, 6838c2ecf20Sopenharmony_ci const struct nand_subop *subop) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci return anfc_misc_data_type_exec(chip, subop, PROG_RDPARAM); 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int anfc_data_read_type_exec(struct nand_chip *chip, 6898c2ecf20Sopenharmony_ci const struct nand_subop *subop) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci return anfc_misc_data_type_exec(chip, subop, PROG_PGRD); 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int anfc_param_write_type_exec(struct nand_chip *chip, 6958c2ecf20Sopenharmony_ci const struct nand_subop *subop) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci return anfc_misc_data_type_exec(chip, subop, PROG_SET_FEATURE); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic int anfc_data_write_type_exec(struct nand_chip *chip, 7018c2ecf20Sopenharmony_ci const struct nand_subop *subop) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci return anfc_misc_data_type_exec(chip, subop, PROG_PGPROG); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic int anfc_misc_zerolen_type_exec(struct nand_chip *chip, 7078c2ecf20Sopenharmony_ci const struct nand_subop *subop, 7088c2ecf20Sopenharmony_ci u32 prog_reg) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 7118c2ecf20Sopenharmony_ci struct anfc_op nfc_op = {}; 7128c2ecf20Sopenharmony_ci int ret; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci ret = anfc_parse_instructions(chip, subop, &nfc_op); 7158c2ecf20Sopenharmony_ci if (ret) 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci nfc_op.prog_reg = prog_reg; 7198c2ecf20Sopenharmony_ci anfc_trigger_op(nfc, &nfc_op); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ret = anfc_wait_for_event(nfc, XFER_COMPLETE); 7228c2ecf20Sopenharmony_ci if (ret) 7238c2ecf20Sopenharmony_ci return ret; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (nfc_op.rdy_timeout_ms) 7268c2ecf20Sopenharmony_ci ret = anfc_wait_for_rb(nfc, chip, nfc_op.rdy_timeout_ms); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return ret; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int anfc_status_type_exec(struct nand_chip *chip, 7328c2ecf20Sopenharmony_ci const struct nand_subop *subop) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 7358c2ecf20Sopenharmony_ci u32 tmp; 7368c2ecf20Sopenharmony_ci int ret; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* See anfc_check_op() for details about this constraint */ 7398c2ecf20Sopenharmony_ci if (subop->instrs[0].ctx.cmd.opcode != NAND_CMD_STATUS) 7408c2ecf20Sopenharmony_ci return -ENOTSUPP; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ret = anfc_misc_zerolen_type_exec(chip, subop, PROG_STATUS); 7438c2ecf20Sopenharmony_ci if (ret) 7448c2ecf20Sopenharmony_ci return ret; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci tmp = readl_relaxed(nfc->base + FLASH_STS_REG); 7478c2ecf20Sopenharmony_ci memcpy(subop->instrs[1].ctx.data.buf.in, &tmp, 1); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return 0; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic int anfc_reset_type_exec(struct nand_chip *chip, 7538c2ecf20Sopenharmony_ci const struct nand_subop *subop) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci return anfc_misc_zerolen_type_exec(chip, subop, PROG_RST); 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic int anfc_erase_type_exec(struct nand_chip *chip, 7598c2ecf20Sopenharmony_ci const struct nand_subop *subop) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci return anfc_misc_zerolen_type_exec(chip, subop, PROG_ERASE); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int anfc_wait_type_exec(struct nand_chip *chip, 7658c2ecf20Sopenharmony_ci const struct nand_subop *subop) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 7688c2ecf20Sopenharmony_ci struct anfc_op nfc_op = {}; 7698c2ecf20Sopenharmony_ci int ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ret = anfc_parse_instructions(chip, subop, &nfc_op); 7728c2ecf20Sopenharmony_ci if (ret) 7738c2ecf20Sopenharmony_ci return ret; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return anfc_wait_for_rb(nfc, chip, nfc_op.rdy_timeout_ms); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic const struct nand_op_parser anfc_op_parser = NAND_OP_PARSER( 7798c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 7808c2ecf20Sopenharmony_ci anfc_param_read_type_exec, 7818c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 7828c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC), 7838c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), 7848c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, ANFC_MAX_CHUNK_SIZE)), 7858c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 7868c2ecf20Sopenharmony_ci anfc_param_write_type_exec, 7878c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 7888c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC), 7898c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, ANFC_MAX_PARAM_SIZE)), 7908c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 7918c2ecf20Sopenharmony_ci anfc_data_read_type_exec, 7928c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 7938c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC), 7948c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 7958c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), 7968c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, ANFC_MAX_CHUNK_SIZE)), 7978c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 7988c2ecf20Sopenharmony_ci anfc_data_write_type_exec, 7998c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 8008c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC), 8018c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, ANFC_MAX_CHUNK_SIZE), 8028c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false)), 8038c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 8048c2ecf20Sopenharmony_ci anfc_reset_type_exec, 8058c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 8068c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 8078c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 8088c2ecf20Sopenharmony_ci anfc_erase_type_exec, 8098c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 8108c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(false, ANFC_MAX_ADDR_CYC), 8118c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 8128c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 8138c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 8148c2ecf20Sopenharmony_ci anfc_status_type_exec, 8158c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(false), 8168c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, ANFC_MAX_CHUNK_SIZE)), 8178c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN( 8188c2ecf20Sopenharmony_ci anfc_wait_type_exec, 8198c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 8208c2ecf20Sopenharmony_ci ); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic int anfc_check_op(struct nand_chip *chip, 8238c2ecf20Sopenharmony_ci const struct nand_operation *op) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci const struct nand_op_instr *instr; 8268c2ecf20Sopenharmony_ci int op_id; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* 8298c2ecf20Sopenharmony_ci * The controller abstracts all the NAND operations and do not support 8308c2ecf20Sopenharmony_ci * data only operations. 8318c2ecf20Sopenharmony_ci * 8328c2ecf20Sopenharmony_ci * TODO: The nand_op_parser framework should be extended to 8338c2ecf20Sopenharmony_ci * support custom checks on DATA instructions. 8348c2ecf20Sopenharmony_ci */ 8358c2ecf20Sopenharmony_ci for (op_id = 0; op_id < op->ninstrs; op_id++) { 8368c2ecf20Sopenharmony_ci instr = &op->instrs[op_id]; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci switch (instr->type) { 8398c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 8408c2ecf20Sopenharmony_ci if (instr->ctx.addr.naddrs > ANFC_MAX_ADDR_CYC) 8418c2ecf20Sopenharmony_ci return -ENOTSUPP; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 8458c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 8468c2ecf20Sopenharmony_ci if (instr->ctx.data.len > ANFC_MAX_CHUNK_SIZE) 8478c2ecf20Sopenharmony_ci return -ENOTSUPP; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (anfc_pkt_len_config(instr->ctx.data.len, 0, 0)) 8508c2ecf20Sopenharmony_ci return -ENOTSUPP; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci default: 8548c2ecf20Sopenharmony_ci break; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* 8598c2ecf20Sopenharmony_ci * The controller does not allow to proceed with a CMD+DATA_IN cycle 8608c2ecf20Sopenharmony_ci * manually on the bus by reading data from the data register. Instead, 8618c2ecf20Sopenharmony_ci * the controller abstract a status read operation with its own status 8628c2ecf20Sopenharmony_ci * register after ordering a read status operation. Hence, we cannot 8638c2ecf20Sopenharmony_ci * support any CMD+DATA_IN operation other than a READ STATUS. 8648c2ecf20Sopenharmony_ci * 8658c2ecf20Sopenharmony_ci * TODO: The nand_op_parser() framework should be extended to describe 8668c2ecf20Sopenharmony_ci * fixed patterns instead of open-coding this check here. 8678c2ecf20Sopenharmony_ci */ 8688c2ecf20Sopenharmony_ci if (op->ninstrs == 2 && 8698c2ecf20Sopenharmony_ci op->instrs[0].type == NAND_OP_CMD_INSTR && 8708c2ecf20Sopenharmony_ci op->instrs[0].ctx.cmd.opcode != NAND_CMD_STATUS && 8718c2ecf20Sopenharmony_ci op->instrs[1].type == NAND_OP_DATA_IN_INSTR) 8728c2ecf20Sopenharmony_ci return -ENOTSUPP; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return nand_op_parser_exec_op(chip, &anfc_op_parser, op, true); 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int anfc_exec_op(struct nand_chip *chip, 8788c2ecf20Sopenharmony_ci const struct nand_operation *op, 8798c2ecf20Sopenharmony_ci bool check_only) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci int ret; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (check_only) 8848c2ecf20Sopenharmony_ci return anfc_check_op(chip, op); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci ret = anfc_select_target(chip, op->cs); 8878c2ecf20Sopenharmony_ci if (ret) 8888c2ecf20Sopenharmony_ci return ret; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return nand_op_parser_exec_op(chip, &anfc_op_parser, op, check_only); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int anfc_setup_interface(struct nand_chip *chip, int target, 8948c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 8958c2ecf20Sopenharmony_ci{ 8968c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 8978c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 8988c2ecf20Sopenharmony_ci struct device_node *np = nfc->dev->of_node; 8998c2ecf20Sopenharmony_ci const struct nand_sdr_timings *sdr; 9008c2ecf20Sopenharmony_ci const struct nand_nvddr_timings *nvddr; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (nand_interface_is_nvddr(conf)) { 9038c2ecf20Sopenharmony_ci nvddr = nand_get_nvddr_timings(conf); 9048c2ecf20Sopenharmony_ci if (IS_ERR(nvddr)) 9058c2ecf20Sopenharmony_ci return PTR_ERR(nvddr); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * The controller only supports data payload requests which are 9098c2ecf20Sopenharmony_ci * a multiple of 4. In practice, most data accesses are 4-byte 9108c2ecf20Sopenharmony_ci * aligned and this is not an issue. However, rounding up will 9118c2ecf20Sopenharmony_ci * simply be refused by the controller if we reached the end of 9128c2ecf20Sopenharmony_ci * the device *and* we are using the NV-DDR interface(!). In 9138c2ecf20Sopenharmony_ci * this situation, unaligned data requests ending at the device 9148c2ecf20Sopenharmony_ci * boundary will confuse the controller and cannot be performed. 9158c2ecf20Sopenharmony_ci * 9168c2ecf20Sopenharmony_ci * This is something that happens in nand_read_subpage() when 9178c2ecf20Sopenharmony_ci * selecting software ECC support and must be avoided. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_SOFT) 9208c2ecf20Sopenharmony_ci return -ENOTSUPP; 9218c2ecf20Sopenharmony_ci } else { 9228c2ecf20Sopenharmony_ci sdr = nand_get_sdr_timings(conf); 9238c2ecf20Sopenharmony_ci if (IS_ERR(sdr)) 9248c2ecf20Sopenharmony_ci return PTR_ERR(sdr); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (target < 0) 9288c2ecf20Sopenharmony_ci return 0; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (nand_interface_is_sdr(conf)) 9318c2ecf20Sopenharmony_ci anand->timings = DIFACE_SDR | 9328c2ecf20Sopenharmony_ci DIFACE_SDR_MODE(conf->timings.mode); 9338c2ecf20Sopenharmony_ci else 9348c2ecf20Sopenharmony_ci anand->timings = DIFACE_NVDDR | 9358c2ecf20Sopenharmony_ci DIFACE_DDR_MODE(conf->timings.mode); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (nand_interface_is_sdr(conf)) { 9388c2ecf20Sopenharmony_ci anand->clk = ANFC_XLNX_SDR_DFLT_CORE_CLK; 9398c2ecf20Sopenharmony_ci } else { 9408c2ecf20Sopenharmony_ci /* ONFI timings are defined in picoseconds */ 9418c2ecf20Sopenharmony_ci anand->clk = div_u64((u64)NSEC_PER_SEC * 1000, 9428c2ecf20Sopenharmony_ci conf->timings.nvddr.tCK_min); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* 9468c2ecf20Sopenharmony_ci * Due to a hardware bug in the ZynqMP SoC, SDR timing modes 0-1 work 9478c2ecf20Sopenharmony_ci * with f > 90MHz (default clock is 100MHz) but signals are unstable 9488c2ecf20Sopenharmony_ci * with higher modes. Hence we decrease a little bit the clock rate to 9498c2ecf20Sopenharmony_ci * 80MHz when using SDR modes 2-5 with this SoC. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "xlnx,zynqmp-nand-controller") && 9528c2ecf20Sopenharmony_ci nand_interface_is_sdr(conf) && conf->timings.mode >= 2) 9538c2ecf20Sopenharmony_ci anand->clk = ANFC_XLNX_SDR_HS_CORE_CLK; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci return 0; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic int anfc_calc_hw_ecc_bytes(int step_size, int strength) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci unsigned int bch_gf_mag, ecc_bits; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci switch (step_size) { 9638c2ecf20Sopenharmony_ci case SZ_512: 9648c2ecf20Sopenharmony_ci bch_gf_mag = 13; 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci case SZ_1K: 9678c2ecf20Sopenharmony_ci bch_gf_mag = 14; 9688c2ecf20Sopenharmony_ci break; 9698c2ecf20Sopenharmony_ci default: 9708c2ecf20Sopenharmony_ci return -EINVAL; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci ecc_bits = bch_gf_mag * strength; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return DIV_ROUND_UP(ecc_bits, 8); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic const int anfc_hw_ecc_512_strengths[] = {4, 8, 12}; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic const int anfc_hw_ecc_1024_strengths[] = {24}; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic const struct nand_ecc_step_info anfc_hw_ecc_step_infos[] = { 9838c2ecf20Sopenharmony_ci { 9848c2ecf20Sopenharmony_ci .stepsize = SZ_512, 9858c2ecf20Sopenharmony_ci .strengths = anfc_hw_ecc_512_strengths, 9868c2ecf20Sopenharmony_ci .nstrengths = ARRAY_SIZE(anfc_hw_ecc_512_strengths), 9878c2ecf20Sopenharmony_ci }, 9888c2ecf20Sopenharmony_ci { 9898c2ecf20Sopenharmony_ci .stepsize = SZ_1K, 9908c2ecf20Sopenharmony_ci .strengths = anfc_hw_ecc_1024_strengths, 9918c2ecf20Sopenharmony_ci .nstrengths = ARRAY_SIZE(anfc_hw_ecc_1024_strengths), 9928c2ecf20Sopenharmony_ci }, 9938c2ecf20Sopenharmony_ci}; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic const struct nand_ecc_caps anfc_hw_ecc_caps = { 9968c2ecf20Sopenharmony_ci .stepinfos = anfc_hw_ecc_step_infos, 9978c2ecf20Sopenharmony_ci .nstepinfos = ARRAY_SIZE(anfc_hw_ecc_step_infos), 9988c2ecf20Sopenharmony_ci .calc_ecc_bytes = anfc_calc_hw_ecc_bytes, 9998c2ecf20Sopenharmony_ci}; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_cistatic int anfc_init_hw_ecc_controller(struct arasan_nfc *nfc, 10028c2ecf20Sopenharmony_ci struct nand_chip *chip) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 10058c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 10068c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 10078c2ecf20Sopenharmony_ci unsigned int bch_prim_poly = 0, bch_gf_mag = 0, ecc_offset; 10088c2ecf20Sopenharmony_ci int ret; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci switch (mtd->writesize) { 10118c2ecf20Sopenharmony_ci case SZ_512: 10128c2ecf20Sopenharmony_ci case SZ_2K: 10138c2ecf20Sopenharmony_ci case SZ_4K: 10148c2ecf20Sopenharmony_ci case SZ_8K: 10158c2ecf20Sopenharmony_ci case SZ_16K: 10168c2ecf20Sopenharmony_ci break; 10178c2ecf20Sopenharmony_ci default: 10188c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Unsupported page size %d\n", mtd->writesize); 10198c2ecf20Sopenharmony_ci return -EINVAL; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci ret = nand_ecc_choose_conf(chip, &anfc_hw_ecc_caps, mtd->oobsize); 10238c2ecf20Sopenharmony_ci if (ret) 10248c2ecf20Sopenharmony_ci return ret; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci switch (ecc->strength) { 10278c2ecf20Sopenharmony_ci case 12: 10288c2ecf20Sopenharmony_ci anand->strength = 0x1; 10298c2ecf20Sopenharmony_ci break; 10308c2ecf20Sopenharmony_ci case 8: 10318c2ecf20Sopenharmony_ci anand->strength = 0x2; 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci case 4: 10348c2ecf20Sopenharmony_ci anand->strength = 0x3; 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci case 24: 10378c2ecf20Sopenharmony_ci anand->strength = 0x4; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci default: 10408c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Unsupported strength %d\n", ecc->strength); 10418c2ecf20Sopenharmony_ci return -EINVAL; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci switch (ecc->size) { 10458c2ecf20Sopenharmony_ci case SZ_512: 10468c2ecf20Sopenharmony_ci bch_gf_mag = 13; 10478c2ecf20Sopenharmony_ci bch_prim_poly = 0x201b; 10488c2ecf20Sopenharmony_ci break; 10498c2ecf20Sopenharmony_ci case SZ_1K: 10508c2ecf20Sopenharmony_ci bch_gf_mag = 14; 10518c2ecf20Sopenharmony_ci bch_prim_poly = 0x4443; 10528c2ecf20Sopenharmony_ci break; 10538c2ecf20Sopenharmony_ci default: 10548c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Unsupported step size %d\n", ecc->strength); 10558c2ecf20Sopenharmony_ci return -EINVAL; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci ecc->steps = mtd->writesize / ecc->size; 10618c2ecf20Sopenharmony_ci ecc->algo = NAND_ECC_ALGO_BCH; 10628c2ecf20Sopenharmony_ci anand->ecc_bits = bch_gf_mag * ecc->strength; 10638c2ecf20Sopenharmony_ci ecc->bytes = DIV_ROUND_UP(anand->ecc_bits, 8); 10648c2ecf20Sopenharmony_ci anand->ecc_total = DIV_ROUND_UP(anand->ecc_bits * ecc->steps, 8); 10658c2ecf20Sopenharmony_ci ecc_offset = mtd->writesize + mtd->oobsize - anand->ecc_total; 10668c2ecf20Sopenharmony_ci anand->ecc_conf = ECC_CONF_COL(ecc_offset) | 10678c2ecf20Sopenharmony_ci ECC_CONF_LEN(anand->ecc_total) | 10688c2ecf20Sopenharmony_ci ECC_CONF_BCH_EN; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci anand->errloc = devm_kmalloc_array(nfc->dev, ecc->strength, 10718c2ecf20Sopenharmony_ci sizeof(*anand->errloc), GFP_KERNEL); 10728c2ecf20Sopenharmony_ci if (!anand->errloc) 10738c2ecf20Sopenharmony_ci return -ENOMEM; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci anand->hw_ecc = devm_kmalloc(nfc->dev, ecc->bytes, GFP_KERNEL); 10768c2ecf20Sopenharmony_ci if (!anand->hw_ecc) 10778c2ecf20Sopenharmony_ci return -ENOMEM; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* Enforce bit swapping to fit the hardware */ 10808c2ecf20Sopenharmony_ci anand->bch = bch_init(bch_gf_mag, ecc->strength, bch_prim_poly, true); 10818c2ecf20Sopenharmony_ci if (!anand->bch) 10828c2ecf20Sopenharmony_ci return -EINVAL; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci ecc->read_page = anfc_sel_read_page_hw_ecc; 10858c2ecf20Sopenharmony_ci ecc->write_page = anfc_sel_write_page_hw_ecc; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic int anfc_attach_chip(struct nand_chip *chip) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 10938c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = to_anfc(chip->controller); 10948c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 10958c2ecf20Sopenharmony_ci int ret = 0; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci if (mtd->writesize <= SZ_512) 10988c2ecf20Sopenharmony_ci anand->caddr_cycles = 1; 10998c2ecf20Sopenharmony_ci else 11008c2ecf20Sopenharmony_ci anand->caddr_cycles = 2; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (chip->options & NAND_ROW_ADDR_3) 11038c2ecf20Sopenharmony_ci anand->raddr_cycles = 3; 11048c2ecf20Sopenharmony_ci else 11058c2ecf20Sopenharmony_ci anand->raddr_cycles = 2; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci switch (mtd->writesize) { 11088c2ecf20Sopenharmony_ci case 512: 11098c2ecf20Sopenharmony_ci anand->page_sz = 0; 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci case 1024: 11128c2ecf20Sopenharmony_ci anand->page_sz = 5; 11138c2ecf20Sopenharmony_ci break; 11148c2ecf20Sopenharmony_ci case 2048: 11158c2ecf20Sopenharmony_ci anand->page_sz = 1; 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci case 4096: 11188c2ecf20Sopenharmony_ci anand->page_sz = 2; 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci case 8192: 11218c2ecf20Sopenharmony_ci anand->page_sz = 3; 11228c2ecf20Sopenharmony_ci break; 11238c2ecf20Sopenharmony_ci case 16384: 11248c2ecf20Sopenharmony_ci anand->page_sz = 4; 11258c2ecf20Sopenharmony_ci break; 11268c2ecf20Sopenharmony_ci default: 11278c2ecf20Sopenharmony_ci return -EINVAL; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* These hooks are valid for all ECC providers */ 11318c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = nand_monolithic_read_page_raw; 11328c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = nand_monolithic_write_page_raw; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci switch (chip->ecc.engine_type) { 11358c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 11368c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 11378c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_DIE: 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 11408c2ecf20Sopenharmony_ci ret = anfc_init_hw_ecc_controller(nfc, chip); 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci default: 11438c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Unsupported ECC mode: %d\n", 11448c2ecf20Sopenharmony_ci chip->ecc.engine_type); 11458c2ecf20Sopenharmony_ci return -EINVAL; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci return ret; 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cistatic void anfc_detach_chip(struct nand_chip *chip) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci struct anand *anand = to_anand(chip); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if (anand->bch) 11568c2ecf20Sopenharmony_ci bch_free(anand->bch); 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic const struct nand_controller_ops anfc_ops = { 11608c2ecf20Sopenharmony_ci .exec_op = anfc_exec_op, 11618c2ecf20Sopenharmony_ci .setup_interface = anfc_setup_interface, 11628c2ecf20Sopenharmony_ci .attach_chip = anfc_attach_chip, 11638c2ecf20Sopenharmony_ci .detach_chip = anfc_detach_chip, 11648c2ecf20Sopenharmony_ci}; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic int anfc_chip_init(struct arasan_nfc *nfc, struct device_node *np) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci struct anand *anand; 11698c2ecf20Sopenharmony_ci struct nand_chip *chip; 11708c2ecf20Sopenharmony_ci struct mtd_info *mtd; 11718c2ecf20Sopenharmony_ci int cs, rb, ret; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci anand = devm_kzalloc(nfc->dev, sizeof(*anand), GFP_KERNEL); 11748c2ecf20Sopenharmony_ci if (!anand) 11758c2ecf20Sopenharmony_ci return -ENOMEM; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci /* We do not support multiple CS per chip yet */ 11788c2ecf20Sopenharmony_ci if (of_property_count_elems_of_size(np, "reg", sizeof(u32)) != 1) { 11798c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Invalid reg property\n"); 11808c2ecf20Sopenharmony_ci return -EINVAL; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "reg", &cs); 11848c2ecf20Sopenharmony_ci if (ret) 11858c2ecf20Sopenharmony_ci return ret; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "nand-rb", &rb); 11888c2ecf20Sopenharmony_ci if (ret) 11898c2ecf20Sopenharmony_ci return ret; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (cs >= ANFC_MAX_CS || rb >= ANFC_MAX_CS) { 11928c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Wrong CS %d or RB %d\n", cs, rb); 11938c2ecf20Sopenharmony_ci return -EINVAL; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (test_and_set_bit(cs, &nfc->assigned_cs)) { 11978c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Already assigned CS %d\n", cs); 11988c2ecf20Sopenharmony_ci return -EINVAL; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci anand->cs = cs; 12028c2ecf20Sopenharmony_ci anand->rb = rb; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci chip = &anand->chip; 12058c2ecf20Sopenharmony_ci mtd = nand_to_mtd(chip); 12068c2ecf20Sopenharmony_ci mtd->dev.parent = nfc->dev; 12078c2ecf20Sopenharmony_ci chip->controller = &nfc->controller; 12088c2ecf20Sopenharmony_ci chip->options = NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE | 12098c2ecf20Sopenharmony_ci NAND_USES_DMA; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci nand_set_flash_node(chip, np); 12128c2ecf20Sopenharmony_ci if (!mtd->name) { 12138c2ecf20Sopenharmony_ci dev_err(nfc->dev, "NAND label property is mandatory\n"); 12148c2ecf20Sopenharmony_ci return -EINVAL; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci ret = nand_scan(chip, 1); 12188c2ecf20Sopenharmony_ci if (ret) { 12198c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Scan operation failed\n"); 12208c2ecf20Sopenharmony_ci return ret; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 12248c2ecf20Sopenharmony_ci if (ret) { 12258c2ecf20Sopenharmony_ci nand_cleanup(chip); 12268c2ecf20Sopenharmony_ci return ret; 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci list_add_tail(&anand->node, &nfc->chips); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return 0; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic void anfc_chips_cleanup(struct arasan_nfc *nfc) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct anand *anand, *tmp; 12378c2ecf20Sopenharmony_ci struct nand_chip *chip; 12388c2ecf20Sopenharmony_ci int ret; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci list_for_each_entry_safe(anand, tmp, &nfc->chips, node) { 12418c2ecf20Sopenharmony_ci chip = &anand->chip; 12428c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 12438c2ecf20Sopenharmony_ci WARN_ON(ret); 12448c2ecf20Sopenharmony_ci nand_cleanup(chip); 12458c2ecf20Sopenharmony_ci list_del(&anand->node); 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic int anfc_chips_init(struct arasan_nfc *nfc) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci struct device_node *np = nfc->dev->of_node, *nand_np; 12528c2ecf20Sopenharmony_ci int nchips = of_get_child_count(np); 12538c2ecf20Sopenharmony_ci int ret; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci if (!nchips || nchips > ANFC_MAX_CS) { 12568c2ecf20Sopenharmony_ci dev_err(nfc->dev, "Incorrect number of NAND chips (%d)\n", 12578c2ecf20Sopenharmony_ci nchips); 12588c2ecf20Sopenharmony_ci return -EINVAL; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci for_each_child_of_node(np, nand_np) { 12628c2ecf20Sopenharmony_ci ret = anfc_chip_init(nfc, nand_np); 12638c2ecf20Sopenharmony_ci if (ret) { 12648c2ecf20Sopenharmony_ci of_node_put(nand_np); 12658c2ecf20Sopenharmony_ci anfc_chips_cleanup(nfc); 12668c2ecf20Sopenharmony_ci break; 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci return ret; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void anfc_reset(struct arasan_nfc *nfc) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci /* Disable interrupt signals */ 12768c2ecf20Sopenharmony_ci writel_relaxed(0, nfc->base + INTR_SIG_EN_REG); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci /* Enable interrupt status */ 12798c2ecf20Sopenharmony_ci writel_relaxed(EVENT_MASK, nfc->base + INTR_STS_EN_REG); 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic int anfc_probe(struct platform_device *pdev) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci struct arasan_nfc *nfc; 12858c2ecf20Sopenharmony_ci int ret; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); 12888c2ecf20Sopenharmony_ci if (!nfc) 12898c2ecf20Sopenharmony_ci return -ENOMEM; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci nfc->dev = &pdev->dev; 12928c2ecf20Sopenharmony_ci nand_controller_init(&nfc->controller); 12938c2ecf20Sopenharmony_ci nfc->controller.ops = &anfc_ops; 12948c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nfc->chips); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci nfc->base = devm_platform_ioremap_resource(pdev, 0); 12978c2ecf20Sopenharmony_ci if (IS_ERR(nfc->base)) 12988c2ecf20Sopenharmony_ci return PTR_ERR(nfc->base); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci anfc_reset(nfc); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci nfc->controller_clk = devm_clk_get(&pdev->dev, "controller"); 13038c2ecf20Sopenharmony_ci if (IS_ERR(nfc->controller_clk)) 13048c2ecf20Sopenharmony_ci return PTR_ERR(nfc->controller_clk); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci nfc->bus_clk = devm_clk_get(&pdev->dev, "bus"); 13078c2ecf20Sopenharmony_ci if (IS_ERR(nfc->bus_clk)) 13088c2ecf20Sopenharmony_ci return PTR_ERR(nfc->bus_clk); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->controller_clk); 13118c2ecf20Sopenharmony_ci if (ret) 13128c2ecf20Sopenharmony_ci return ret; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nfc->bus_clk); 13158c2ecf20Sopenharmony_ci if (ret) 13168c2ecf20Sopenharmony_ci goto disable_controller_clk; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci ret = anfc_chips_init(nfc); 13198c2ecf20Sopenharmony_ci if (ret) 13208c2ecf20Sopenharmony_ci goto disable_bus_clk; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, nfc); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci return 0; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_cidisable_bus_clk: 13278c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->bus_clk); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_cidisable_controller_clk: 13308c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->controller_clk); 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci return ret; 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic int anfc_remove(struct platform_device *pdev) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci struct arasan_nfc *nfc = platform_get_drvdata(pdev); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci anfc_chips_cleanup(nfc); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->bus_clk); 13428c2ecf20Sopenharmony_ci clk_disable_unprepare(nfc->controller_clk); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci return 0; 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic const struct of_device_id anfc_ids[] = { 13488c2ecf20Sopenharmony_ci { 13498c2ecf20Sopenharmony_ci .compatible = "xlnx,zynqmp-nand-controller", 13508c2ecf20Sopenharmony_ci }, 13518c2ecf20Sopenharmony_ci { 13528c2ecf20Sopenharmony_ci .compatible = "arasan,nfc-v3p10", 13538c2ecf20Sopenharmony_ci }, 13548c2ecf20Sopenharmony_ci {} 13558c2ecf20Sopenharmony_ci}; 13568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, anfc_ids); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_cistatic struct platform_driver anfc_driver = { 13598c2ecf20Sopenharmony_ci .driver = { 13608c2ecf20Sopenharmony_ci .name = "arasan-nand-controller", 13618c2ecf20Sopenharmony_ci .of_match_table = anfc_ids, 13628c2ecf20Sopenharmony_ci }, 13638c2ecf20Sopenharmony_ci .probe = anfc_probe, 13648c2ecf20Sopenharmony_ci .remove = anfc_remove, 13658c2ecf20Sopenharmony_ci}; 13668c2ecf20Sopenharmony_cimodule_platform_driver(anfc_driver); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 13698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Punnaiah Choudary Kalluri <punnaia@xilinx.com>"); 13708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Naga Sureshkumar Relli <nagasure@xilinx.com>"); 13718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>"); 13728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Arasan NAND Flash Controller Driver"); 1373