18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2017 ATMEL 48c2ecf20Sopenharmony_ci * Copyright 2017 Free Electrons 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Derived from the atmel_nand.c driver which contained the following 98c2ecf20Sopenharmony_ci * copyrights: 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright 2003 Rick Bronson 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) 148c2ecf20Sopenharmony_ci * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Derived from drivers/mtd/spia.c (removed in v3.8) 178c2ecf20Sopenharmony_ci * Copyright 2000 Steven J. Hill (sjhill@cotw.com) 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 218c2ecf20Sopenharmony_ci * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Derived from Das U-Boot source code 248c2ecf20Sopenharmony_ci * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) 258c2ecf20Sopenharmony_ci * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Add Programmable Multibit ECC support for various AT91 SoC 288c2ecf20Sopenharmony_ci * Copyright 2012 ATMEL, Hong Xu 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Add Nand Flash Controller support for SAMA5 SoC 318c2ecf20Sopenharmony_ci * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * A few words about the naming convention in this file. This convention 348c2ecf20Sopenharmony_ci * applies to structure and function names. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * Prefixes: 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * - atmel_nand_: all generic structures/functions 398c2ecf20Sopenharmony_ci * - atmel_smc_nand_: all structures/functions specific to the SMC interface 408c2ecf20Sopenharmony_ci * (at91sam9 and avr32 SoCs) 418c2ecf20Sopenharmony_ci * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface 428c2ecf20Sopenharmony_ci * (sama5 SoCs and later) 438c2ecf20Sopenharmony_ci * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block 448c2ecf20Sopenharmony_ci * that is available in the HSMC block 458c2ecf20Sopenharmony_ci * - <soc>_nand_: all SoC specific structures/functions 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/clk.h> 498c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 508c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 518c2ecf20Sopenharmony_ci#include <linux/genalloc.h> 528c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 538c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 548c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 558c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/atmel-matrix.h> 568c2ecf20Sopenharmony_ci#include <linux/mfd/syscon/atmel-smc.h> 578c2ecf20Sopenharmony_ci#include <linux/module.h> 588c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 598c2ecf20Sopenharmony_ci#include <linux/of_address.h> 608c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 618c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 628c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 638c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 648c2ecf20Sopenharmony_ci#include <linux/regmap.h> 658c2ecf20Sopenharmony_ci#include <soc/at91/atmel-sfr.h> 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#include "pmecc.h" 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG 0x0 708c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) 718c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) 728c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20)) 738c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16) 748c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13) 758c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12) 768c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9) 778c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8) 788c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0) 798c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL 0x4 828c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL_EN BIT(0) 838c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR 0x8 868c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_IER 0xc 878c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_IDR 0x10 888c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_IMR 0x14 898c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1) 908c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4) 918c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5) 928c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_BUSY BIT(8) 938c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_WR BIT(11) 948c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12) 958c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16) 968c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17) 978c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_DTOE BIT(20) 988c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21) 998c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_AWB BIT(22) 1008c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23) 1018c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \ 1028c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_SR_UNDEF | \ 1038c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_SR_AWB | \ 1048c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_SR_NFCASE) 1058c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24) 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_ADDR 0x18 1088c2ecf20Sopenharmony_ci#define ATMEL_HSMC_NFC_BANK 0x1c 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define ATMEL_NFC_MAX_RB_ID 7 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define ATMEL_NFC_SRAM_SIZE 0x2400 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2)) 1158c2ecf20Sopenharmony_ci#define ATMEL_NFC_VCMD2 BIT(18) 1168c2ecf20Sopenharmony_ci#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19) 1178c2ecf20Sopenharmony_ci#define ATMEL_NFC_CSID(cs) ((cs) << 22) 1188c2ecf20Sopenharmony_ci#define ATMEL_NFC_DATAEN BIT(25) 1198c2ecf20Sopenharmony_ci#define ATMEL_NFC_NFCWR BIT(26) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define ATMEL_NFC_MAX_ADDR_CYCLES 5 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define ATMEL_NAND_ALE_OFFSET BIT(21) 1248c2ecf20Sopenharmony_ci#define ATMEL_NAND_CLE_OFFSET BIT(22) 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci#define DEFAULT_TIMEOUT_MS 1000 1278c2ecf20Sopenharmony_ci#define MIN_DMA_LEN 128 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic bool atmel_nand_avoid_dma __read_mostly; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(avoiddma, "Avoid using DMA"); 1328c2ecf20Sopenharmony_cimodule_param_named(avoiddma, atmel_nand_avoid_dma, bool, 0400); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cienum atmel_nand_rb_type { 1358c2ecf20Sopenharmony_ci ATMEL_NAND_NO_RB, 1368c2ecf20Sopenharmony_ci ATMEL_NAND_NATIVE_RB, 1378c2ecf20Sopenharmony_ci ATMEL_NAND_GPIO_RB, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistruct atmel_nand_rb { 1418c2ecf20Sopenharmony_ci enum atmel_nand_rb_type type; 1428c2ecf20Sopenharmony_ci union { 1438c2ecf20Sopenharmony_ci struct gpio_desc *gpio; 1448c2ecf20Sopenharmony_ci int id; 1458c2ecf20Sopenharmony_ci }; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistruct atmel_nand_cs { 1498c2ecf20Sopenharmony_ci int id; 1508c2ecf20Sopenharmony_ci struct atmel_nand_rb rb; 1518c2ecf20Sopenharmony_ci struct gpio_desc *csgpio; 1528c2ecf20Sopenharmony_ci struct { 1538c2ecf20Sopenharmony_ci void __iomem *virt; 1548c2ecf20Sopenharmony_ci dma_addr_t dma; 1558c2ecf20Sopenharmony_ci } io; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci struct atmel_smc_cs_conf smcconf; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistruct atmel_nand { 1618c2ecf20Sopenharmony_ci struct list_head node; 1628c2ecf20Sopenharmony_ci struct device *dev; 1638c2ecf20Sopenharmony_ci struct nand_chip base; 1648c2ecf20Sopenharmony_ci struct atmel_nand_cs *activecs; 1658c2ecf20Sopenharmony_ci struct atmel_pmecc_user *pmecc; 1668c2ecf20Sopenharmony_ci struct gpio_desc *cdgpio; 1678c2ecf20Sopenharmony_ci int numcs; 1688c2ecf20Sopenharmony_ci struct atmel_nand_cs cs[]; 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci return container_of(chip, struct atmel_nand, base); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cienum atmel_nfc_data_xfer { 1778c2ecf20Sopenharmony_ci ATMEL_NFC_NO_DATA, 1788c2ecf20Sopenharmony_ci ATMEL_NFC_READ_DATA, 1798c2ecf20Sopenharmony_ci ATMEL_NFC_WRITE_DATA, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistruct atmel_nfc_op { 1838c2ecf20Sopenharmony_ci u8 cs; 1848c2ecf20Sopenharmony_ci u8 ncmds; 1858c2ecf20Sopenharmony_ci u8 cmds[2]; 1868c2ecf20Sopenharmony_ci u8 naddrs; 1878c2ecf20Sopenharmony_ci u8 addrs[5]; 1888c2ecf20Sopenharmony_ci enum atmel_nfc_data_xfer data; 1898c2ecf20Sopenharmony_ci u32 wait; 1908c2ecf20Sopenharmony_ci u32 errors; 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistruct atmel_nand_controller; 1948c2ecf20Sopenharmony_cistruct atmel_nand_controller_caps; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct atmel_nand_controller_ops { 1978c2ecf20Sopenharmony_ci int (*probe)(struct platform_device *pdev, 1988c2ecf20Sopenharmony_ci const struct atmel_nand_controller_caps *caps); 1998c2ecf20Sopenharmony_ci int (*remove)(struct atmel_nand_controller *nc); 2008c2ecf20Sopenharmony_ci void (*nand_init)(struct atmel_nand_controller *nc, 2018c2ecf20Sopenharmony_ci struct atmel_nand *nand); 2028c2ecf20Sopenharmony_ci int (*ecc_init)(struct nand_chip *chip); 2038c2ecf20Sopenharmony_ci int (*setup_interface)(struct atmel_nand *nand, int csline, 2048c2ecf20Sopenharmony_ci const struct nand_interface_config *conf); 2058c2ecf20Sopenharmony_ci int (*exec_op)(struct atmel_nand *nand, 2068c2ecf20Sopenharmony_ci const struct nand_operation *op, bool check_only); 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistruct atmel_nand_controller_caps { 2108c2ecf20Sopenharmony_ci bool has_dma; 2118c2ecf20Sopenharmony_ci bool legacy_of_bindings; 2128c2ecf20Sopenharmony_ci u32 ale_offs; 2138c2ecf20Sopenharmony_ci u32 cle_offs; 2148c2ecf20Sopenharmony_ci const char *ebi_csa_regmap_name; 2158c2ecf20Sopenharmony_ci const struct atmel_nand_controller_ops *ops; 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistruct atmel_nand_controller { 2198c2ecf20Sopenharmony_ci struct nand_controller base; 2208c2ecf20Sopenharmony_ci const struct atmel_nand_controller_caps *caps; 2218c2ecf20Sopenharmony_ci struct device *dev; 2228c2ecf20Sopenharmony_ci struct regmap *smc; 2238c2ecf20Sopenharmony_ci struct dma_chan *dmac; 2248c2ecf20Sopenharmony_ci struct atmel_pmecc *pmecc; 2258c2ecf20Sopenharmony_ci struct list_head chips; 2268c2ecf20Sopenharmony_ci struct clk *mck; 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic inline struct atmel_nand_controller * 2308c2ecf20Sopenharmony_cito_nand_controller(struct nand_controller *ctl) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci return container_of(ctl, struct atmel_nand_controller, base); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistruct atmel_smc_nand_ebi_csa_cfg { 2368c2ecf20Sopenharmony_ci u32 offs; 2378c2ecf20Sopenharmony_ci u32 nfd0_on_d16; 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistruct atmel_smc_nand_controller { 2418c2ecf20Sopenharmony_ci struct atmel_nand_controller base; 2428c2ecf20Sopenharmony_ci struct regmap *ebi_csa_regmap; 2438c2ecf20Sopenharmony_ci struct atmel_smc_nand_ebi_csa_cfg *ebi_csa; 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic inline struct atmel_smc_nand_controller * 2478c2ecf20Sopenharmony_cito_smc_nand_controller(struct nand_controller *ctl) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci return container_of(to_nand_controller(ctl), 2508c2ecf20Sopenharmony_ci struct atmel_smc_nand_controller, base); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistruct atmel_hsmc_nand_controller { 2548c2ecf20Sopenharmony_ci struct atmel_nand_controller base; 2558c2ecf20Sopenharmony_ci struct { 2568c2ecf20Sopenharmony_ci struct gen_pool *pool; 2578c2ecf20Sopenharmony_ci void __iomem *virt; 2588c2ecf20Sopenharmony_ci dma_addr_t dma; 2598c2ecf20Sopenharmony_ci } sram; 2608c2ecf20Sopenharmony_ci const struct atmel_hsmc_reg_layout *hsmc_layout; 2618c2ecf20Sopenharmony_ci struct regmap *io; 2628c2ecf20Sopenharmony_ci struct atmel_nfc_op op; 2638c2ecf20Sopenharmony_ci struct completion complete; 2648c2ecf20Sopenharmony_ci u32 cfg; 2658c2ecf20Sopenharmony_ci int irq; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Only used when instantiating from legacy DT bindings. */ 2688c2ecf20Sopenharmony_ci struct clk *clk; 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline struct atmel_hsmc_nand_controller * 2728c2ecf20Sopenharmony_cito_hsmc_nand_controller(struct nand_controller *ctl) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return container_of(to_nand_controller(ctl), 2758c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller, base); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS; 2818c2ecf20Sopenharmony_ci op->wait ^= status & op->wait; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return !op->wait || op->errors; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic irqreturn_t atmel_nfc_interrupt(int irq, void *data) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc = data; 2898c2ecf20Sopenharmony_ci u32 sr, rcvd; 2908c2ecf20Sopenharmony_ci bool done; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); 2958c2ecf20Sopenharmony_ci done = atmel_nfc_op_done(&nc->op, sr); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (rcvd) 2988c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (done) 3018c2ecf20Sopenharmony_ci complete(&nc->complete); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return rcvd ? IRQ_HANDLED : IRQ_NONE; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll, 3078c2ecf20Sopenharmony_ci unsigned int timeout_ms) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci int ret; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!timeout_ms) 3128c2ecf20Sopenharmony_ci timeout_ms = DEFAULT_TIMEOUT_MS; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (poll) { 3158c2ecf20Sopenharmony_ci u32 status; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = regmap_read_poll_timeout(nc->base.smc, 3188c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_SR, status, 3198c2ecf20Sopenharmony_ci atmel_nfc_op_done(&nc->op, 3208c2ecf20Sopenharmony_ci status), 3218c2ecf20Sopenharmony_ci 0, timeout_ms * 1000); 3228c2ecf20Sopenharmony_ci } else { 3238c2ecf20Sopenharmony_ci init_completion(&nc->complete); 3248c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER, 3258c2ecf20Sopenharmony_ci nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); 3268c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&nc->complete, 3278c2ecf20Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 3288c2ecf20Sopenharmony_ci if (!ret) 3298c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3308c2ecf20Sopenharmony_ci else 3318c2ecf20Sopenharmony_ci ret = 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) { 3378c2ecf20Sopenharmony_ci dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n"); 3388c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) { 3428c2ecf20Sopenharmony_ci dev_err(nc->base.dev, "Access to an undefined area\n"); 3438c2ecf20Sopenharmony_ci ret = -EIO; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) { 3478c2ecf20Sopenharmony_ci dev_err(nc->base.dev, "Access while busy\n"); 3488c2ecf20Sopenharmony_ci ret = -EIO; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) { 3528c2ecf20Sopenharmony_ci dev_err(nc->base.dev, "Wrong access size\n"); 3538c2ecf20Sopenharmony_ci ret = -EIO; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void atmel_nand_dma_transfer_finished(void *data) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct completion *finished = data; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci complete(finished); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int atmel_nand_dma_transfer(struct atmel_nand_controller *nc, 3678c2ecf20Sopenharmony_ci void *buf, dma_addr_t dev_dma, size_t len, 3688c2ecf20Sopenharmony_ci enum dma_data_direction dir) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(finished); 3718c2ecf20Sopenharmony_ci dma_addr_t src_dma, dst_dma, buf_dma; 3728c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 3738c2ecf20Sopenharmony_ci dma_cookie_t cookie; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci buf_dma = dma_map_single(nc->dev, buf, len, dir); 3768c2ecf20Sopenharmony_ci if (dma_mapping_error(nc->dev, dev_dma)) { 3778c2ecf20Sopenharmony_ci dev_err(nc->dev, 3788c2ecf20Sopenharmony_ci "Failed to prepare a buffer for DMA access\n"); 3798c2ecf20Sopenharmony_ci goto err; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (dir == DMA_FROM_DEVICE) { 3838c2ecf20Sopenharmony_ci src_dma = dev_dma; 3848c2ecf20Sopenharmony_ci dst_dma = buf_dma; 3858c2ecf20Sopenharmony_ci } else { 3868c2ecf20Sopenharmony_ci src_dma = buf_dma; 3878c2ecf20Sopenharmony_ci dst_dma = dev_dma; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len, 3918c2ecf20Sopenharmony_ci DMA_CTRL_ACK | DMA_PREP_INTERRUPT); 3928c2ecf20Sopenharmony_ci if (!tx) { 3938c2ecf20Sopenharmony_ci dev_err(nc->dev, "Failed to prepare DMA memcpy\n"); 3948c2ecf20Sopenharmony_ci goto err_unmap; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci tx->callback = atmel_nand_dma_transfer_finished; 3988c2ecf20Sopenharmony_ci tx->callback_param = &finished; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci cookie = dmaengine_submit(tx); 4018c2ecf20Sopenharmony_ci if (dma_submit_error(cookie)) { 4028c2ecf20Sopenharmony_ci dev_err(nc->dev, "Failed to do DMA tx_submit\n"); 4038c2ecf20Sopenharmony_ci goto err_unmap; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci dma_async_issue_pending(nc->dmac); 4078c2ecf20Sopenharmony_ci wait_for_completion(&finished); 4088c2ecf20Sopenharmony_ci dma_unmap_single(nc->dev, buf_dma, len, dir); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cierr_unmap: 4138c2ecf20Sopenharmony_ci dma_unmap_single(nc->dev, buf_dma, len, dir); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cierr: 4168c2ecf20Sopenharmony_ci dev_dbg(nc->dev, "Fall back to CPU I/O\n"); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return -EIO; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci u8 *addrs = nc->op.addrs; 4248c2ecf20Sopenharmony_ci unsigned int op = 0; 4258c2ecf20Sopenharmony_ci u32 addr, val; 4268c2ecf20Sopenharmony_ci int i, ret; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci for (i = 0; i < nc->op.ncmds; i++) 4318c2ecf20Sopenharmony_ci op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) 4348c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci op |= ATMEL_NFC_CSID(nc->op.cs) | 4378c2ecf20Sopenharmony_ci ATMEL_NFC_ACYCLE(nc->op.naddrs); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (nc->op.ncmds > 1) 4408c2ecf20Sopenharmony_ci op |= ATMEL_NFC_VCMD2; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | 4438c2ecf20Sopenharmony_ci (addrs[3] << 24); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (nc->op.data != ATMEL_NFC_NO_DATA) { 4468c2ecf20Sopenharmony_ci op |= ATMEL_NFC_DATAEN; 4478c2ecf20Sopenharmony_ci nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (nc->op.data == ATMEL_NFC_WRITE_DATA) 4508c2ecf20Sopenharmony_ci op |= ATMEL_NFC_NFCWR; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* Clear all flags. */ 4548c2ecf20Sopenharmony_ci regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Send the command. */ 4578c2ecf20Sopenharmony_ci regmap_write(nc->io, op, addr); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = atmel_nfc_wait(nc, poll, 0); 4608c2ecf20Sopenharmony_ci if (ret) 4618c2ecf20Sopenharmony_ci dev_err(nc->base.dev, 4628c2ecf20Sopenharmony_ci "Failed to send NAND command (err = %d)!", 4638c2ecf20Sopenharmony_ci ret); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Reset the op state. */ 4668c2ecf20Sopenharmony_ci memset(&nc->op, 0, sizeof(nc->op)); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void atmel_nand_data_in(struct atmel_nand *nand, void *buf, 4728c2ecf20Sopenharmony_ci unsigned int len, bool force_8bit) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* 4798c2ecf20Sopenharmony_ci * If the controller supports DMA, the buffer address is DMA-able and 4808c2ecf20Sopenharmony_ci * len is long enough to make DMA transfers profitable, let's trigger 4818c2ecf20Sopenharmony_ci * a DMA transfer. If it fails, fallback to PIO mode. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci if (nc->dmac && virt_addr_valid(buf) && 4848c2ecf20Sopenharmony_ci len >= MIN_DMA_LEN && !force_8bit && 4858c2ecf20Sopenharmony_ci !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len, 4868c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit) 4908c2ecf20Sopenharmony_ci ioread16_rep(nand->activecs->io.virt, buf, len / 2); 4918c2ecf20Sopenharmony_ci else 4928c2ecf20Sopenharmony_ci ioread8_rep(nand->activecs->io.virt, buf, len); 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic void atmel_nand_data_out(struct atmel_nand *nand, const void *buf, 4968c2ecf20Sopenharmony_ci unsigned int len, bool force_8bit) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * If the controller supports DMA, the buffer address is DMA-able and 5048c2ecf20Sopenharmony_ci * len is long enough to make DMA transfers profitable, let's trigger 5058c2ecf20Sopenharmony_ci * a DMA transfer. If it fails, fallback to PIO mode. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci if (nc->dmac && virt_addr_valid(buf) && 5088c2ecf20Sopenharmony_ci len >= MIN_DMA_LEN && !force_8bit && 5098c2ecf20Sopenharmony_ci !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma, 5108c2ecf20Sopenharmony_ci len, DMA_TO_DEVICE)) 5118c2ecf20Sopenharmony_ci return; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit) 5148c2ecf20Sopenharmony_ci iowrite16_rep(nand->activecs->io.virt, buf, len / 2); 5158c2ecf20Sopenharmony_ci else 5168c2ecf20Sopenharmony_ci iowrite8_rep(nand->activecs->io.virt, buf, len); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic int atmel_nand_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci if (nand->activecs->rb.type == ATMEL_NAND_NO_RB) 5228c2ecf20Sopenharmony_ci return nand_soft_waitrdy(&nand->base, timeout_ms); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio, 5258c2ecf20Sopenharmony_ci timeout_ms); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand, 5298c2ecf20Sopenharmony_ci unsigned int timeout_ms) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 5328c2ecf20Sopenharmony_ci u32 status, mask; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) 5358c2ecf20Sopenharmony_ci return atmel_nand_waitrdy(nand, timeout_ms); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(nand->base.controller); 5388c2ecf20Sopenharmony_ci mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); 5398c2ecf20Sopenharmony_ci return regmap_read_poll_timeout_atomic(nc->base.smc, ATMEL_HSMC_NFC_SR, 5408c2ecf20Sopenharmony_ci status, status & mask, 5418c2ecf20Sopenharmony_ci 10, timeout_ms * 1000); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic void atmel_nand_select_target(struct atmel_nand *nand, 5458c2ecf20Sopenharmony_ci unsigned int cs) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci nand->activecs = &nand->cs[cs]; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic void atmel_hsmc_nand_select_target(struct atmel_nand *nand, 5518c2ecf20Sopenharmony_ci unsigned int cs) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&nand->base); 5548c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 5558c2ecf20Sopenharmony_ci u32 cfg = ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | 5568c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | 5578c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_RSPARE; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci nand->activecs = &nand->cs[cs]; 5608c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(nand->base.controller); 5618c2ecf20Sopenharmony_ci if (nc->cfg == cfg) 5628c2ecf20Sopenharmony_ci return; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG, 5658c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK | 5668c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK | 5678c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_RSPARE | 5688c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_WSPARE, 5698c2ecf20Sopenharmony_ci cfg); 5708c2ecf20Sopenharmony_ci nc->cfg = cfg; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int atmel_smc_nand_exec_instr(struct atmel_nand *nand, 5748c2ecf20Sopenharmony_ci const struct nand_op_instr *instr) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 5778c2ecf20Sopenharmony_ci unsigned int i; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 5808c2ecf20Sopenharmony_ci switch (instr->type) { 5818c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 5828c2ecf20Sopenharmony_ci writeb(instr->ctx.cmd.opcode, 5838c2ecf20Sopenharmony_ci nand->activecs->io.virt + nc->caps->cle_offs); 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 5868c2ecf20Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) 5878c2ecf20Sopenharmony_ci writeb(instr->ctx.addr.addrs[i], 5888c2ecf20Sopenharmony_ci nand->activecs->io.virt + nc->caps->ale_offs); 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 5918c2ecf20Sopenharmony_ci atmel_nand_data_in(nand, instr->ctx.data.buf.in, 5928c2ecf20Sopenharmony_ci instr->ctx.data.len, 5938c2ecf20Sopenharmony_ci instr->ctx.data.force_8bit); 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 5968c2ecf20Sopenharmony_ci atmel_nand_data_out(nand, instr->ctx.data.buf.out, 5978c2ecf20Sopenharmony_ci instr->ctx.data.len, 5988c2ecf20Sopenharmony_ci instr->ctx.data.force_8bit); 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 6018c2ecf20Sopenharmony_ci return atmel_nand_waitrdy(nand, 6028c2ecf20Sopenharmony_ci instr->ctx.waitrdy.timeout_ms); 6038c2ecf20Sopenharmony_ci default: 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic int atmel_smc_nand_exec_op(struct atmel_nand *nand, 6118c2ecf20Sopenharmony_ci const struct nand_operation *op, 6128c2ecf20Sopenharmony_ci bool check_only) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci unsigned int i; 6158c2ecf20Sopenharmony_ci int ret = 0; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (check_only) 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci atmel_nand_select_target(nand, op->cs); 6218c2ecf20Sopenharmony_ci gpiod_set_value(nand->activecs->csgpio, 0); 6228c2ecf20Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 6238c2ecf20Sopenharmony_ci ret = atmel_smc_nand_exec_instr(nand, &op->instrs[i]); 6248c2ecf20Sopenharmony_ci if (ret) 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci gpiod_set_value(nand->activecs->csgpio, 1); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return ret; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int atmel_hsmc_exec_cmd_addr(struct nand_chip *chip, 6338c2ecf20Sopenharmony_ci const struct nand_subop *subop) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 6368c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 6378c2ecf20Sopenharmony_ci unsigned int i, j; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci nc->op.cs = nand->activecs->id; 6428c2ecf20Sopenharmony_ci for (i = 0; i < subop->ninstrs; i++) { 6438c2ecf20Sopenharmony_ci const struct nand_op_instr *instr = &subop->instrs[i]; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (instr->type == NAND_OP_CMD_INSTR) { 6468c2ecf20Sopenharmony_ci nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode; 6478c2ecf20Sopenharmony_ci continue; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci for (j = nand_subop_get_addr_start_off(subop, i); 6518c2ecf20Sopenharmony_ci j < nand_subop_get_num_addr_cyc(subop, i); j++) { 6528c2ecf20Sopenharmony_ci nc->op.addrs[nc->op.naddrs] = instr->ctx.addr.addrs[j]; 6538c2ecf20Sopenharmony_ci nc->op.naddrs++; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return atmel_nfc_exec_op(nc, true); 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int atmel_hsmc_exec_rw(struct nand_chip *chip, 6618c2ecf20Sopenharmony_ci const struct nand_subop *subop) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci const struct nand_op_instr *instr = subop->instrs; 6648c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (instr->type == NAND_OP_DATA_IN_INSTR) 6678c2ecf20Sopenharmony_ci atmel_nand_data_in(nand, instr->ctx.data.buf.in, 6688c2ecf20Sopenharmony_ci instr->ctx.data.len, 6698c2ecf20Sopenharmony_ci instr->ctx.data.force_8bit); 6708c2ecf20Sopenharmony_ci else 6718c2ecf20Sopenharmony_ci atmel_nand_data_out(nand, instr->ctx.data.buf.out, 6728c2ecf20Sopenharmony_ci instr->ctx.data.len, 6738c2ecf20Sopenharmony_ci instr->ctx.data.force_8bit); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci return 0; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int atmel_hsmc_exec_waitrdy(struct nand_chip *chip, 6798c2ecf20Sopenharmony_ci const struct nand_subop *subop) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci const struct nand_op_instr *instr = subop->instrs; 6828c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci return atmel_hsmc_nand_waitrdy(nand, instr->ctx.waitrdy.timeout_ms); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic const struct nand_op_parser atmel_hsmc_op_parser = NAND_OP_PARSER( 6888c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_cmd_addr, 6898c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 6908c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5), 6918c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true)), 6928c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw, 6938c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 0)), 6948c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw, 6958c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 0)), 6968c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_waitrdy, 6978c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 6988c2ecf20Sopenharmony_ci); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_exec_op(struct atmel_nand *nand, 7018c2ecf20Sopenharmony_ci const struct nand_operation *op, 7028c2ecf20Sopenharmony_ci bool check_only) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci int ret; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (check_only) 7078c2ecf20Sopenharmony_ci return nand_op_parser_exec_op(&nand->base, 7088c2ecf20Sopenharmony_ci &atmel_hsmc_op_parser, op, true); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci atmel_hsmc_nand_select_target(nand, op->cs); 7118c2ecf20Sopenharmony_ci ret = nand_op_parser_exec_op(&nand->base, &atmel_hsmc_op_parser, op, 7128c2ecf20Sopenharmony_ci false); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci return ret; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf, 7188c2ecf20Sopenharmony_ci bool oob_required) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7218c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 7228c2ecf20Sopenharmony_ci int ret = -EIO; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (nc->base.dmac) 7278c2ecf20Sopenharmony_ci ret = atmel_nand_dma_transfer(&nc->base, (void *)buf, 7288c2ecf20Sopenharmony_ci nc->sram.dma, mtd->writesize, 7298c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* Falling back to CPU copy. */ 7328c2ecf20Sopenharmony_ci if (ret) 7338c2ecf20Sopenharmony_ci memcpy_toio(nc->sram.virt, buf, mtd->writesize); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci if (oob_required) 7368c2ecf20Sopenharmony_ci memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, 7378c2ecf20Sopenharmony_ci mtd->oobsize); 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf, 7418c2ecf20Sopenharmony_ci bool oob_required) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7448c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 7458c2ecf20Sopenharmony_ci int ret = -EIO; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (nc->base.dmac) 7508c2ecf20Sopenharmony_ci ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma, 7518c2ecf20Sopenharmony_ci mtd->writesize, DMA_FROM_DEVICE); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* Falling back to CPU copy. */ 7548c2ecf20Sopenharmony_ci if (ret) 7558c2ecf20Sopenharmony_ci memcpy_fromio(buf, nc->sram.virt, mtd->writesize); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (oob_required) 7588c2ecf20Sopenharmony_ci memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, 7598c2ecf20Sopenharmony_ci mtd->oobsize); 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7658c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (column >= 0) { 7708c2ecf20Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = column; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* 7738c2ecf20Sopenharmony_ci * 2 address cycles for the column offset on large page NANDs. 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ci if (mtd->writesize > 512) 7768c2ecf20Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = column >> 8; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (page >= 0) { 7808c2ecf20Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = page; 7818c2ecf20Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = page >> 8; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (chip->options & NAND_ROW_ADDR_3) 7848c2ecf20Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = page >> 16; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 7918c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 7928c2ecf20Sopenharmony_ci int ret; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci nc = to_nand_controller(chip->controller); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (raw) 7978c2ecf20Sopenharmony_ci return 0; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci ret = atmel_pmecc_enable(nand->pmecc, op); 8008c2ecf20Sopenharmony_ci if (ret) 8018c2ecf20Sopenharmony_ci dev_err(nc->dev, 8028c2ecf20Sopenharmony_ci "Failed to enable ECC engine (err = %d)\n", ret); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return ret; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (!raw) 8128c2ecf20Sopenharmony_ci atmel_pmecc_disable(nand->pmecc); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 8188c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 8198c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 8208c2ecf20Sopenharmony_ci struct mtd_oob_region oobregion; 8218c2ecf20Sopenharmony_ci void *eccbuf; 8228c2ecf20Sopenharmony_ci int ret, i; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci nc = to_nand_controller(chip->controller); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (raw) 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ret = atmel_pmecc_wait_rdy(nand->pmecc); 8308c2ecf20Sopenharmony_ci if (ret) { 8318c2ecf20Sopenharmony_ci dev_err(nc->dev, 8328c2ecf20Sopenharmony_ci "Failed to transfer NAND page data (err = %d)\n", 8338c2ecf20Sopenharmony_ci ret); 8348c2ecf20Sopenharmony_ci return ret; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &oobregion); 8388c2ecf20Sopenharmony_ci eccbuf = chip->oob_poi + oobregion.offset; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci for (i = 0; i < chip->ecc.steps; i++) { 8418c2ecf20Sopenharmony_ci atmel_pmecc_get_generated_eccbytes(nand->pmecc, i, 8428c2ecf20Sopenharmony_ci eccbuf); 8438c2ecf20Sopenharmony_ci eccbuf += chip->ecc.bytes; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return 0; 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, 8508c2ecf20Sopenharmony_ci bool raw) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 8538c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 8548c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 8558c2ecf20Sopenharmony_ci struct mtd_oob_region oobregion; 8568c2ecf20Sopenharmony_ci int ret, i, max_bitflips = 0; 8578c2ecf20Sopenharmony_ci void *databuf, *eccbuf; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci nc = to_nand_controller(chip->controller); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (raw) 8628c2ecf20Sopenharmony_ci return 0; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci ret = atmel_pmecc_wait_rdy(nand->pmecc); 8658c2ecf20Sopenharmony_ci if (ret) { 8668c2ecf20Sopenharmony_ci dev_err(nc->dev, 8678c2ecf20Sopenharmony_ci "Failed to read NAND page data (err = %d)\n", 8688c2ecf20Sopenharmony_ci ret); 8698c2ecf20Sopenharmony_ci return ret; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &oobregion); 8738c2ecf20Sopenharmony_ci eccbuf = chip->oob_poi + oobregion.offset; 8748c2ecf20Sopenharmony_ci databuf = buf; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci for (i = 0; i < chip->ecc.steps; i++) { 8778c2ecf20Sopenharmony_ci ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf, 8788c2ecf20Sopenharmony_ci eccbuf); 8798c2ecf20Sopenharmony_ci if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc)) 8808c2ecf20Sopenharmony_ci ret = nand_check_erased_ecc_chunk(databuf, 8818c2ecf20Sopenharmony_ci chip->ecc.size, 8828c2ecf20Sopenharmony_ci eccbuf, 8838c2ecf20Sopenharmony_ci chip->ecc.bytes, 8848c2ecf20Sopenharmony_ci NULL, 0, 8858c2ecf20Sopenharmony_ci chip->ecc.strength); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (ret >= 0) { 8888c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ret; 8898c2ecf20Sopenharmony_ci max_bitflips = max(ret, max_bitflips); 8908c2ecf20Sopenharmony_ci } else { 8918c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci databuf += chip->ecc.size; 8958c2ecf20Sopenharmony_ci eccbuf += chip->ecc.bytes; 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci return max_bitflips; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, 9028c2ecf20Sopenharmony_ci bool oob_required, int page, bool raw) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 9058c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 9068c2ecf20Sopenharmony_ci int ret; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); 9118c2ecf20Sopenharmony_ci if (ret) 9128c2ecf20Sopenharmony_ci return ret; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci nand_write_data_op(chip, buf, mtd->writesize, false); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); 9178c2ecf20Sopenharmony_ci if (ret) { 9188c2ecf20Sopenharmony_ci atmel_pmecc_disable(nand->pmecc); 9198c2ecf20Sopenharmony_ci return ret; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_write_page(struct nand_chip *chip, const u8 *buf, 9308c2ecf20Sopenharmony_ci int oob_required, int page) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_write_page_raw(struct nand_chip *chip, 9368c2ecf20Sopenharmony_ci const u8 *buf, int oob_required, 9378c2ecf20Sopenharmony_ci int page) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true); 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, 9438c2ecf20Sopenharmony_ci bool oob_required, int page, bool raw) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 9468c2ecf20Sopenharmony_ci int ret; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); 9518c2ecf20Sopenharmony_ci if (ret) 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci ret = nand_read_data_op(chip, buf, mtd->writesize, false, false); 9558c2ecf20Sopenharmony_ci if (ret) 9568c2ecf20Sopenharmony_ci goto out_disable; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, false); 9598c2ecf20Sopenharmony_ci if (ret) 9608c2ecf20Sopenharmony_ci goto out_disable; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_correct_data(chip, buf, raw); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ciout_disable: 9658c2ecf20Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return ret; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf, 9718c2ecf20Sopenharmony_ci int oob_required, int page) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false); 9748c2ecf20Sopenharmony_ci} 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_read_page_raw(struct nand_chip *chip, u8 *buf, 9778c2ecf20Sopenharmony_ci int oob_required, int page) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true); 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, 9838c2ecf20Sopenharmony_ci const u8 *buf, bool oob_required, 9848c2ecf20Sopenharmony_ci int page, bool raw) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 9878c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 9888c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 9898c2ecf20Sopenharmony_ci int ret; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci atmel_hsmc_nand_select_target(nand, chip->cur_cs); 9928c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci atmel_nfc_copy_to_sram(chip, buf, false); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci nc->op.cmds[0] = NAND_CMD_SEQIN; 9978c2ecf20Sopenharmony_ci nc->op.ncmds = 1; 9988c2ecf20Sopenharmony_ci atmel_nfc_set_op_addr(chip, page, 0x0); 9998c2ecf20Sopenharmony_ci nc->op.cs = nand->activecs->id; 10008c2ecf20Sopenharmony_ci nc->op.data = ATMEL_NFC_WRITE_DATA; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); 10038c2ecf20Sopenharmony_ci if (ret) 10048c2ecf20Sopenharmony_ci return ret; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci ret = atmel_nfc_exec_op(nc, false); 10078c2ecf20Sopenharmony_ci if (ret) { 10088c2ecf20Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 10098c2ecf20Sopenharmony_ci dev_err(nc->base.dev, 10108c2ecf20Sopenharmony_ci "Failed to transfer NAND page data (err = %d)\n", 10118c2ecf20Sopenharmony_ci ret); 10128c2ecf20Sopenharmony_ci return ret; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci if (ret) 10208c2ecf20Sopenharmony_ci return ret; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_page(struct nand_chip *chip, 10288c2ecf20Sopenharmony_ci const u8 *buf, int oob_required, 10298c2ecf20Sopenharmony_ci int page) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, 10328c2ecf20Sopenharmony_ci false); 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_page_raw(struct nand_chip *chip, 10368c2ecf20Sopenharmony_ci const u8 *buf, 10378c2ecf20Sopenharmony_ci int oob_required, int page) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, 10408c2ecf20Sopenharmony_ci true); 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, 10448c2ecf20Sopenharmony_ci bool oob_required, int page, 10458c2ecf20Sopenharmony_ci bool raw) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 10488c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 10498c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 10508c2ecf20Sopenharmony_ci int ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci atmel_hsmc_nand_select_target(nand, chip->cur_cs); 10538c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* 10568c2ecf20Sopenharmony_ci * Optimized read page accessors only work when the NAND R/B pin is 10578c2ecf20Sopenharmony_ci * connected to a native SoC R/B pin. If that's not the case, fallback 10588c2ecf20Sopenharmony_ci * to the non-optimized one. 10598c2ecf20Sopenharmony_ci */ 10608c2ecf20Sopenharmony_ci if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) 10618c2ecf20Sopenharmony_ci return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, 10628c2ecf20Sopenharmony_ci raw); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci if (mtd->writesize > 512) 10678c2ecf20Sopenharmony_ci nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci atmel_nfc_set_op_addr(chip, page, 0x0); 10708c2ecf20Sopenharmony_ci nc->op.cs = nand->activecs->id; 10718c2ecf20Sopenharmony_ci nc->op.data = ATMEL_NFC_READ_DATA; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); 10748c2ecf20Sopenharmony_ci if (ret) 10758c2ecf20Sopenharmony_ci return ret; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci ret = atmel_nfc_exec_op(nc, false); 10788c2ecf20Sopenharmony_ci if (ret) { 10798c2ecf20Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 10808c2ecf20Sopenharmony_ci dev_err(nc->base.dev, 10818c2ecf20Sopenharmony_ci "Failed to load NAND page data (err = %d)\n", 10828c2ecf20Sopenharmony_ci ret); 10838c2ecf20Sopenharmony_ci return ret; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci atmel_nfc_copy_from_sram(chip, buf, true); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_correct_data(chip, buf, raw); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return ret; 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf, 10968c2ecf20Sopenharmony_ci int oob_required, int page) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, 10998c2ecf20Sopenharmony_ci false); 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_page_raw(struct nand_chip *chip, 11038c2ecf20Sopenharmony_ci u8 *buf, int oob_required, 11048c2ecf20Sopenharmony_ci int page) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, 11078c2ecf20Sopenharmony_ci true); 11088c2ecf20Sopenharmony_ci} 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic int atmel_nand_pmecc_init(struct nand_chip *chip) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 11138c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 11148c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 11158c2ecf20Sopenharmony_ci struct nand_device *nanddev = mtd_to_nanddev(mtd); 11168c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 11178c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 11188c2ecf20Sopenharmony_ci struct atmel_pmecc_user_req req; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci nc = to_nand_controller(chip->controller); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (!nc->pmecc) { 11238c2ecf20Sopenharmony_ci dev_err(nc->dev, "HW ECC not supported\n"); 11248c2ecf20Sopenharmony_ci return -ENOTSUPP; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (nc->caps->legacy_of_bindings) { 11288c2ecf20Sopenharmony_ci u32 val; 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap", 11318c2ecf20Sopenharmony_ci &val)) 11328c2ecf20Sopenharmony_ci chip->ecc.strength = val; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (!of_property_read_u32(nc->dev->of_node, 11358c2ecf20Sopenharmony_ci "atmel,pmecc-sector-size", 11368c2ecf20Sopenharmony_ci &val)) 11378c2ecf20Sopenharmony_ci chip->ecc.size = val; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) 11418c2ecf20Sopenharmony_ci req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; 11428c2ecf20Sopenharmony_ci else if (chip->ecc.strength) 11438c2ecf20Sopenharmony_ci req.ecc.strength = chip->ecc.strength; 11448c2ecf20Sopenharmony_ci else if (requirements->strength) 11458c2ecf20Sopenharmony_ci req.ecc.strength = requirements->strength; 11468c2ecf20Sopenharmony_ci else 11478c2ecf20Sopenharmony_ci req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (chip->ecc.size) 11508c2ecf20Sopenharmony_ci req.ecc.sectorsize = chip->ecc.size; 11518c2ecf20Sopenharmony_ci else if (requirements->step_size) 11528c2ecf20Sopenharmony_ci req.ecc.sectorsize = requirements->step_size; 11538c2ecf20Sopenharmony_ci else 11548c2ecf20Sopenharmony_ci req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci req.pagesize = mtd->writesize; 11578c2ecf20Sopenharmony_ci req.oobsize = mtd->oobsize; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (mtd->writesize <= 512) { 11608c2ecf20Sopenharmony_ci req.ecc.bytes = 4; 11618c2ecf20Sopenharmony_ci req.ecc.ooboffset = 0; 11628c2ecf20Sopenharmony_ci } else { 11638c2ecf20Sopenharmony_ci req.ecc.bytes = mtd->oobsize - 2; 11648c2ecf20Sopenharmony_ci req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req); 11688c2ecf20Sopenharmony_ci if (IS_ERR(nand->pmecc)) 11698c2ecf20Sopenharmony_ci return PTR_ERR(nand->pmecc); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_BCH; 11728c2ecf20Sopenharmony_ci chip->ecc.size = req.ecc.sectorsize; 11738c2ecf20Sopenharmony_ci chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors; 11748c2ecf20Sopenharmony_ci chip->ecc.strength = req.ecc.strength; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci chip->options |= NAND_NO_SUBPAGE_WRITE; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci return 0; 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic int atmel_nand_ecc_init(struct nand_chip *chip) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 11868c2ecf20Sopenharmony_ci int ret; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci nc = to_nand_controller(chip->controller); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci switch (chip->ecc.engine_type) { 11918c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 11928c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * Nothing to do, the core will initialize everything for us. 11958c2ecf20Sopenharmony_ci */ 11968c2ecf20Sopenharmony_ci break; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 11998c2ecf20Sopenharmony_ci ret = atmel_nand_pmecc_init(chip); 12008c2ecf20Sopenharmony_ci if (ret) 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci chip->ecc.read_page = atmel_nand_pmecc_read_page; 12048c2ecf20Sopenharmony_ci chip->ecc.write_page = atmel_nand_pmecc_write_page; 12058c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw; 12068c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw; 12078c2ecf20Sopenharmony_ci break; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci default: 12108c2ecf20Sopenharmony_ci /* Other modes are not supported. */ 12118c2ecf20Sopenharmony_ci dev_err(nc->dev, "Unsupported ECC mode: %d\n", 12128c2ecf20Sopenharmony_ci chip->ecc.engine_type); 12138c2ecf20Sopenharmony_ci return -ENOTSUPP; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci return 0; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_ecc_init(struct nand_chip *chip) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci int ret; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci ret = atmel_nand_ecc_init(chip); 12248c2ecf20Sopenharmony_ci if (ret) 12258c2ecf20Sopenharmony_ci return ret; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 12288c2ecf20Sopenharmony_ci return 0; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* Adjust the ECC operations for the HSMC IP. */ 12318c2ecf20Sopenharmony_ci chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page; 12328c2ecf20Sopenharmony_ci chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; 12338c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; 12348c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci return 0; 12378c2ecf20Sopenharmony_ci} 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_cistatic int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, 12408c2ecf20Sopenharmony_ci const struct nand_interface_config *conf, 12418c2ecf20Sopenharmony_ci struct atmel_smc_cs_conf *smcconf) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci u32 ncycles, totalcycles, timeps, mckperiodps; 12448c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 12458c2ecf20Sopenharmony_ci int ret; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* DDR interface not supported. */ 12508c2ecf20Sopenharmony_ci if (!nand_interface_is_sdr(conf)) 12518c2ecf20Sopenharmony_ci return -ENOTSUPP; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* 12548c2ecf20Sopenharmony_ci * tRC < 30ns implies EDO mode. This controller does not support this 12558c2ecf20Sopenharmony_ci * mode. 12568c2ecf20Sopenharmony_ci */ 12578c2ecf20Sopenharmony_ci if (conf->timings.sdr.tRC_min < 30000) 12588c2ecf20Sopenharmony_ci return -ENOTSUPP; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci atmel_smc_cs_conf_init(smcconf); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck); 12638c2ecf20Sopenharmony_ci mckperiodps *= 1000; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* 12668c2ecf20Sopenharmony_ci * Set write pulse timing. This one is easy to extract: 12678c2ecf20Sopenharmony_ci * 12688c2ecf20Sopenharmony_ci * NWE_PULSE = tWP 12698c2ecf20Sopenharmony_ci */ 12708c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps); 12718c2ecf20Sopenharmony_ci totalcycles = ncycles; 12728c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT, 12738c2ecf20Sopenharmony_ci ncycles); 12748c2ecf20Sopenharmony_ci if (ret) 12758c2ecf20Sopenharmony_ci return ret; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* 12788c2ecf20Sopenharmony_ci * The write setup timing depends on the operation done on the NAND. 12798c2ecf20Sopenharmony_ci * All operations goes through the same data bus, but the operation 12808c2ecf20Sopenharmony_ci * type depends on the address we are writing to (ALE/CLE address 12818c2ecf20Sopenharmony_ci * lines). 12828c2ecf20Sopenharmony_ci * Since we have no way to differentiate the different operations at 12838c2ecf20Sopenharmony_ci * the SMC level, we must consider the worst case (the biggest setup 12848c2ecf20Sopenharmony_ci * time among all operation types): 12858c2ecf20Sopenharmony_ci * 12868c2ecf20Sopenharmony_ci * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_ci timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min, 12898c2ecf20Sopenharmony_ci conf->timings.sdr.tALS_min); 12908c2ecf20Sopenharmony_ci timeps = max(timeps, conf->timings.sdr.tDS_min); 12918c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(timeps, mckperiodps); 12928c2ecf20Sopenharmony_ci ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0; 12938c2ecf20Sopenharmony_ci totalcycles += ncycles; 12948c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT, 12958c2ecf20Sopenharmony_ci ncycles); 12968c2ecf20Sopenharmony_ci if (ret) 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci /* 13008c2ecf20Sopenharmony_ci * As for the write setup timing, the write hold timing depends on the 13018c2ecf20Sopenharmony_ci * operation done on the NAND: 13028c2ecf20Sopenharmony_ci * 13038c2ecf20Sopenharmony_ci * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH) 13048c2ecf20Sopenharmony_ci */ 13058c2ecf20Sopenharmony_ci timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min, 13068c2ecf20Sopenharmony_ci conf->timings.sdr.tALH_min); 13078c2ecf20Sopenharmony_ci timeps = max3(timeps, conf->timings.sdr.tDH_min, 13088c2ecf20Sopenharmony_ci conf->timings.sdr.tWH_min); 13098c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(timeps, mckperiodps); 13108c2ecf20Sopenharmony_ci totalcycles += ncycles; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci /* 13138c2ecf20Sopenharmony_ci * The write cycle timing is directly matching tWC, but is also 13148c2ecf20Sopenharmony_ci * dependent on the other timings on the setup and hold timings we 13158c2ecf20Sopenharmony_ci * calculated earlier, which gives: 13168c2ecf20Sopenharmony_ci * 13178c2ecf20Sopenharmony_ci * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD) 13188c2ecf20Sopenharmony_ci */ 13198c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps); 13208c2ecf20Sopenharmony_ci ncycles = max(totalcycles, ncycles); 13218c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT, 13228c2ecf20Sopenharmony_ci ncycles); 13238c2ecf20Sopenharmony_ci if (ret) 13248c2ecf20Sopenharmony_ci return ret; 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci /* 13278c2ecf20Sopenharmony_ci * We don't want the CS line to be toggled between each byte/word 13288c2ecf20Sopenharmony_ci * transfer to the NAND. The only way to guarantee that is to have the 13298c2ecf20Sopenharmony_ci * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: 13308c2ecf20Sopenharmony_ci * 13318c2ecf20Sopenharmony_ci * NCS_WR_PULSE = NWE_CYCLE 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT, 13348c2ecf20Sopenharmony_ci ncycles); 13358c2ecf20Sopenharmony_ci if (ret) 13368c2ecf20Sopenharmony_ci return ret; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci /* 13398c2ecf20Sopenharmony_ci * As for the write setup timing, the read hold timing depends on the 13408c2ecf20Sopenharmony_ci * operation done on the NAND: 13418c2ecf20Sopenharmony_ci * 13428c2ecf20Sopenharmony_ci * NRD_HOLD = max(tREH, tRHOH) 13438c2ecf20Sopenharmony_ci */ 13448c2ecf20Sopenharmony_ci timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min); 13458c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(timeps, mckperiodps); 13468c2ecf20Sopenharmony_ci totalcycles = ncycles; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci /* 13498c2ecf20Sopenharmony_ci * TDF = tRHZ - NRD_HOLD 13508c2ecf20Sopenharmony_ci */ 13518c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps); 13528c2ecf20Sopenharmony_ci ncycles -= totalcycles; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* 13558c2ecf20Sopenharmony_ci * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and 13568c2ecf20Sopenharmony_ci * we might end up with a config that does not fit in the TDF field. 13578c2ecf20Sopenharmony_ci * Just take the max value in this case and hope that the NAND is more 13588c2ecf20Sopenharmony_ci * tolerant than advertised. 13598c2ecf20Sopenharmony_ci */ 13608c2ecf20Sopenharmony_ci if (ncycles > ATMEL_SMC_MODE_TDF_MAX) 13618c2ecf20Sopenharmony_ci ncycles = ATMEL_SMC_MODE_TDF_MAX; 13628c2ecf20Sopenharmony_ci else if (ncycles < ATMEL_SMC_MODE_TDF_MIN) 13638c2ecf20Sopenharmony_ci ncycles = ATMEL_SMC_MODE_TDF_MIN; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) | 13668c2ecf20Sopenharmony_ci ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci /* 13698c2ecf20Sopenharmony_ci * Read pulse timing directly matches tRP: 13708c2ecf20Sopenharmony_ci * 13718c2ecf20Sopenharmony_ci * NRD_PULSE = tRP 13728c2ecf20Sopenharmony_ci */ 13738c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); 13748c2ecf20Sopenharmony_ci totalcycles += ncycles; 13758c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, 13768c2ecf20Sopenharmony_ci ncycles); 13778c2ecf20Sopenharmony_ci if (ret) 13788c2ecf20Sopenharmony_ci return ret; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* 13818c2ecf20Sopenharmony_ci * The write cycle timing is directly matching tWC, but is also 13828c2ecf20Sopenharmony_ci * dependent on the setup and hold timings we calculated earlier, 13838c2ecf20Sopenharmony_ci * which gives: 13848c2ecf20Sopenharmony_ci * 13858c2ecf20Sopenharmony_ci * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD) 13868c2ecf20Sopenharmony_ci * 13878c2ecf20Sopenharmony_ci * NRD_SETUP is always 0. 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps); 13908c2ecf20Sopenharmony_ci ncycles = max(totalcycles, ncycles); 13918c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT, 13928c2ecf20Sopenharmony_ci ncycles); 13938c2ecf20Sopenharmony_ci if (ret) 13948c2ecf20Sopenharmony_ci return ret; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci /* 13978c2ecf20Sopenharmony_ci * We don't want the CS line to be toggled between each byte/word 13988c2ecf20Sopenharmony_ci * transfer from the NAND. The only way to guarantee that is to have 13998c2ecf20Sopenharmony_ci * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: 14008c2ecf20Sopenharmony_ci * 14018c2ecf20Sopenharmony_ci * NCS_RD_PULSE = NRD_CYCLE 14028c2ecf20Sopenharmony_ci */ 14038c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT, 14048c2ecf20Sopenharmony_ci ncycles); 14058c2ecf20Sopenharmony_ci if (ret) 14068c2ecf20Sopenharmony_ci return ret; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci /* Txxx timings are directly matching tXXX ones. */ 14098c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps); 14108c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 14118c2ecf20Sopenharmony_ci ATMEL_HSMC_TIMINGS_TCLR_SHIFT, 14128c2ecf20Sopenharmony_ci ncycles); 14138c2ecf20Sopenharmony_ci if (ret) 14148c2ecf20Sopenharmony_ci return ret; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps); 14178c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 14188c2ecf20Sopenharmony_ci ATMEL_HSMC_TIMINGS_TADL_SHIFT, 14198c2ecf20Sopenharmony_ci ncycles); 14208c2ecf20Sopenharmony_ci /* 14218c2ecf20Sopenharmony_ci * Version 4 of the ONFI spec mandates that tADL be at least 400 14228c2ecf20Sopenharmony_ci * nanoseconds, but, depending on the master clock rate, 400 ns may not 14238c2ecf20Sopenharmony_ci * fit in the tADL field of the SMC reg. We need to relax the check and 14248c2ecf20Sopenharmony_ci * accept the -ERANGE return code. 14258c2ecf20Sopenharmony_ci * 14268c2ecf20Sopenharmony_ci * Note that previous versions of the ONFI spec had a lower tADL_min 14278c2ecf20Sopenharmony_ci * (100 or 200 ns). It's not clear why this timing constraint got 14288c2ecf20Sopenharmony_ci * increased but it seems most NANDs are fine with values lower than 14298c2ecf20Sopenharmony_ci * 400ns, so we should be safe. 14308c2ecf20Sopenharmony_ci */ 14318c2ecf20Sopenharmony_ci if (ret && ret != -ERANGE) 14328c2ecf20Sopenharmony_ci return ret; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); 14358c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 14368c2ecf20Sopenharmony_ci ATMEL_HSMC_TIMINGS_TAR_SHIFT, 14378c2ecf20Sopenharmony_ci ncycles); 14388c2ecf20Sopenharmony_ci if (ret) 14398c2ecf20Sopenharmony_ci return ret; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps); 14428c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 14438c2ecf20Sopenharmony_ci ATMEL_HSMC_TIMINGS_TRR_SHIFT, 14448c2ecf20Sopenharmony_ci ncycles); 14458c2ecf20Sopenharmony_ci if (ret) 14468c2ecf20Sopenharmony_ci return ret; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps); 14498c2ecf20Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 14508c2ecf20Sopenharmony_ci ATMEL_HSMC_TIMINGS_TWB_SHIFT, 14518c2ecf20Sopenharmony_ci ncycles); 14528c2ecf20Sopenharmony_ci if (ret) 14538c2ecf20Sopenharmony_ci return ret; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci /* Attach the CS line to the NFC logic. */ 14568c2ecf20Sopenharmony_ci smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* Set the appropriate data bus width. */ 14598c2ecf20Sopenharmony_ci if (nand->base.options & NAND_BUSWIDTH_16) 14608c2ecf20Sopenharmony_ci smcconf->mode |= ATMEL_SMC_MODE_DBW_16; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* Operate in NRD/NWE READ/WRITEMODE. */ 14638c2ecf20Sopenharmony_ci smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD | 14648c2ecf20Sopenharmony_ci ATMEL_SMC_MODE_WRITEMODE_NWE; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return 0; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic int atmel_smc_nand_setup_interface(struct atmel_nand *nand, 14708c2ecf20Sopenharmony_ci int csline, 14718c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 14748c2ecf20Sopenharmony_ci struct atmel_smc_cs_conf smcconf; 14758c2ecf20Sopenharmony_ci struct atmel_nand_cs *cs; 14768c2ecf20Sopenharmony_ci int ret; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); 14818c2ecf20Sopenharmony_ci if (ret) 14828c2ecf20Sopenharmony_ci return ret; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 14858c2ecf20Sopenharmony_ci return 0; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci cs = &nand->cs[csline]; 14888c2ecf20Sopenharmony_ci cs->smcconf = smcconf; 14898c2ecf20Sopenharmony_ci atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci return 0; 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand, 14958c2ecf20Sopenharmony_ci int csline, 14968c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 14998c2ecf20Sopenharmony_ci struct atmel_smc_cs_conf smcconf; 15008c2ecf20Sopenharmony_ci struct atmel_nand_cs *cs; 15018c2ecf20Sopenharmony_ci int ret; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci nc = to_hsmc_nand_controller(nand->base.controller); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); 15068c2ecf20Sopenharmony_ci if (ret) 15078c2ecf20Sopenharmony_ci return ret; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 15108c2ecf20Sopenharmony_ci return 0; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci cs = &nand->cs[csline]; 15138c2ecf20Sopenharmony_ci cs->smcconf = smcconf; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (cs->rb.type == ATMEL_NAND_NATIVE_RB) 15168c2ecf20Sopenharmony_ci cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id, 15198c2ecf20Sopenharmony_ci &cs->smcconf); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci return 0; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic int atmel_nand_setup_interface(struct nand_chip *chip, int csline, 15258c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 15268c2ecf20Sopenharmony_ci{ 15278c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 15288c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci if (csline >= nand->numcs || 15338c2ecf20Sopenharmony_ci (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) 15348c2ecf20Sopenharmony_ci return -EINVAL; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci return nc->caps->ops->setup_interface(nand, csline, conf); 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_cistatic int atmel_nand_exec_op(struct nand_chip *chip, 15408c2ecf20Sopenharmony_ci const struct nand_operation *op, 15418c2ecf20Sopenharmony_ci bool check_only) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 15448c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci return nc->caps->ops->exec_op(nand, op, check_only); 15498c2ecf20Sopenharmony_ci} 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_cistatic void atmel_nand_init(struct atmel_nand_controller *nc, 15528c2ecf20Sopenharmony_ci struct atmel_nand *nand) 15538c2ecf20Sopenharmony_ci{ 15548c2ecf20Sopenharmony_ci struct nand_chip *chip = &nand->base; 15558c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci mtd->dev.parent = nc->dev; 15588c2ecf20Sopenharmony_ci nand->base.controller = &nc->base; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (!nc->mck || !nc->caps->ops->setup_interface) 15618c2ecf20Sopenharmony_ci chip->options |= NAND_KEEP_TIMINGS; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci /* 15648c2ecf20Sopenharmony_ci * Use a bounce buffer when the buffer passed by the MTD user is not 15658c2ecf20Sopenharmony_ci * suitable for DMA. 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci if (nc->dmac) 15688c2ecf20Sopenharmony_ci chip->options |= NAND_USES_DMA; 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci /* Default to HW ECC if pmecc is available. */ 15718c2ecf20Sopenharmony_ci if (nc->pmecc) 15728c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cistatic void atmel_smc_nand_init(struct atmel_nand_controller *nc, 15768c2ecf20Sopenharmony_ci struct atmel_nand *nand) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct nand_chip *chip = &nand->base; 15798c2ecf20Sopenharmony_ci struct atmel_smc_nand_controller *smc_nc; 15808c2ecf20Sopenharmony_ci int i; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci atmel_nand_init(nc, nand); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci smc_nc = to_smc_nand_controller(chip->controller); 15858c2ecf20Sopenharmony_ci if (!smc_nc->ebi_csa_regmap) 15868c2ecf20Sopenharmony_ci return; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* Attach the CS to the NAND Flash logic. */ 15898c2ecf20Sopenharmony_ci for (i = 0; i < nand->numcs; i++) 15908c2ecf20Sopenharmony_ci regmap_update_bits(smc_nc->ebi_csa_regmap, 15918c2ecf20Sopenharmony_ci smc_nc->ebi_csa->offs, 15928c2ecf20Sopenharmony_ci BIT(nand->cs[i].id), BIT(nand->cs[i].id)); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (smc_nc->ebi_csa->nfd0_on_d16) 15958c2ecf20Sopenharmony_ci regmap_update_bits(smc_nc->ebi_csa_regmap, 15968c2ecf20Sopenharmony_ci smc_nc->ebi_csa->offs, 15978c2ecf20Sopenharmony_ci smc_nc->ebi_csa->nfd0_on_d16, 15988c2ecf20Sopenharmony_ci smc_nc->ebi_csa->nfd0_on_d16); 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic int atmel_nand_controller_remove_nand(struct atmel_nand *nand) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci struct nand_chip *chip = &nand->base; 16048c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 16058c2ecf20Sopenharmony_ci int ret; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci ret = mtd_device_unregister(mtd); 16088c2ecf20Sopenharmony_ci if (ret) 16098c2ecf20Sopenharmony_ci return ret; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci nand_cleanup(chip); 16128c2ecf20Sopenharmony_ci list_del(&nand->node); 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci return 0; 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cistatic struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, 16188c2ecf20Sopenharmony_ci struct device_node *np, 16198c2ecf20Sopenharmony_ci int reg_cells) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci struct atmel_nand *nand; 16228c2ecf20Sopenharmony_ci struct gpio_desc *gpio; 16238c2ecf20Sopenharmony_ci int numcs, ret, i; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci numcs = of_property_count_elems_of_size(np, "reg", 16268c2ecf20Sopenharmony_ci reg_cells * sizeof(u32)); 16278c2ecf20Sopenharmony_ci if (numcs < 1) { 16288c2ecf20Sopenharmony_ci dev_err(nc->dev, "Missing or invalid reg property\n"); 16298c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci nand = devm_kzalloc(nc->dev, struct_size(nand, cs, numcs), GFP_KERNEL); 16338c2ecf20Sopenharmony_ci if (!nand) { 16348c2ecf20Sopenharmony_ci dev_err(nc->dev, "Failed to allocate NAND object\n"); 16358c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci nand->numcs = numcs; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci gpio = devm_fwnode_gpiod_get(nc->dev, of_fwnode_handle(np), 16418c2ecf20Sopenharmony_ci "det", GPIOD_IN, "nand-det"); 16428c2ecf20Sopenharmony_ci if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { 16438c2ecf20Sopenharmony_ci dev_err(nc->dev, 16448c2ecf20Sopenharmony_ci "Failed to get detect gpio (err = %ld)\n", 16458c2ecf20Sopenharmony_ci PTR_ERR(gpio)); 16468c2ecf20Sopenharmony_ci return ERR_CAST(gpio); 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (!IS_ERR(gpio)) 16508c2ecf20Sopenharmony_ci nand->cdgpio = gpio; 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci for (i = 0; i < numcs; i++) { 16538c2ecf20Sopenharmony_ci struct resource res; 16548c2ecf20Sopenharmony_ci u32 val; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 16578c2ecf20Sopenharmony_ci if (ret) { 16588c2ecf20Sopenharmony_ci dev_err(nc->dev, "Invalid reg property (err = %d)\n", 16598c2ecf20Sopenharmony_ci ret); 16608c2ecf20Sopenharmony_ci return ERR_PTR(ret); 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, "reg", i * reg_cells, 16648c2ecf20Sopenharmony_ci &val); 16658c2ecf20Sopenharmony_ci if (ret) { 16668c2ecf20Sopenharmony_ci dev_err(nc->dev, "Invalid reg property (err = %d)\n", 16678c2ecf20Sopenharmony_ci ret); 16688c2ecf20Sopenharmony_ci return ERR_PTR(ret); 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci nand->cs[i].id = val; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci nand->cs[i].io.dma = res.start; 16748c2ecf20Sopenharmony_ci nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res); 16758c2ecf20Sopenharmony_ci if (IS_ERR(nand->cs[i].io.virt)) 16768c2ecf20Sopenharmony_ci return ERR_CAST(nand->cs[i].io.virt); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "atmel,rb", &val)) { 16798c2ecf20Sopenharmony_ci if (val > ATMEL_NFC_MAX_RB_ID) 16808c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB; 16838c2ecf20Sopenharmony_ci nand->cs[i].rb.id = val; 16848c2ecf20Sopenharmony_ci } else { 16858c2ecf20Sopenharmony_ci gpio = devm_fwnode_gpiod_get_index(nc->dev, 16868c2ecf20Sopenharmony_ci of_fwnode_handle(np), 16878c2ecf20Sopenharmony_ci "rb", i, GPIOD_IN, 16888c2ecf20Sopenharmony_ci "nand-rb"); 16898c2ecf20Sopenharmony_ci if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { 16908c2ecf20Sopenharmony_ci dev_err(nc->dev, 16918c2ecf20Sopenharmony_ci "Failed to get R/B gpio (err = %ld)\n", 16928c2ecf20Sopenharmony_ci PTR_ERR(gpio)); 16938c2ecf20Sopenharmony_ci return ERR_CAST(gpio); 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci if (!IS_ERR(gpio)) { 16978c2ecf20Sopenharmony_ci nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB; 16988c2ecf20Sopenharmony_ci nand->cs[i].rb.gpio = gpio; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci gpio = devm_fwnode_gpiod_get_index(nc->dev, 17038c2ecf20Sopenharmony_ci of_fwnode_handle(np), 17048c2ecf20Sopenharmony_ci "cs", i, GPIOD_OUT_HIGH, 17058c2ecf20Sopenharmony_ci "nand-cs"); 17068c2ecf20Sopenharmony_ci if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { 17078c2ecf20Sopenharmony_ci dev_err(nc->dev, 17088c2ecf20Sopenharmony_ci "Failed to get CS gpio (err = %ld)\n", 17098c2ecf20Sopenharmony_ci PTR_ERR(gpio)); 17108c2ecf20Sopenharmony_ci return ERR_CAST(gpio); 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (!IS_ERR(gpio)) 17148c2ecf20Sopenharmony_ci nand->cs[i].csgpio = gpio; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci nand_set_flash_node(&nand->base, np); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci return nand; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_cistatic int 17238c2ecf20Sopenharmony_ciatmel_nand_controller_add_nand(struct atmel_nand_controller *nc, 17248c2ecf20Sopenharmony_ci struct atmel_nand *nand) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct nand_chip *chip = &nand->base; 17278c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 17288c2ecf20Sopenharmony_ci int ret; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci /* No card inserted, skip this NAND. */ 17318c2ecf20Sopenharmony_ci if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) { 17328c2ecf20Sopenharmony_ci dev_info(nc->dev, "No SmartMedia card inserted.\n"); 17338c2ecf20Sopenharmony_ci return 0; 17348c2ecf20Sopenharmony_ci } 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci nc->caps->ops->nand_init(nc, nand); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci ret = nand_scan(chip, nand->numcs); 17398c2ecf20Sopenharmony_ci if (ret) { 17408c2ecf20Sopenharmony_ci dev_err(nc->dev, "NAND scan failed: %d\n", ret); 17418c2ecf20Sopenharmony_ci return ret; 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 17458c2ecf20Sopenharmony_ci if (ret) { 17468c2ecf20Sopenharmony_ci dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); 17478c2ecf20Sopenharmony_ci nand_cleanup(chip); 17488c2ecf20Sopenharmony_ci return ret; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci list_add_tail(&nand->node, &nc->chips); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci return 0; 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_cistatic int 17578c2ecf20Sopenharmony_ciatmel_nand_controller_remove_nands(struct atmel_nand_controller *nc) 17588c2ecf20Sopenharmony_ci{ 17598c2ecf20Sopenharmony_ci struct atmel_nand *nand, *tmp; 17608c2ecf20Sopenharmony_ci int ret; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci list_for_each_entry_safe(nand, tmp, &nc->chips, node) { 17638c2ecf20Sopenharmony_ci ret = atmel_nand_controller_remove_nand(nand); 17648c2ecf20Sopenharmony_ci if (ret) 17658c2ecf20Sopenharmony_ci return ret; 17668c2ecf20Sopenharmony_ci } 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci return 0; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_cistatic int 17728c2ecf20Sopenharmony_ciatmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci struct device *dev = nc->dev; 17758c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 17768c2ecf20Sopenharmony_ci struct atmel_nand *nand; 17778c2ecf20Sopenharmony_ci struct gpio_desc *gpio; 17788c2ecf20Sopenharmony_ci struct resource *res; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci /* 17818c2ecf20Sopenharmony_ci * Legacy bindings only allow connecting a single NAND with a unique CS 17828c2ecf20Sopenharmony_ci * line to the controller. 17838c2ecf20Sopenharmony_ci */ 17848c2ecf20Sopenharmony_ci nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs), 17858c2ecf20Sopenharmony_ci GFP_KERNEL); 17868c2ecf20Sopenharmony_ci if (!nand) 17878c2ecf20Sopenharmony_ci return -ENOMEM; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci nand->numcs = 1; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 17928c2ecf20Sopenharmony_ci nand->cs[0].io.virt = devm_ioremap_resource(dev, res); 17938c2ecf20Sopenharmony_ci if (IS_ERR(nand->cs[0].io.virt)) 17948c2ecf20Sopenharmony_ci return PTR_ERR(nand->cs[0].io.virt); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci nand->cs[0].io.dma = res->start; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci /* 17998c2ecf20Sopenharmony_ci * The old driver was hardcoding the CS id to 3 for all sama5 18008c2ecf20Sopenharmony_ci * controllers. Since this id is only meaningful for the sama5 18018c2ecf20Sopenharmony_ci * controller we can safely assign this id to 3 no matter the 18028c2ecf20Sopenharmony_ci * controller. 18038c2ecf20Sopenharmony_ci * If one wants to connect a NAND to a different CS line, he will 18048c2ecf20Sopenharmony_ci * have to use the new bindings. 18058c2ecf20Sopenharmony_ci */ 18068c2ecf20Sopenharmony_ci nand->cs[0].id = 3; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci /* R/B GPIO. */ 18098c2ecf20Sopenharmony_ci gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN); 18108c2ecf20Sopenharmony_ci if (IS_ERR(gpio)) { 18118c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get R/B gpio (err = %ld)\n", 18128c2ecf20Sopenharmony_ci PTR_ERR(gpio)); 18138c2ecf20Sopenharmony_ci return PTR_ERR(gpio); 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci if (gpio) { 18178c2ecf20Sopenharmony_ci nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB; 18188c2ecf20Sopenharmony_ci nand->cs[0].rb.gpio = gpio; 18198c2ecf20Sopenharmony_ci } 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci /* CS GPIO. */ 18228c2ecf20Sopenharmony_ci gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH); 18238c2ecf20Sopenharmony_ci if (IS_ERR(gpio)) { 18248c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get CS gpio (err = %ld)\n", 18258c2ecf20Sopenharmony_ci PTR_ERR(gpio)); 18268c2ecf20Sopenharmony_ci return PTR_ERR(gpio); 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci nand->cs[0].csgpio = gpio; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci /* Card detect GPIO. */ 18328c2ecf20Sopenharmony_ci gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN); 18338c2ecf20Sopenharmony_ci if (IS_ERR(gpio)) { 18348c2ecf20Sopenharmony_ci dev_err(dev, 18358c2ecf20Sopenharmony_ci "Failed to get detect gpio (err = %ld)\n", 18368c2ecf20Sopenharmony_ci PTR_ERR(gpio)); 18378c2ecf20Sopenharmony_ci return PTR_ERR(gpio); 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci nand->cdgpio = gpio; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci nand_set_flash_node(&nand->base, nc->dev->of_node); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci return atmel_nand_controller_add_nand(nc, nand); 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_cistatic int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct device_node *np, *nand_np; 18508c2ecf20Sopenharmony_ci struct device *dev = nc->dev; 18518c2ecf20Sopenharmony_ci int ret, reg_cells; 18528c2ecf20Sopenharmony_ci u32 val; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci /* We do not retrieve the SMC syscon when parsing old DTs. */ 18558c2ecf20Sopenharmony_ci if (nc->caps->legacy_of_bindings) 18568c2ecf20Sopenharmony_ci return atmel_nand_controller_legacy_add_nands(nc); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci np = dev->of_node; 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "#address-cells", &val); 18618c2ecf20Sopenharmony_ci if (ret) { 18628c2ecf20Sopenharmony_ci dev_err(dev, "missing #address-cells property\n"); 18638c2ecf20Sopenharmony_ci return ret; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_ci reg_cells = val; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "#size-cells", &val); 18698c2ecf20Sopenharmony_ci if (ret) { 18708c2ecf20Sopenharmony_ci dev_err(dev, "missing #size-cells property\n"); 18718c2ecf20Sopenharmony_ci return ret; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci reg_cells += val; 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci for_each_child_of_node(np, nand_np) { 18778c2ecf20Sopenharmony_ci struct atmel_nand *nand; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci nand = atmel_nand_create(nc, nand_np, reg_cells); 18808c2ecf20Sopenharmony_ci if (IS_ERR(nand)) { 18818c2ecf20Sopenharmony_ci ret = PTR_ERR(nand); 18828c2ecf20Sopenharmony_ci goto err; 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci ret = atmel_nand_controller_add_nand(nc, nand); 18868c2ecf20Sopenharmony_ci if (ret) 18878c2ecf20Sopenharmony_ci goto err; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci return 0; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_cierr: 18938c2ecf20Sopenharmony_ci atmel_nand_controller_remove_nands(nc); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci return ret; 18968c2ecf20Sopenharmony_ci} 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_cistatic void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci if (nc->dmac) 19018c2ecf20Sopenharmony_ci dma_release_channel(nc->dmac); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci clk_put(nc->mck); 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9260_ebi_csa = { 19078c2ecf20Sopenharmony_ci .offs = AT91SAM9260_MATRIX_EBICSA, 19088c2ecf20Sopenharmony_ci}; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9261_ebi_csa = { 19118c2ecf20Sopenharmony_ci .offs = AT91SAM9261_MATRIX_EBICSA, 19128c2ecf20Sopenharmony_ci}; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9263_ebi_csa = { 19158c2ecf20Sopenharmony_ci .offs = AT91SAM9263_MATRIX_EBI0CSA, 19168c2ecf20Sopenharmony_ci}; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9rl_ebi_csa = { 19198c2ecf20Sopenharmony_ci .offs = AT91SAM9RL_MATRIX_EBICSA, 19208c2ecf20Sopenharmony_ci}; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9g45_ebi_csa = { 19238c2ecf20Sopenharmony_ci .offs = AT91SAM9G45_MATRIX_EBICSA, 19248c2ecf20Sopenharmony_ci}; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9n12_ebi_csa = { 19278c2ecf20Sopenharmony_ci .offs = AT91SAM9N12_MATRIX_EBICSA, 19288c2ecf20Sopenharmony_ci}; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9x5_ebi_csa = { 19318c2ecf20Sopenharmony_ci .offs = AT91SAM9X5_MATRIX_EBICSA, 19328c2ecf20Sopenharmony_ci}; 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = { 19358c2ecf20Sopenharmony_ci .offs = AT91_SFR_CCFG_EBICSA, 19368c2ecf20Sopenharmony_ci .nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16, 19378c2ecf20Sopenharmony_ci}; 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_ebi_csa_regmap_of_ids[] = { 19408c2ecf20Sopenharmony_ci { 19418c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9260-matrix", 19428c2ecf20Sopenharmony_ci .data = &at91sam9260_ebi_csa, 19438c2ecf20Sopenharmony_ci }, 19448c2ecf20Sopenharmony_ci { 19458c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9261-matrix", 19468c2ecf20Sopenharmony_ci .data = &at91sam9261_ebi_csa, 19478c2ecf20Sopenharmony_ci }, 19488c2ecf20Sopenharmony_ci { 19498c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9263-matrix", 19508c2ecf20Sopenharmony_ci .data = &at91sam9263_ebi_csa, 19518c2ecf20Sopenharmony_ci }, 19528c2ecf20Sopenharmony_ci { 19538c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9rl-matrix", 19548c2ecf20Sopenharmony_ci .data = &at91sam9rl_ebi_csa, 19558c2ecf20Sopenharmony_ci }, 19568c2ecf20Sopenharmony_ci { 19578c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9g45-matrix", 19588c2ecf20Sopenharmony_ci .data = &at91sam9g45_ebi_csa, 19598c2ecf20Sopenharmony_ci }, 19608c2ecf20Sopenharmony_ci { 19618c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9n12-matrix", 19628c2ecf20Sopenharmony_ci .data = &at91sam9n12_ebi_csa, 19638c2ecf20Sopenharmony_ci }, 19648c2ecf20Sopenharmony_ci { 19658c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9x5-matrix", 19668c2ecf20Sopenharmony_ci .data = &at91sam9x5_ebi_csa, 19678c2ecf20Sopenharmony_ci }, 19688c2ecf20Sopenharmony_ci { 19698c2ecf20Sopenharmony_ci .compatible = "microchip,sam9x60-sfr", 19708c2ecf20Sopenharmony_ci .data = &sam9x60_ebi_csa, 19718c2ecf20Sopenharmony_ci }, 19728c2ecf20Sopenharmony_ci { /* sentinel */ }, 19738c2ecf20Sopenharmony_ci}; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_cistatic int atmel_nand_attach_chip(struct nand_chip *chip) 19768c2ecf20Sopenharmony_ci{ 19778c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc = to_nand_controller(chip->controller); 19788c2ecf20Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 19798c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 19808c2ecf20Sopenharmony_ci int ret; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci ret = nc->caps->ops->ecc_init(chip); 19838c2ecf20Sopenharmony_ci if (ret) 19848c2ecf20Sopenharmony_ci return ret; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { 19878c2ecf20Sopenharmony_ci /* 19888c2ecf20Sopenharmony_ci * We keep the MTD name unchanged to avoid breaking platforms 19898c2ecf20Sopenharmony_ci * where the MTD cmdline parser is used and the bootloader 19908c2ecf20Sopenharmony_ci * has not been updated to use the new naming scheme. 19918c2ecf20Sopenharmony_ci */ 19928c2ecf20Sopenharmony_ci mtd->name = "atmel_nand"; 19938c2ecf20Sopenharmony_ci } else if (!mtd->name) { 19948c2ecf20Sopenharmony_ci /* 19958c2ecf20Sopenharmony_ci * If the new bindings are used and the bootloader has not been 19968c2ecf20Sopenharmony_ci * updated to pass a new mtdparts parameter on the cmdline, you 19978c2ecf20Sopenharmony_ci * should define the following property in your nand node: 19988c2ecf20Sopenharmony_ci * 19998c2ecf20Sopenharmony_ci * label = "atmel_nand"; 20008c2ecf20Sopenharmony_ci * 20018c2ecf20Sopenharmony_ci * This way, mtd->name will be set by the core when 20028c2ecf20Sopenharmony_ci * nand_set_flash_node() is called. 20038c2ecf20Sopenharmony_ci */ 20048c2ecf20Sopenharmony_ci mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, 20058c2ecf20Sopenharmony_ci "%s:nand.%d", dev_name(nc->dev), 20068c2ecf20Sopenharmony_ci nand->cs[0].id); 20078c2ecf20Sopenharmony_ci if (!mtd->name) { 20088c2ecf20Sopenharmony_ci dev_err(nc->dev, "Failed to allocate mtd->name\n"); 20098c2ecf20Sopenharmony_ci return -ENOMEM; 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci return 0; 20148c2ecf20Sopenharmony_ci} 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_cistatic const struct nand_controller_ops atmel_nand_controller_ops = { 20178c2ecf20Sopenharmony_ci .attach_chip = atmel_nand_attach_chip, 20188c2ecf20Sopenharmony_ci .setup_interface = atmel_nand_setup_interface, 20198c2ecf20Sopenharmony_ci .exec_op = atmel_nand_exec_op, 20208c2ecf20Sopenharmony_ci}; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_cistatic int atmel_nand_controller_init(struct atmel_nand_controller *nc, 20238c2ecf20Sopenharmony_ci struct platform_device *pdev, 20248c2ecf20Sopenharmony_ci const struct atmel_nand_controller_caps *caps) 20258c2ecf20Sopenharmony_ci{ 20268c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 20278c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 20288c2ecf20Sopenharmony_ci int ret; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci nand_controller_init(&nc->base); 20318c2ecf20Sopenharmony_ci nc->base.ops = &atmel_nand_controller_ops; 20328c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&nc->chips); 20338c2ecf20Sopenharmony_ci nc->dev = dev; 20348c2ecf20Sopenharmony_ci nc->caps = caps; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, nc); 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci nc->pmecc = devm_atmel_pmecc_get(dev); 20398c2ecf20Sopenharmony_ci if (IS_ERR(nc->pmecc)) 20408c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(nc->pmecc), 20418c2ecf20Sopenharmony_ci "Could not get PMECC object\n"); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci if (nc->caps->has_dma && !atmel_nand_avoid_dma) { 20448c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci dma_cap_zero(mask); 20478c2ecf20Sopenharmony_ci dma_cap_set(DMA_MEMCPY, mask); 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci nc->dmac = dma_request_channel(mask, NULL, NULL); 20508c2ecf20Sopenharmony_ci if (!nc->dmac) 20518c2ecf20Sopenharmony_ci dev_err(nc->dev, "Failed to request DMA channel\n"); 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci /* We do not retrieve the SMC syscon when parsing old DTs. */ 20558c2ecf20Sopenharmony_ci if (nc->caps->legacy_of_bindings) 20568c2ecf20Sopenharmony_ci return 0; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci nc->mck = of_clk_get(dev->parent->of_node, 0); 20598c2ecf20Sopenharmony_ci if (IS_ERR(nc->mck)) { 20608c2ecf20Sopenharmony_ci dev_err(dev, "Failed to retrieve MCK clk\n"); 20618c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->mck); 20628c2ecf20Sopenharmony_ci goto out_release_dma; 20638c2ecf20Sopenharmony_ci } 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); 20668c2ecf20Sopenharmony_ci if (!np) { 20678c2ecf20Sopenharmony_ci dev_err(dev, "Missing or invalid atmel,smc property\n"); 20688c2ecf20Sopenharmony_ci ret = -EINVAL; 20698c2ecf20Sopenharmony_ci goto out_release_dma; 20708c2ecf20Sopenharmony_ci } 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci nc->smc = syscon_node_to_regmap(np); 20738c2ecf20Sopenharmony_ci of_node_put(np); 20748c2ecf20Sopenharmony_ci if (IS_ERR(nc->smc)) { 20758c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->smc); 20768c2ecf20Sopenharmony_ci dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); 20778c2ecf20Sopenharmony_ci goto out_release_dma; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci return 0; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ciout_release_dma: 20838c2ecf20Sopenharmony_ci if (nc->dmac) 20848c2ecf20Sopenharmony_ci dma_release_channel(nc->dmac); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci return ret; 20878c2ecf20Sopenharmony_ci} 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_cistatic int 20908c2ecf20Sopenharmony_ciatmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) 20918c2ecf20Sopenharmony_ci{ 20928c2ecf20Sopenharmony_ci struct device *dev = nc->base.dev; 20938c2ecf20Sopenharmony_ci const struct of_device_id *match; 20948c2ecf20Sopenharmony_ci struct device_node *np; 20958c2ecf20Sopenharmony_ci int ret; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci /* We do not retrieve the EBICSA regmap when parsing old DTs. */ 20988c2ecf20Sopenharmony_ci if (nc->base.caps->legacy_of_bindings) 20998c2ecf20Sopenharmony_ci return 0; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->parent->of_node, 21028c2ecf20Sopenharmony_ci nc->base.caps->ebi_csa_regmap_name, 0); 21038c2ecf20Sopenharmony_ci if (!np) 21048c2ecf20Sopenharmony_ci return 0; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci match = of_match_node(atmel_ebi_csa_regmap_of_ids, np); 21078c2ecf20Sopenharmony_ci if (!match) { 21088c2ecf20Sopenharmony_ci of_node_put(np); 21098c2ecf20Sopenharmony_ci return 0; 21108c2ecf20Sopenharmony_ci } 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci nc->ebi_csa_regmap = syscon_node_to_regmap(np); 21138c2ecf20Sopenharmony_ci of_node_put(np); 21148c2ecf20Sopenharmony_ci if (IS_ERR(nc->ebi_csa_regmap)) { 21158c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->ebi_csa_regmap); 21168c2ecf20Sopenharmony_ci dev_err(dev, "Could not get EBICSA regmap (err = %d)\n", ret); 21178c2ecf20Sopenharmony_ci return ret; 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci /* 21238c2ecf20Sopenharmony_ci * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 21248c2ecf20Sopenharmony_ci * add 4 to ->ebi_csa->offs. 21258c2ecf20Sopenharmony_ci */ 21268c2ecf20Sopenharmony_ci if (of_device_is_compatible(dev->parent->of_node, 21278c2ecf20Sopenharmony_ci "atmel,at91sam9263-ebi1")) 21288c2ecf20Sopenharmony_ci nc->ebi_csa->offs += 4; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci return 0; 21318c2ecf20Sopenharmony_ci} 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_cistatic int 21348c2ecf20Sopenharmony_ciatmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci struct regmap_config regmap_conf = { 21378c2ecf20Sopenharmony_ci .reg_bits = 32, 21388c2ecf20Sopenharmony_ci .val_bits = 32, 21398c2ecf20Sopenharmony_ci .reg_stride = 4, 21408c2ecf20Sopenharmony_ci }; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci struct device *dev = nc->base.dev; 21438c2ecf20Sopenharmony_ci struct device_node *nand_np, *nfc_np; 21448c2ecf20Sopenharmony_ci void __iomem *iomem; 21458c2ecf20Sopenharmony_ci struct resource res; 21468c2ecf20Sopenharmony_ci int ret; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci nand_np = dev->of_node; 21498c2ecf20Sopenharmony_ci nfc_np = of_get_compatible_child(dev->of_node, "atmel,sama5d3-nfc"); 21508c2ecf20Sopenharmony_ci if (!nfc_np) { 21518c2ecf20Sopenharmony_ci dev_err(dev, "Could not find device node for sama5d3-nfc\n"); 21528c2ecf20Sopenharmony_ci return -ENODEV; 21538c2ecf20Sopenharmony_ci } 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci nc->clk = of_clk_get(nfc_np, 0); 21568c2ecf20Sopenharmony_ci if (IS_ERR(nc->clk)) { 21578c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->clk); 21588c2ecf20Sopenharmony_ci dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n", 21598c2ecf20Sopenharmony_ci ret); 21608c2ecf20Sopenharmony_ci goto out; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci ret = clk_prepare_enable(nc->clk); 21648c2ecf20Sopenharmony_ci if (ret) { 21658c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n", 21668c2ecf20Sopenharmony_ci ret); 21678c2ecf20Sopenharmony_ci goto out; 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci nc->irq = of_irq_get(nand_np, 0); 21718c2ecf20Sopenharmony_ci if (nc->irq <= 0) { 21728c2ecf20Sopenharmony_ci ret = nc->irq ?: -ENXIO; 21738c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 21748c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get IRQ number (err = %d)\n", 21758c2ecf20Sopenharmony_ci ret); 21768c2ecf20Sopenharmony_ci goto out; 21778c2ecf20Sopenharmony_ci } 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci ret = of_address_to_resource(nfc_np, 0, &res); 21808c2ecf20Sopenharmony_ci if (ret) { 21818c2ecf20Sopenharmony_ci dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n", 21828c2ecf20Sopenharmony_ci ret); 21838c2ecf20Sopenharmony_ci goto out; 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci iomem = devm_ioremap_resource(dev, &res); 21878c2ecf20Sopenharmony_ci if (IS_ERR(iomem)) { 21888c2ecf20Sopenharmony_ci ret = PTR_ERR(iomem); 21898c2ecf20Sopenharmony_ci goto out; 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci regmap_conf.name = "nfc-io"; 21938c2ecf20Sopenharmony_ci regmap_conf.max_register = resource_size(&res) - 4; 21948c2ecf20Sopenharmony_ci nc->io = devm_regmap_init_mmio(dev, iomem, ®map_conf); 21958c2ecf20Sopenharmony_ci if (IS_ERR(nc->io)) { 21968c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->io); 21978c2ecf20Sopenharmony_ci dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", 21988c2ecf20Sopenharmony_ci ret); 21998c2ecf20Sopenharmony_ci goto out; 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci ret = of_address_to_resource(nfc_np, 1, &res); 22038c2ecf20Sopenharmony_ci if (ret) { 22048c2ecf20Sopenharmony_ci dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n", 22058c2ecf20Sopenharmony_ci ret); 22068c2ecf20Sopenharmony_ci goto out; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci iomem = devm_ioremap_resource(dev, &res); 22108c2ecf20Sopenharmony_ci if (IS_ERR(iomem)) { 22118c2ecf20Sopenharmony_ci ret = PTR_ERR(iomem); 22128c2ecf20Sopenharmony_ci goto out; 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci regmap_conf.name = "smc"; 22168c2ecf20Sopenharmony_ci regmap_conf.max_register = resource_size(&res) - 4; 22178c2ecf20Sopenharmony_ci nc->base.smc = devm_regmap_init_mmio(dev, iomem, ®map_conf); 22188c2ecf20Sopenharmony_ci if (IS_ERR(nc->base.smc)) { 22198c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->base.smc); 22208c2ecf20Sopenharmony_ci dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", 22218c2ecf20Sopenharmony_ci ret); 22228c2ecf20Sopenharmony_ci goto out; 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_ci ret = of_address_to_resource(nfc_np, 2, &res); 22268c2ecf20Sopenharmony_ci if (ret) { 22278c2ecf20Sopenharmony_ci dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n", 22288c2ecf20Sopenharmony_ci ret); 22298c2ecf20Sopenharmony_ci goto out; 22308c2ecf20Sopenharmony_ci } 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci nc->sram.virt = devm_ioremap_resource(dev, &res); 22338c2ecf20Sopenharmony_ci if (IS_ERR(nc->sram.virt)) { 22348c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->sram.virt); 22358c2ecf20Sopenharmony_ci goto out; 22368c2ecf20Sopenharmony_ci } 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci nc->sram.dma = res.start; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ciout: 22418c2ecf20Sopenharmony_ci of_node_put(nfc_np); 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci return ret; 22448c2ecf20Sopenharmony_ci} 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_cistatic int 22478c2ecf20Sopenharmony_ciatmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) 22488c2ecf20Sopenharmony_ci{ 22498c2ecf20Sopenharmony_ci struct device *dev = nc->base.dev; 22508c2ecf20Sopenharmony_ci struct device_node *np; 22518c2ecf20Sopenharmony_ci int ret; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); 22548c2ecf20Sopenharmony_ci if (!np) { 22558c2ecf20Sopenharmony_ci dev_err(dev, "Missing or invalid atmel,smc property\n"); 22568c2ecf20Sopenharmony_ci return -EINVAL; 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci nc->hsmc_layout = atmel_hsmc_get_reg_layout(np); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci nc->irq = of_irq_get(np, 0); 22628c2ecf20Sopenharmony_ci of_node_put(np); 22638c2ecf20Sopenharmony_ci if (nc->irq <= 0) { 22648c2ecf20Sopenharmony_ci ret = nc->irq ?: -ENXIO; 22658c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 22668c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get IRQ number (err = %d)\n", 22678c2ecf20Sopenharmony_ci ret); 22688c2ecf20Sopenharmony_ci return ret; 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); 22728c2ecf20Sopenharmony_ci if (!np) { 22738c2ecf20Sopenharmony_ci dev_err(dev, "Missing or invalid atmel,nfc-io property\n"); 22748c2ecf20Sopenharmony_ci return -EINVAL; 22758c2ecf20Sopenharmony_ci } 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci nc->io = syscon_node_to_regmap(np); 22788c2ecf20Sopenharmony_ci of_node_put(np); 22798c2ecf20Sopenharmony_ci if (IS_ERR(nc->io)) { 22808c2ecf20Sopenharmony_ci ret = PTR_ERR(nc->io); 22818c2ecf20Sopenharmony_ci dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret); 22828c2ecf20Sopenharmony_ci return ret; 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node, 22868c2ecf20Sopenharmony_ci "atmel,nfc-sram", 0); 22878c2ecf20Sopenharmony_ci if (!nc->sram.pool) { 22888c2ecf20Sopenharmony_ci dev_err(nc->base.dev, "Missing SRAM\n"); 22898c2ecf20Sopenharmony_ci return -ENOMEM; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool, 22938c2ecf20Sopenharmony_ci ATMEL_NFC_SRAM_SIZE, 22948c2ecf20Sopenharmony_ci &nc->sram.dma); 22958c2ecf20Sopenharmony_ci if (!nc->sram.virt) { 22968c2ecf20Sopenharmony_ci dev_err(nc->base.dev, 22978c2ecf20Sopenharmony_ci "Could not allocate memory from the NFC SRAM pool\n"); 22988c2ecf20Sopenharmony_ci return -ENOMEM; 22998c2ecf20Sopenharmony_ci } 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci return 0; 23028c2ecf20Sopenharmony_ci} 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_cistatic int 23058c2ecf20Sopenharmony_ciatmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) 23068c2ecf20Sopenharmony_ci{ 23078c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *hsmc_nc; 23088c2ecf20Sopenharmony_ci int ret; 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci ret = atmel_nand_controller_remove_nands(nc); 23118c2ecf20Sopenharmony_ci if (ret) 23128c2ecf20Sopenharmony_ci return ret; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base); 23158c2ecf20Sopenharmony_ci regmap_write(hsmc_nc->base.smc, ATMEL_HSMC_NFC_CTRL, 23168c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CTRL_DIS); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci if (hsmc_nc->sram.pool) 23198c2ecf20Sopenharmony_ci gen_pool_free(hsmc_nc->sram.pool, 23208c2ecf20Sopenharmony_ci (unsigned long)hsmc_nc->sram.virt, 23218c2ecf20Sopenharmony_ci ATMEL_NFC_SRAM_SIZE); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci if (hsmc_nc->clk) { 23248c2ecf20Sopenharmony_ci clk_disable_unprepare(hsmc_nc->clk); 23258c2ecf20Sopenharmony_ci clk_put(hsmc_nc->clk); 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci atmel_nand_controller_cleanup(nc); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci return 0; 23318c2ecf20Sopenharmony_ci} 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_cistatic int atmel_hsmc_nand_controller_probe(struct platform_device *pdev, 23348c2ecf20Sopenharmony_ci const struct atmel_nand_controller_caps *caps) 23358c2ecf20Sopenharmony_ci{ 23368c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 23378c2ecf20Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 23388c2ecf20Sopenharmony_ci int ret; 23398c2ecf20Sopenharmony_ci 23408c2ecf20Sopenharmony_ci nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); 23418c2ecf20Sopenharmony_ci if (!nc) 23428c2ecf20Sopenharmony_ci return -ENOMEM; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci ret = atmel_nand_controller_init(&nc->base, pdev, caps); 23458c2ecf20Sopenharmony_ci if (ret) 23468c2ecf20Sopenharmony_ci return ret; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci if (caps->legacy_of_bindings) 23498c2ecf20Sopenharmony_ci ret = atmel_hsmc_nand_controller_legacy_init(nc); 23508c2ecf20Sopenharmony_ci else 23518c2ecf20Sopenharmony_ci ret = atmel_hsmc_nand_controller_init(nc); 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci if (ret) 23548c2ecf20Sopenharmony_ci return ret; 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci /* Make sure all irqs are masked before registering our IRQ handler. */ 23578c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); 23588c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt, 23598c2ecf20Sopenharmony_ci IRQF_SHARED, "nfc", nc); 23608c2ecf20Sopenharmony_ci if (ret) { 23618c2ecf20Sopenharmony_ci dev_err(dev, 23628c2ecf20Sopenharmony_ci "Could not get register NFC interrupt handler (err = %d)\n", 23638c2ecf20Sopenharmony_ci ret); 23648c2ecf20Sopenharmony_ci goto err; 23658c2ecf20Sopenharmony_ci } 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci /* Initial NFC configuration. */ 23688c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG, 23698c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CFG_DTO_MAX); 23708c2ecf20Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, 23718c2ecf20Sopenharmony_ci ATMEL_HSMC_NFC_CTRL_EN); 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci ret = atmel_nand_controller_add_nands(&nc->base); 23748c2ecf20Sopenharmony_ci if (ret) 23758c2ecf20Sopenharmony_ci goto err; 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci return 0; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cierr: 23808c2ecf20Sopenharmony_ci atmel_hsmc_nand_controller_remove(&nc->base); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci return ret; 23838c2ecf20Sopenharmony_ci} 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { 23868c2ecf20Sopenharmony_ci .probe = atmel_hsmc_nand_controller_probe, 23878c2ecf20Sopenharmony_ci .remove = atmel_hsmc_nand_controller_remove, 23888c2ecf20Sopenharmony_ci .ecc_init = atmel_hsmc_nand_ecc_init, 23898c2ecf20Sopenharmony_ci .nand_init = atmel_nand_init, 23908c2ecf20Sopenharmony_ci .setup_interface = atmel_hsmc_nand_setup_interface, 23918c2ecf20Sopenharmony_ci .exec_op = atmel_hsmc_nand_exec_op, 23928c2ecf20Sopenharmony_ci}; 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { 23958c2ecf20Sopenharmony_ci .has_dma = true, 23968c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 23978c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 23988c2ecf20Sopenharmony_ci .ops = &atmel_hsmc_nc_ops, 23998c2ecf20Sopenharmony_ci}; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci/* Only used to parse old bindings. */ 24028c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sama5_nand_caps = { 24038c2ecf20Sopenharmony_ci .has_dma = true, 24048c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 24058c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 24068c2ecf20Sopenharmony_ci .ops = &atmel_hsmc_nc_ops, 24078c2ecf20Sopenharmony_ci .legacy_of_bindings = true, 24088c2ecf20Sopenharmony_ci}; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_cistatic int atmel_smc_nand_controller_probe(struct platform_device *pdev, 24118c2ecf20Sopenharmony_ci const struct atmel_nand_controller_caps *caps) 24128c2ecf20Sopenharmony_ci{ 24138c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 24148c2ecf20Sopenharmony_ci struct atmel_smc_nand_controller *nc; 24158c2ecf20Sopenharmony_ci int ret; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); 24188c2ecf20Sopenharmony_ci if (!nc) 24198c2ecf20Sopenharmony_ci return -ENOMEM; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci ret = atmel_nand_controller_init(&nc->base, pdev, caps); 24228c2ecf20Sopenharmony_ci if (ret) 24238c2ecf20Sopenharmony_ci return ret; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci ret = atmel_smc_nand_controller_init(nc); 24268c2ecf20Sopenharmony_ci if (ret) 24278c2ecf20Sopenharmony_ci return ret; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci return atmel_nand_controller_add_nands(&nc->base); 24308c2ecf20Sopenharmony_ci} 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_cistatic int 24338c2ecf20Sopenharmony_ciatmel_smc_nand_controller_remove(struct atmel_nand_controller *nc) 24348c2ecf20Sopenharmony_ci{ 24358c2ecf20Sopenharmony_ci int ret; 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci ret = atmel_nand_controller_remove_nands(nc); 24388c2ecf20Sopenharmony_ci if (ret) 24398c2ecf20Sopenharmony_ci return ret; 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci atmel_nand_controller_cleanup(nc); 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci return 0; 24448c2ecf20Sopenharmony_ci} 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci/* 24478c2ecf20Sopenharmony_ci * The SMC reg layout of at91rm9200 is completely different which prevents us 24488c2ecf20Sopenharmony_ci * from re-using atmel_smc_nand_setup_interface() for the 24498c2ecf20Sopenharmony_ci * ->setup_interface() hook. 24508c2ecf20Sopenharmony_ci * At this point, there's no support for the at91rm9200 SMC IP, so we leave 24518c2ecf20Sopenharmony_ci * ->setup_interface() unassigned. 24528c2ecf20Sopenharmony_ci */ 24538c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_ops at91rm9200_nc_ops = { 24548c2ecf20Sopenharmony_ci .probe = atmel_smc_nand_controller_probe, 24558c2ecf20Sopenharmony_ci .remove = atmel_smc_nand_controller_remove, 24568c2ecf20Sopenharmony_ci .ecc_init = atmel_nand_ecc_init, 24578c2ecf20Sopenharmony_ci .nand_init = atmel_smc_nand_init, 24588c2ecf20Sopenharmony_ci .exec_op = atmel_smc_nand_exec_op, 24598c2ecf20Sopenharmony_ci}; 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = { 24628c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 24638c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 24648c2ecf20Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 24658c2ecf20Sopenharmony_ci .ops = &at91rm9200_nc_ops, 24668c2ecf20Sopenharmony_ci}; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_ops atmel_smc_nc_ops = { 24698c2ecf20Sopenharmony_ci .probe = atmel_smc_nand_controller_probe, 24708c2ecf20Sopenharmony_ci .remove = atmel_smc_nand_controller_remove, 24718c2ecf20Sopenharmony_ci .ecc_init = atmel_nand_ecc_init, 24728c2ecf20Sopenharmony_ci .nand_init = atmel_smc_nand_init, 24738c2ecf20Sopenharmony_ci .setup_interface = atmel_smc_nand_setup_interface, 24748c2ecf20Sopenharmony_ci .exec_op = atmel_smc_nand_exec_op, 24758c2ecf20Sopenharmony_ci}; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { 24788c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 24798c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 24808c2ecf20Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 24818c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 24828c2ecf20Sopenharmony_ci}; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = { 24858c2ecf20Sopenharmony_ci .ale_offs = BIT(22), 24868c2ecf20Sopenharmony_ci .cle_offs = BIT(21), 24878c2ecf20Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 24888c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 24898c2ecf20Sopenharmony_ci}; 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = { 24928c2ecf20Sopenharmony_ci .has_dma = true, 24938c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 24948c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 24958c2ecf20Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 24968c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 24978c2ecf20Sopenharmony_ci}; 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps microchip_sam9x60_nc_caps = { 25008c2ecf20Sopenharmony_ci .has_dma = true, 25018c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 25028c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 25038c2ecf20Sopenharmony_ci .ebi_csa_regmap_name = "microchip,sfr", 25048c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 25058c2ecf20Sopenharmony_ci}; 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci/* Only used to parse old bindings. */ 25088c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = { 25098c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 25108c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 25118c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 25128c2ecf20Sopenharmony_ci .legacy_of_bindings = true, 25138c2ecf20Sopenharmony_ci}; 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = { 25168c2ecf20Sopenharmony_ci .ale_offs = BIT(22), 25178c2ecf20Sopenharmony_ci .cle_offs = BIT(21), 25188c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 25198c2ecf20Sopenharmony_ci .legacy_of_bindings = true, 25208c2ecf20Sopenharmony_ci}; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = { 25238c2ecf20Sopenharmony_ci .has_dma = true, 25248c2ecf20Sopenharmony_ci .ale_offs = BIT(21), 25258c2ecf20Sopenharmony_ci .cle_offs = BIT(22), 25268c2ecf20Sopenharmony_ci .ops = &atmel_smc_nc_ops, 25278c2ecf20Sopenharmony_ci .legacy_of_bindings = true, 25288c2ecf20Sopenharmony_ci}; 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_nand_controller_of_ids[] = { 25318c2ecf20Sopenharmony_ci { 25328c2ecf20Sopenharmony_ci .compatible = "atmel,at91rm9200-nand-controller", 25338c2ecf20Sopenharmony_ci .data = &atmel_rm9200_nc_caps, 25348c2ecf20Sopenharmony_ci }, 25358c2ecf20Sopenharmony_ci { 25368c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9260-nand-controller", 25378c2ecf20Sopenharmony_ci .data = &atmel_sam9260_nc_caps, 25388c2ecf20Sopenharmony_ci }, 25398c2ecf20Sopenharmony_ci { 25408c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9261-nand-controller", 25418c2ecf20Sopenharmony_ci .data = &atmel_sam9261_nc_caps, 25428c2ecf20Sopenharmony_ci }, 25438c2ecf20Sopenharmony_ci { 25448c2ecf20Sopenharmony_ci .compatible = "atmel,at91sam9g45-nand-controller", 25458c2ecf20Sopenharmony_ci .data = &atmel_sam9g45_nc_caps, 25468c2ecf20Sopenharmony_ci }, 25478c2ecf20Sopenharmony_ci { 25488c2ecf20Sopenharmony_ci .compatible = "atmel,sama5d3-nand-controller", 25498c2ecf20Sopenharmony_ci .data = &atmel_sama5_nc_caps, 25508c2ecf20Sopenharmony_ci }, 25518c2ecf20Sopenharmony_ci { 25528c2ecf20Sopenharmony_ci .compatible = "microchip,sam9x60-nand-controller", 25538c2ecf20Sopenharmony_ci .data = µchip_sam9x60_nc_caps, 25548c2ecf20Sopenharmony_ci }, 25558c2ecf20Sopenharmony_ci /* Support for old/deprecated bindings: */ 25568c2ecf20Sopenharmony_ci { 25578c2ecf20Sopenharmony_ci .compatible = "atmel,at91rm9200-nand", 25588c2ecf20Sopenharmony_ci .data = &atmel_rm9200_nand_caps, 25598c2ecf20Sopenharmony_ci }, 25608c2ecf20Sopenharmony_ci { 25618c2ecf20Sopenharmony_ci .compatible = "atmel,sama5d4-nand", 25628c2ecf20Sopenharmony_ci .data = &atmel_rm9200_nand_caps, 25638c2ecf20Sopenharmony_ci }, 25648c2ecf20Sopenharmony_ci { 25658c2ecf20Sopenharmony_ci .compatible = "atmel,sama5d2-nand", 25668c2ecf20Sopenharmony_ci .data = &atmel_rm9200_nand_caps, 25678c2ecf20Sopenharmony_ci }, 25688c2ecf20Sopenharmony_ci { /* sentinel */ }, 25698c2ecf20Sopenharmony_ci}; 25708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids); 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_cistatic int atmel_nand_controller_probe(struct platform_device *pdev) 25738c2ecf20Sopenharmony_ci{ 25748c2ecf20Sopenharmony_ci const struct atmel_nand_controller_caps *caps; 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci if (pdev->id_entry) 25778c2ecf20Sopenharmony_ci caps = (void *)pdev->id_entry->driver_data; 25788c2ecf20Sopenharmony_ci else 25798c2ecf20Sopenharmony_ci caps = of_device_get_match_data(&pdev->dev); 25808c2ecf20Sopenharmony_ci 25818c2ecf20Sopenharmony_ci if (!caps) { 25828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not retrieve NFC caps\n"); 25838c2ecf20Sopenharmony_ci return -EINVAL; 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci if (caps->legacy_of_bindings) { 25878c2ecf20Sopenharmony_ci struct device_node *nfc_node; 25888c2ecf20Sopenharmony_ci u32 ale_offs = 21; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci /* 25918c2ecf20Sopenharmony_ci * If we are parsing legacy DT props and the DT contains a 25928c2ecf20Sopenharmony_ci * valid NFC node, forward the request to the sama5 logic. 25938c2ecf20Sopenharmony_ci */ 25948c2ecf20Sopenharmony_ci nfc_node = of_get_compatible_child(pdev->dev.of_node, 25958c2ecf20Sopenharmony_ci "atmel,sama5d3-nfc"); 25968c2ecf20Sopenharmony_ci if (nfc_node) { 25978c2ecf20Sopenharmony_ci caps = &atmel_sama5_nand_caps; 25988c2ecf20Sopenharmony_ci of_node_put(nfc_node); 25998c2ecf20Sopenharmony_ci } 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci /* 26028c2ecf20Sopenharmony_ci * Even if the compatible says we are dealing with an 26038c2ecf20Sopenharmony_ci * at91rm9200 controller, the atmel,nand-has-dma specify that 26048c2ecf20Sopenharmony_ci * this controller supports DMA, which means we are in fact 26058c2ecf20Sopenharmony_ci * dealing with an at91sam9g45+ controller. 26068c2ecf20Sopenharmony_ci */ 26078c2ecf20Sopenharmony_ci if (!caps->has_dma && 26088c2ecf20Sopenharmony_ci of_property_read_bool(pdev->dev.of_node, 26098c2ecf20Sopenharmony_ci "atmel,nand-has-dma")) 26108c2ecf20Sopenharmony_ci caps = &atmel_sam9g45_nand_caps; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci /* 26138c2ecf20Sopenharmony_ci * All SoCs except the at91sam9261 are assigning ALE to A21 and 26148c2ecf20Sopenharmony_ci * CLE to A22. If atmel,nand-addr-offset != 21 this means we're 26158c2ecf20Sopenharmony_ci * actually dealing with an at91sam9261 controller. 26168c2ecf20Sopenharmony_ci */ 26178c2ecf20Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, 26188c2ecf20Sopenharmony_ci "atmel,nand-addr-offset", &ale_offs); 26198c2ecf20Sopenharmony_ci if (ale_offs != 21) 26208c2ecf20Sopenharmony_ci caps = &atmel_sam9261_nand_caps; 26218c2ecf20Sopenharmony_ci } 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci return caps->ops->probe(pdev, caps); 26248c2ecf20Sopenharmony_ci} 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_cistatic int atmel_nand_controller_remove(struct platform_device *pdev) 26278c2ecf20Sopenharmony_ci{ 26288c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc = platform_get_drvdata(pdev); 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci return nc->caps->ops->remove(nc); 26318c2ecf20Sopenharmony_ci} 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_cistatic __maybe_unused int atmel_nand_controller_resume(struct device *dev) 26348c2ecf20Sopenharmony_ci{ 26358c2ecf20Sopenharmony_ci struct atmel_nand_controller *nc = dev_get_drvdata(dev); 26368c2ecf20Sopenharmony_ci struct atmel_nand *nand; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci if (nc->pmecc) 26398c2ecf20Sopenharmony_ci atmel_pmecc_reset(nc->pmecc); 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci list_for_each_entry(nand, &nc->chips, node) { 26428c2ecf20Sopenharmony_ci int i; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci for (i = 0; i < nand->numcs; i++) 26458c2ecf20Sopenharmony_ci nand_reset(&nand->base, i); 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci return 0; 26498c2ecf20Sopenharmony_ci} 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL, 26528c2ecf20Sopenharmony_ci atmel_nand_controller_resume); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_cistatic struct platform_driver atmel_nand_controller_driver = { 26558c2ecf20Sopenharmony_ci .driver = { 26568c2ecf20Sopenharmony_ci .name = "atmel-nand-controller", 26578c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(atmel_nand_controller_of_ids), 26588c2ecf20Sopenharmony_ci .pm = &atmel_nand_controller_pm_ops, 26598c2ecf20Sopenharmony_ci }, 26608c2ecf20Sopenharmony_ci .probe = atmel_nand_controller_probe, 26618c2ecf20Sopenharmony_ci .remove = atmel_nand_controller_remove, 26628c2ecf20Sopenharmony_ci}; 26638c2ecf20Sopenharmony_cimodule_platform_driver(atmel_nand_controller_driver); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 26668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 26678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs"); 26688c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:atmel-nand-controller"); 2669