162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2017 ATMEL 462306a36Sopenharmony_ci * Copyright 2017 Free Electrons 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Boris Brezillon <boris.brezillon@free-electrons.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Derived from the atmel_nand.c driver which contained the following 962306a36Sopenharmony_ci * copyrights: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Copyright 2003 Rick Bronson 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8) 1462306a36Sopenharmony_ci * Copyright 2001 Thomas Gleixner (gleixner@autronix.de) 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Derived from drivers/mtd/spia.c (removed in v3.8) 1762306a36Sopenharmony_ci * Copyright 2000 Steven J. Hill (sjhill@cotw.com) 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263 2162306a36Sopenharmony_ci * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Derived from Das U-Boot source code 2462306a36Sopenharmony_ci * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) 2562306a36Sopenharmony_ci * Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Add Programmable Multibit ECC support for various AT91 SoC 2862306a36Sopenharmony_ci * Copyright 2012 ATMEL, Hong Xu 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Add Nand Flash Controller support for SAMA5 SoC 3162306a36Sopenharmony_ci * Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com) 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * A few words about the naming convention in this file. This convention 3462306a36Sopenharmony_ci * applies to structure and function names. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * Prefixes: 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * - atmel_nand_: all generic structures/functions 3962306a36Sopenharmony_ci * - atmel_smc_nand_: all structures/functions specific to the SMC interface 4062306a36Sopenharmony_ci * (at91sam9 and avr32 SoCs) 4162306a36Sopenharmony_ci * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface 4262306a36Sopenharmony_ci * (sama5 SoCs and later) 4362306a36Sopenharmony_ci * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block 4462306a36Sopenharmony_ci * that is available in the HSMC block 4562306a36Sopenharmony_ci * - <soc>_nand_: all SoC specific structures/functions 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include <linux/clk.h> 4962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 5062306a36Sopenharmony_ci#include <linux/dmaengine.h> 5162306a36Sopenharmony_ci#include <linux/genalloc.h> 5262306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 5362306a36Sopenharmony_ci#include <linux/interrupt.h> 5462306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 5562306a36Sopenharmony_ci#include <linux/mfd/syscon/atmel-matrix.h> 5662306a36Sopenharmony_ci#include <linux/mfd/syscon/atmel-smc.h> 5762306a36Sopenharmony_ci#include <linux/module.h> 5862306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 5962306a36Sopenharmony_ci#include <linux/of_address.h> 6062306a36Sopenharmony_ci#include <linux/of_irq.h> 6162306a36Sopenharmony_ci#include <linux/of_platform.h> 6262306a36Sopenharmony_ci#include <linux/iopoll.h> 6362306a36Sopenharmony_ci#include <linux/platform_device.h> 6462306a36Sopenharmony_ci#include <linux/regmap.h> 6562306a36Sopenharmony_ci#include <soc/at91/atmel-sfr.h> 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#include "pmecc.h" 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG 0x0 7062306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x) (((x) / 4) << 24) 7162306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK GENMASK(30, 24) 7262306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul) (((cyc) << 16) | ((mul) << 20)) 7362306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_DTO_MAX GENMASK(22, 16) 7462306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_RBEDGE BIT(13) 7562306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE BIT(12) 7662306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_RSPARE BIT(9) 7762306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_WSPARE BIT(8) 7862306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK GENMASK(2, 0) 7962306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x) (fls((x) / 512) - 1) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL 0x4 8262306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL_EN BIT(0) 8362306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_CTRL_DIS BIT(1) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR 0x8 8662306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_IER 0xc 8762306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_IDR 0x10 8862306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_IMR 0x14 8962306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_ENABLED BIT(1) 9062306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RB_RISE BIT(4) 9162306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RB_FALL BIT(5) 9262306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_BUSY BIT(8) 9362306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_WR BIT(11) 9462306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_CSID GENMASK(14, 12) 9562306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_XFRDONE BIT(16) 9662306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_CMDDONE BIT(17) 9762306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_DTOE BIT(20) 9862306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_UNDEF BIT(21) 9962306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_AWB BIT(22) 10062306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_NFCASE BIT(23) 10162306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_ERRORS (ATMEL_HSMC_NFC_SR_DTOE | \ 10262306a36Sopenharmony_ci ATMEL_HSMC_NFC_SR_UNDEF | \ 10362306a36Sopenharmony_ci ATMEL_HSMC_NFC_SR_AWB | \ 10462306a36Sopenharmony_ci ATMEL_HSMC_NFC_SR_NFCASE) 10562306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_SR_RBEDGE(x) BIT((x) + 24) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_ADDR 0x18 10862306a36Sopenharmony_ci#define ATMEL_HSMC_NFC_BANK 0x1c 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define ATMEL_NFC_MAX_RB_ID 7 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#define ATMEL_NFC_SRAM_SIZE 0x2400 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define ATMEL_NFC_CMD(pos, cmd) ((cmd) << (((pos) * 8) + 2)) 11562306a36Sopenharmony_ci#define ATMEL_NFC_VCMD2 BIT(18) 11662306a36Sopenharmony_ci#define ATMEL_NFC_ACYCLE(naddrs) ((naddrs) << 19) 11762306a36Sopenharmony_ci#define ATMEL_NFC_CSID(cs) ((cs) << 22) 11862306a36Sopenharmony_ci#define ATMEL_NFC_DATAEN BIT(25) 11962306a36Sopenharmony_ci#define ATMEL_NFC_NFCWR BIT(26) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define ATMEL_NFC_MAX_ADDR_CYCLES 5 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define ATMEL_NAND_ALE_OFFSET BIT(21) 12462306a36Sopenharmony_ci#define ATMEL_NAND_CLE_OFFSET BIT(22) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define DEFAULT_TIMEOUT_MS 1000 12762306a36Sopenharmony_ci#define MIN_DMA_LEN 128 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic bool atmel_nand_avoid_dma __read_mostly; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciMODULE_PARM_DESC(avoiddma, "Avoid using DMA"); 13262306a36Sopenharmony_cimodule_param_named(avoiddma, atmel_nand_avoid_dma, bool, 0400); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cienum atmel_nand_rb_type { 13562306a36Sopenharmony_ci ATMEL_NAND_NO_RB, 13662306a36Sopenharmony_ci ATMEL_NAND_NATIVE_RB, 13762306a36Sopenharmony_ci ATMEL_NAND_GPIO_RB, 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct atmel_nand_rb { 14162306a36Sopenharmony_ci enum atmel_nand_rb_type type; 14262306a36Sopenharmony_ci union { 14362306a36Sopenharmony_ci struct gpio_desc *gpio; 14462306a36Sopenharmony_ci int id; 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct atmel_nand_cs { 14962306a36Sopenharmony_ci int id; 15062306a36Sopenharmony_ci struct atmel_nand_rb rb; 15162306a36Sopenharmony_ci struct gpio_desc *csgpio; 15262306a36Sopenharmony_ci struct { 15362306a36Sopenharmony_ci void __iomem *virt; 15462306a36Sopenharmony_ci dma_addr_t dma; 15562306a36Sopenharmony_ci } io; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci struct atmel_smc_cs_conf smcconf; 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistruct atmel_nand { 16162306a36Sopenharmony_ci struct list_head node; 16262306a36Sopenharmony_ci struct device *dev; 16362306a36Sopenharmony_ci struct nand_chip base; 16462306a36Sopenharmony_ci struct atmel_nand_cs *activecs; 16562306a36Sopenharmony_ci struct atmel_pmecc_user *pmecc; 16662306a36Sopenharmony_ci struct gpio_desc *cdgpio; 16762306a36Sopenharmony_ci int numcs; 16862306a36Sopenharmony_ci struct atmel_nand_cs cs[]; 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return container_of(chip, struct atmel_nand, base); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cienum atmel_nfc_data_xfer { 17762306a36Sopenharmony_ci ATMEL_NFC_NO_DATA, 17862306a36Sopenharmony_ci ATMEL_NFC_READ_DATA, 17962306a36Sopenharmony_ci ATMEL_NFC_WRITE_DATA, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistruct atmel_nfc_op { 18362306a36Sopenharmony_ci u8 cs; 18462306a36Sopenharmony_ci u8 ncmds; 18562306a36Sopenharmony_ci u8 cmds[2]; 18662306a36Sopenharmony_ci u8 naddrs; 18762306a36Sopenharmony_ci u8 addrs[5]; 18862306a36Sopenharmony_ci enum atmel_nfc_data_xfer data; 18962306a36Sopenharmony_ci u32 wait; 19062306a36Sopenharmony_ci u32 errors; 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistruct atmel_nand_controller; 19462306a36Sopenharmony_cistruct atmel_nand_controller_caps; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct atmel_nand_controller_ops { 19762306a36Sopenharmony_ci int (*probe)(struct platform_device *pdev, 19862306a36Sopenharmony_ci const struct atmel_nand_controller_caps *caps); 19962306a36Sopenharmony_ci int (*remove)(struct atmel_nand_controller *nc); 20062306a36Sopenharmony_ci void (*nand_init)(struct atmel_nand_controller *nc, 20162306a36Sopenharmony_ci struct atmel_nand *nand); 20262306a36Sopenharmony_ci int (*ecc_init)(struct nand_chip *chip); 20362306a36Sopenharmony_ci int (*setup_interface)(struct atmel_nand *nand, int csline, 20462306a36Sopenharmony_ci const struct nand_interface_config *conf); 20562306a36Sopenharmony_ci int (*exec_op)(struct atmel_nand *nand, 20662306a36Sopenharmony_ci const struct nand_operation *op, bool check_only); 20762306a36Sopenharmony_ci}; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistruct atmel_nand_controller_caps { 21062306a36Sopenharmony_ci bool has_dma; 21162306a36Sopenharmony_ci bool legacy_of_bindings; 21262306a36Sopenharmony_ci u32 ale_offs; 21362306a36Sopenharmony_ci u32 cle_offs; 21462306a36Sopenharmony_ci const char *ebi_csa_regmap_name; 21562306a36Sopenharmony_ci const struct atmel_nand_controller_ops *ops; 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistruct atmel_nand_controller { 21962306a36Sopenharmony_ci struct nand_controller base; 22062306a36Sopenharmony_ci const struct atmel_nand_controller_caps *caps; 22162306a36Sopenharmony_ci struct device *dev; 22262306a36Sopenharmony_ci struct regmap *smc; 22362306a36Sopenharmony_ci struct dma_chan *dmac; 22462306a36Sopenharmony_ci struct atmel_pmecc *pmecc; 22562306a36Sopenharmony_ci struct list_head chips; 22662306a36Sopenharmony_ci struct clk *mck; 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic inline struct atmel_nand_controller * 23062306a36Sopenharmony_cito_nand_controller(struct nand_controller *ctl) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci return container_of(ctl, struct atmel_nand_controller, base); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistruct atmel_smc_nand_ebi_csa_cfg { 23662306a36Sopenharmony_ci u32 offs; 23762306a36Sopenharmony_ci u32 nfd0_on_d16; 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistruct atmel_smc_nand_controller { 24162306a36Sopenharmony_ci struct atmel_nand_controller base; 24262306a36Sopenharmony_ci struct regmap *ebi_csa_regmap; 24362306a36Sopenharmony_ci struct atmel_smc_nand_ebi_csa_cfg *ebi_csa; 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic inline struct atmel_smc_nand_controller * 24762306a36Sopenharmony_cito_smc_nand_controller(struct nand_controller *ctl) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci return container_of(to_nand_controller(ctl), 25062306a36Sopenharmony_ci struct atmel_smc_nand_controller, base); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistruct atmel_hsmc_nand_controller { 25462306a36Sopenharmony_ci struct atmel_nand_controller base; 25562306a36Sopenharmony_ci struct { 25662306a36Sopenharmony_ci struct gen_pool *pool; 25762306a36Sopenharmony_ci void __iomem *virt; 25862306a36Sopenharmony_ci dma_addr_t dma; 25962306a36Sopenharmony_ci } sram; 26062306a36Sopenharmony_ci const struct atmel_hsmc_reg_layout *hsmc_layout; 26162306a36Sopenharmony_ci struct regmap *io; 26262306a36Sopenharmony_ci struct atmel_nfc_op op; 26362306a36Sopenharmony_ci struct completion complete; 26462306a36Sopenharmony_ci u32 cfg; 26562306a36Sopenharmony_ci int irq; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Only used when instantiating from legacy DT bindings. */ 26862306a36Sopenharmony_ci struct clk *clk; 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic inline struct atmel_hsmc_nand_controller * 27262306a36Sopenharmony_cito_hsmc_nand_controller(struct nand_controller *ctl) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci return container_of(to_nand_controller(ctl), 27562306a36Sopenharmony_ci struct atmel_hsmc_nand_controller, base); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS; 28162306a36Sopenharmony_ci op->wait ^= status & op->wait; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return !op->wait || op->errors; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic irqreturn_t atmel_nfc_interrupt(int irq, void *data) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc = data; 28962306a36Sopenharmony_ci u32 sr, rcvd; 29062306a36Sopenharmony_ci bool done; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); 29562306a36Sopenharmony_ci done = atmel_nfc_op_done(&nc->op, sr); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (rcvd) 29862306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (done) 30162306a36Sopenharmony_ci complete(&nc->complete); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return rcvd ? IRQ_HANDLED : IRQ_NONE; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll, 30762306a36Sopenharmony_ci unsigned int timeout_ms) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (!timeout_ms) 31262306a36Sopenharmony_ci timeout_ms = DEFAULT_TIMEOUT_MS; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (poll) { 31562306a36Sopenharmony_ci u32 status; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = regmap_read_poll_timeout(nc->base.smc, 31862306a36Sopenharmony_ci ATMEL_HSMC_NFC_SR, status, 31962306a36Sopenharmony_ci atmel_nfc_op_done(&nc->op, 32062306a36Sopenharmony_ci status), 32162306a36Sopenharmony_ci 0, timeout_ms * 1000); 32262306a36Sopenharmony_ci } else { 32362306a36Sopenharmony_ci init_completion(&nc->complete); 32462306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER, 32562306a36Sopenharmony_ci nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS); 32662306a36Sopenharmony_ci ret = wait_for_completion_timeout(&nc->complete, 32762306a36Sopenharmony_ci msecs_to_jiffies(timeout_ms)); 32862306a36Sopenharmony_ci if (!ret) 32962306a36Sopenharmony_ci ret = -ETIMEDOUT; 33062306a36Sopenharmony_ci else 33162306a36Sopenharmony_ci ret = 0; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) { 33762306a36Sopenharmony_ci dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n"); 33862306a36Sopenharmony_ci ret = -ETIMEDOUT; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) { 34262306a36Sopenharmony_ci dev_err(nc->base.dev, "Access to an undefined area\n"); 34362306a36Sopenharmony_ci ret = -EIO; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) { 34762306a36Sopenharmony_ci dev_err(nc->base.dev, "Access while busy\n"); 34862306a36Sopenharmony_ci ret = -EIO; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) { 35262306a36Sopenharmony_ci dev_err(nc->base.dev, "Wrong access size\n"); 35362306a36Sopenharmony_ci ret = -EIO; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return ret; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void atmel_nand_dma_transfer_finished(void *data) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct completion *finished = data; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci complete(finished); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic int atmel_nand_dma_transfer(struct atmel_nand_controller *nc, 36762306a36Sopenharmony_ci void *buf, dma_addr_t dev_dma, size_t len, 36862306a36Sopenharmony_ci enum dma_data_direction dir) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(finished); 37162306a36Sopenharmony_ci dma_addr_t src_dma, dst_dma, buf_dma; 37262306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 37362306a36Sopenharmony_ci dma_cookie_t cookie; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci buf_dma = dma_map_single(nc->dev, buf, len, dir); 37662306a36Sopenharmony_ci if (dma_mapping_error(nc->dev, dev_dma)) { 37762306a36Sopenharmony_ci dev_err(nc->dev, 37862306a36Sopenharmony_ci "Failed to prepare a buffer for DMA access\n"); 37962306a36Sopenharmony_ci goto err; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (dir == DMA_FROM_DEVICE) { 38362306a36Sopenharmony_ci src_dma = dev_dma; 38462306a36Sopenharmony_ci dst_dma = buf_dma; 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci src_dma = buf_dma; 38762306a36Sopenharmony_ci dst_dma = dev_dma; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len, 39162306a36Sopenharmony_ci DMA_CTRL_ACK | DMA_PREP_INTERRUPT); 39262306a36Sopenharmony_ci if (!tx) { 39362306a36Sopenharmony_ci dev_err(nc->dev, "Failed to prepare DMA memcpy\n"); 39462306a36Sopenharmony_ci goto err_unmap; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci tx->callback = atmel_nand_dma_transfer_finished; 39862306a36Sopenharmony_ci tx->callback_param = &finished; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci cookie = dmaengine_submit(tx); 40162306a36Sopenharmony_ci if (dma_submit_error(cookie)) { 40262306a36Sopenharmony_ci dev_err(nc->dev, "Failed to do DMA tx_submit\n"); 40362306a36Sopenharmony_ci goto err_unmap; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci dma_async_issue_pending(nc->dmac); 40762306a36Sopenharmony_ci wait_for_completion(&finished); 40862306a36Sopenharmony_ci dma_unmap_single(nc->dev, buf_dma, len, dir); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci return 0; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cierr_unmap: 41362306a36Sopenharmony_ci dma_unmap_single(nc->dev, buf_dma, len, dir); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cierr: 41662306a36Sopenharmony_ci dev_dbg(nc->dev, "Fall back to CPU I/O\n"); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return -EIO; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci u8 *addrs = nc->op.addrs; 42462306a36Sopenharmony_ci unsigned int op = 0; 42562306a36Sopenharmony_ci u32 addr, val; 42662306a36Sopenharmony_ci int i, ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci for (i = 0; i < nc->op.ncmds; i++) 43162306a36Sopenharmony_ci op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES) 43462306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci op |= ATMEL_NFC_CSID(nc->op.cs) | 43762306a36Sopenharmony_ci ATMEL_NFC_ACYCLE(nc->op.naddrs); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (nc->op.ncmds > 1) 44062306a36Sopenharmony_ci op |= ATMEL_NFC_VCMD2; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) | 44362306a36Sopenharmony_ci (addrs[3] << 24); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (nc->op.data != ATMEL_NFC_NO_DATA) { 44662306a36Sopenharmony_ci op |= ATMEL_NFC_DATAEN; 44762306a36Sopenharmony_ci nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (nc->op.data == ATMEL_NFC_WRITE_DATA) 45062306a36Sopenharmony_ci op |= ATMEL_NFC_NFCWR; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* Clear all flags. */ 45462306a36Sopenharmony_ci regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Send the command. */ 45762306a36Sopenharmony_ci regmap_write(nc->io, op, addr); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = atmel_nfc_wait(nc, poll, 0); 46062306a36Sopenharmony_ci if (ret) 46162306a36Sopenharmony_ci dev_err(nc->base.dev, 46262306a36Sopenharmony_ci "Failed to send NAND command (err = %d)!", 46362306a36Sopenharmony_ci ret); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* Reset the op state. */ 46662306a36Sopenharmony_ci memset(&nc->op, 0, sizeof(nc->op)); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic void atmel_nand_data_in(struct atmel_nand *nand, void *buf, 47262306a36Sopenharmony_ci unsigned int len, bool force_8bit) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct atmel_nand_controller *nc; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * If the controller supports DMA, the buffer address is DMA-able and 48062306a36Sopenharmony_ci * len is long enough to make DMA transfers profitable, let's trigger 48162306a36Sopenharmony_ci * a DMA transfer. If it fails, fallback to PIO mode. 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci if (nc->dmac && virt_addr_valid(buf) && 48462306a36Sopenharmony_ci len >= MIN_DMA_LEN && !force_8bit && 48562306a36Sopenharmony_ci !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len, 48662306a36Sopenharmony_ci DMA_FROM_DEVICE)) 48762306a36Sopenharmony_ci return; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit) 49062306a36Sopenharmony_ci ioread16_rep(nand->activecs->io.virt, buf, len / 2); 49162306a36Sopenharmony_ci else 49262306a36Sopenharmony_ci ioread8_rep(nand->activecs->io.virt, buf, len); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void atmel_nand_data_out(struct atmel_nand *nand, const void *buf, 49662306a36Sopenharmony_ci unsigned int len, bool force_8bit) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct atmel_nand_controller *nc; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * If the controller supports DMA, the buffer address is DMA-able and 50462306a36Sopenharmony_ci * len is long enough to make DMA transfers profitable, let's trigger 50562306a36Sopenharmony_ci * a DMA transfer. If it fails, fallback to PIO mode. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci if (nc->dmac && virt_addr_valid(buf) && 50862306a36Sopenharmony_ci len >= MIN_DMA_LEN && !force_8bit && 50962306a36Sopenharmony_ci !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma, 51062306a36Sopenharmony_ci len, DMA_TO_DEVICE)) 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if ((nand->base.options & NAND_BUSWIDTH_16) && !force_8bit) 51462306a36Sopenharmony_ci iowrite16_rep(nand->activecs->io.virt, buf, len / 2); 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci iowrite8_rep(nand->activecs->io.virt, buf, len); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int atmel_nand_waitrdy(struct atmel_nand *nand, unsigned int timeout_ms) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci if (nand->activecs->rb.type == ATMEL_NAND_NO_RB) 52262306a36Sopenharmony_ci return nand_soft_waitrdy(&nand->base, timeout_ms); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return nand_gpio_waitrdy(&nand->base, nand->activecs->rb.gpio, 52562306a36Sopenharmony_ci timeout_ms); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int atmel_hsmc_nand_waitrdy(struct atmel_nand *nand, 52962306a36Sopenharmony_ci unsigned int timeout_ms) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 53262306a36Sopenharmony_ci u32 status, mask; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) 53562306a36Sopenharmony_ci return atmel_nand_waitrdy(nand, timeout_ms); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci nc = to_hsmc_nand_controller(nand->base.controller); 53862306a36Sopenharmony_ci mask = ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id); 53962306a36Sopenharmony_ci return regmap_read_poll_timeout_atomic(nc->base.smc, ATMEL_HSMC_NFC_SR, 54062306a36Sopenharmony_ci status, status & mask, 54162306a36Sopenharmony_ci 10, timeout_ms * 1000); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void atmel_nand_select_target(struct atmel_nand *nand, 54562306a36Sopenharmony_ci unsigned int cs) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci nand->activecs = &nand->cs[cs]; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistatic void atmel_hsmc_nand_select_target(struct atmel_nand *nand, 55162306a36Sopenharmony_ci unsigned int cs) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(&nand->base); 55462306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 55562306a36Sopenharmony_ci u32 cfg = ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) | 55662306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) | 55762306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_RSPARE; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci nand->activecs = &nand->cs[cs]; 56062306a36Sopenharmony_ci nc = to_hsmc_nand_controller(nand->base.controller); 56162306a36Sopenharmony_ci if (nc->cfg == cfg) 56262306a36Sopenharmony_ci return; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG, 56562306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK | 56662306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK | 56762306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_RSPARE | 56862306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_WSPARE, 56962306a36Sopenharmony_ci cfg); 57062306a36Sopenharmony_ci nc->cfg = cfg; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int atmel_smc_nand_exec_instr(struct atmel_nand *nand, 57462306a36Sopenharmony_ci const struct nand_op_instr *instr) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct atmel_nand_controller *nc; 57762306a36Sopenharmony_ci unsigned int i; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 58062306a36Sopenharmony_ci switch (instr->type) { 58162306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 58262306a36Sopenharmony_ci writeb(instr->ctx.cmd.opcode, 58362306a36Sopenharmony_ci nand->activecs->io.virt + nc->caps->cle_offs); 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 58662306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) 58762306a36Sopenharmony_ci writeb(instr->ctx.addr.addrs[i], 58862306a36Sopenharmony_ci nand->activecs->io.virt + nc->caps->ale_offs); 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 59162306a36Sopenharmony_ci atmel_nand_data_in(nand, instr->ctx.data.buf.in, 59262306a36Sopenharmony_ci instr->ctx.data.len, 59362306a36Sopenharmony_ci instr->ctx.data.force_8bit); 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 59662306a36Sopenharmony_ci atmel_nand_data_out(nand, instr->ctx.data.buf.out, 59762306a36Sopenharmony_ci instr->ctx.data.len, 59862306a36Sopenharmony_ci instr->ctx.data.force_8bit); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 60162306a36Sopenharmony_ci return atmel_nand_waitrdy(nand, 60262306a36Sopenharmony_ci instr->ctx.waitrdy.timeout_ms); 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci break; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return -EINVAL; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int atmel_smc_nand_exec_op(struct atmel_nand *nand, 61162306a36Sopenharmony_ci const struct nand_operation *op, 61262306a36Sopenharmony_ci bool check_only) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci unsigned int i; 61562306a36Sopenharmony_ci int ret = 0; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (check_only) 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci atmel_nand_select_target(nand, op->cs); 62162306a36Sopenharmony_ci gpiod_set_value(nand->activecs->csgpio, 0); 62262306a36Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 62362306a36Sopenharmony_ci ret = atmel_smc_nand_exec_instr(nand, &op->instrs[i]); 62462306a36Sopenharmony_ci if (ret) 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci gpiod_set_value(nand->activecs->csgpio, 1); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int atmel_hsmc_exec_cmd_addr(struct nand_chip *chip, 63362306a36Sopenharmony_ci const struct nand_subop *subop) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 63662306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 63762306a36Sopenharmony_ci unsigned int i, j; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci nc->op.cs = nand->activecs->id; 64262306a36Sopenharmony_ci for (i = 0; i < subop->ninstrs; i++) { 64362306a36Sopenharmony_ci const struct nand_op_instr *instr = &subop->instrs[i]; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (instr->type == NAND_OP_CMD_INSTR) { 64662306a36Sopenharmony_ci nc->op.cmds[nc->op.ncmds++] = instr->ctx.cmd.opcode; 64762306a36Sopenharmony_ci continue; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for (j = nand_subop_get_addr_start_off(subop, i); 65162306a36Sopenharmony_ci j < nand_subop_get_num_addr_cyc(subop, i); j++) { 65262306a36Sopenharmony_ci nc->op.addrs[nc->op.naddrs] = instr->ctx.addr.addrs[j]; 65362306a36Sopenharmony_ci nc->op.naddrs++; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return atmel_nfc_exec_op(nc, true); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int atmel_hsmc_exec_rw(struct nand_chip *chip, 66162306a36Sopenharmony_ci const struct nand_subop *subop) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci const struct nand_op_instr *instr = subop->instrs; 66462306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (instr->type == NAND_OP_DATA_IN_INSTR) 66762306a36Sopenharmony_ci atmel_nand_data_in(nand, instr->ctx.data.buf.in, 66862306a36Sopenharmony_ci instr->ctx.data.len, 66962306a36Sopenharmony_ci instr->ctx.data.force_8bit); 67062306a36Sopenharmony_ci else 67162306a36Sopenharmony_ci atmel_nand_data_out(nand, instr->ctx.data.buf.out, 67262306a36Sopenharmony_ci instr->ctx.data.len, 67362306a36Sopenharmony_ci instr->ctx.data.force_8bit); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int atmel_hsmc_exec_waitrdy(struct nand_chip *chip, 67962306a36Sopenharmony_ci const struct nand_subop *subop) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci const struct nand_op_instr *instr = subop->instrs; 68262306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return atmel_hsmc_nand_waitrdy(nand, instr->ctx.waitrdy.timeout_ms); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cistatic const struct nand_op_parser atmel_hsmc_op_parser = NAND_OP_PARSER( 68862306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_cmd_addr, 68962306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 69062306a36Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 5), 69162306a36Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true)), 69262306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw, 69362306a36Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 0)), 69462306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_rw, 69562306a36Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 0)), 69662306a36Sopenharmony_ci NAND_OP_PARSER_PATTERN(atmel_hsmc_exec_waitrdy, 69762306a36Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), 69862306a36Sopenharmony_ci); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic int atmel_hsmc_nand_exec_op(struct atmel_nand *nand, 70162306a36Sopenharmony_ci const struct nand_operation *op, 70262306a36Sopenharmony_ci bool check_only) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci int ret; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if (check_only) 70762306a36Sopenharmony_ci return nand_op_parser_exec_op(&nand->base, 70862306a36Sopenharmony_ci &atmel_hsmc_op_parser, op, true); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci atmel_hsmc_nand_select_target(nand, op->cs); 71162306a36Sopenharmony_ci ret = nand_op_parser_exec_op(&nand->base, &atmel_hsmc_op_parser, op, 71262306a36Sopenharmony_ci false); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return ret; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf, 71862306a36Sopenharmony_ci bool oob_required) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 72162306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 72262306a36Sopenharmony_ci int ret = -EIO; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (nc->base.dmac) 72762306a36Sopenharmony_ci ret = atmel_nand_dma_transfer(&nc->base, (void *)buf, 72862306a36Sopenharmony_ci nc->sram.dma, mtd->writesize, 72962306a36Sopenharmony_ci DMA_TO_DEVICE); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* Falling back to CPU copy. */ 73262306a36Sopenharmony_ci if (ret) 73362306a36Sopenharmony_ci memcpy_toio(nc->sram.virt, buf, mtd->writesize); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (oob_required) 73662306a36Sopenharmony_ci memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi, 73762306a36Sopenharmony_ci mtd->oobsize); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf, 74162306a36Sopenharmony_ci bool oob_required) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 74462306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 74562306a36Sopenharmony_ci int ret = -EIO; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (nc->base.dmac) 75062306a36Sopenharmony_ci ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma, 75162306a36Sopenharmony_ci mtd->writesize, DMA_FROM_DEVICE); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Falling back to CPU copy. */ 75462306a36Sopenharmony_ci if (ret) 75562306a36Sopenharmony_ci memcpy_fromio(buf, nc->sram.virt, mtd->writesize); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (oob_required) 75862306a36Sopenharmony_ci memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize, 75962306a36Sopenharmony_ci mtd->oobsize); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 76562306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (column >= 0) { 77062306a36Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = column; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* 77362306a36Sopenharmony_ci * 2 address cycles for the column offset on large page NANDs. 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci if (mtd->writesize > 512) 77662306a36Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = column >> 8; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (page >= 0) { 78062306a36Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = page; 78162306a36Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = page >> 8; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (chip->options & NAND_ROW_ADDR_3) 78462306a36Sopenharmony_ci nc->op.addrs[nc->op.naddrs++] = page >> 16; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 79162306a36Sopenharmony_ci struct atmel_nand_controller *nc; 79262306a36Sopenharmony_ci int ret; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci nc = to_nand_controller(chip->controller); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (raw) 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ret = atmel_pmecc_enable(nand->pmecc, op); 80062306a36Sopenharmony_ci if (ret) 80162306a36Sopenharmony_ci dev_err(nc->dev, 80262306a36Sopenharmony_ci "Failed to enable ECC engine (err = %d)\n", ret); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return ret; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw) 80862306a36Sopenharmony_ci{ 80962306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (!raw) 81262306a36Sopenharmony_ci atmel_pmecc_disable(nand->pmecc); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 81862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 81962306a36Sopenharmony_ci struct atmel_nand_controller *nc; 82062306a36Sopenharmony_ci struct mtd_oob_region oobregion; 82162306a36Sopenharmony_ci void *eccbuf; 82262306a36Sopenharmony_ci int ret, i; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci nc = to_nand_controller(chip->controller); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (raw) 82762306a36Sopenharmony_ci return 0; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci ret = atmel_pmecc_wait_rdy(nand->pmecc); 83062306a36Sopenharmony_ci if (ret) { 83162306a36Sopenharmony_ci dev_err(nc->dev, 83262306a36Sopenharmony_ci "Failed to transfer NAND page data (err = %d)\n", 83362306a36Sopenharmony_ci ret); 83462306a36Sopenharmony_ci return ret; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &oobregion); 83862306a36Sopenharmony_ci eccbuf = chip->oob_poi + oobregion.offset; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci for (i = 0; i < chip->ecc.steps; i++) { 84162306a36Sopenharmony_ci atmel_pmecc_get_generated_eccbytes(nand->pmecc, i, 84262306a36Sopenharmony_ci eccbuf); 84362306a36Sopenharmony_ci eccbuf += chip->ecc.bytes; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf, 85062306a36Sopenharmony_ci bool raw) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 85362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 85462306a36Sopenharmony_ci struct atmel_nand_controller *nc; 85562306a36Sopenharmony_ci struct mtd_oob_region oobregion; 85662306a36Sopenharmony_ci int ret, i, max_bitflips = 0; 85762306a36Sopenharmony_ci void *databuf, *eccbuf; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci nc = to_nand_controller(chip->controller); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (raw) 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci ret = atmel_pmecc_wait_rdy(nand->pmecc); 86562306a36Sopenharmony_ci if (ret) { 86662306a36Sopenharmony_ci dev_err(nc->dev, 86762306a36Sopenharmony_ci "Failed to read NAND page data (err = %d)\n", 86862306a36Sopenharmony_ci ret); 86962306a36Sopenharmony_ci return ret; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &oobregion); 87362306a36Sopenharmony_ci eccbuf = chip->oob_poi + oobregion.offset; 87462306a36Sopenharmony_ci databuf = buf; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci for (i = 0; i < chip->ecc.steps; i++) { 87762306a36Sopenharmony_ci ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf, 87862306a36Sopenharmony_ci eccbuf); 87962306a36Sopenharmony_ci if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc)) 88062306a36Sopenharmony_ci ret = nand_check_erased_ecc_chunk(databuf, 88162306a36Sopenharmony_ci chip->ecc.size, 88262306a36Sopenharmony_ci eccbuf, 88362306a36Sopenharmony_ci chip->ecc.bytes, 88462306a36Sopenharmony_ci NULL, 0, 88562306a36Sopenharmony_ci chip->ecc.strength); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (ret >= 0) { 88862306a36Sopenharmony_ci mtd->ecc_stats.corrected += ret; 88962306a36Sopenharmony_ci max_bitflips = max(ret, max_bitflips); 89062306a36Sopenharmony_ci } else { 89162306a36Sopenharmony_ci mtd->ecc_stats.failed++; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci databuf += chip->ecc.size; 89562306a36Sopenharmony_ci eccbuf += chip->ecc.bytes; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return max_bitflips; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf, 90262306a36Sopenharmony_ci bool oob_required, int page, bool raw) 90362306a36Sopenharmony_ci{ 90462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 90562306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 90662306a36Sopenharmony_ci int ret; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, NULL, 0); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); 91162306a36Sopenharmony_ci if (ret) 91262306a36Sopenharmony_ci return ret; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci nand_write_data_op(chip, buf, mtd->writesize, false); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); 91762306a36Sopenharmony_ci if (ret) { 91862306a36Sopenharmony_ci atmel_pmecc_disable(nand->pmecc); 91962306a36Sopenharmony_ci return ret; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_cistatic int atmel_nand_pmecc_write_page(struct nand_chip *chip, const u8 *buf, 93062306a36Sopenharmony_ci int oob_required, int page) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic int atmel_nand_pmecc_write_page_raw(struct nand_chip *chip, 93662306a36Sopenharmony_ci const u8 *buf, int oob_required, 93762306a36Sopenharmony_ci int page) 93862306a36Sopenharmony_ci{ 93962306a36Sopenharmony_ci return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true); 94062306a36Sopenharmony_ci} 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, 94362306a36Sopenharmony_ci bool oob_required, int page, bool raw) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 94662306a36Sopenharmony_ci int ret; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci nand_read_page_op(chip, page, 0, NULL, 0); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); 95162306a36Sopenharmony_ci if (ret) 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci ret = nand_read_data_op(chip, buf, mtd->writesize, false, false); 95562306a36Sopenharmony_ci if (ret) 95662306a36Sopenharmony_ci goto out_disable; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci ret = nand_read_data_op(chip, chip->oob_poi, mtd->oobsize, false, false); 95962306a36Sopenharmony_ci if (ret) 96062306a36Sopenharmony_ci goto out_disable; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci ret = atmel_nand_pmecc_correct_data(chip, buf, raw); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ciout_disable: 96562306a36Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci return ret; 96862306a36Sopenharmony_ci} 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_cistatic int atmel_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf, 97162306a36Sopenharmony_ci int oob_required, int page) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false); 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic int atmel_nand_pmecc_read_page_raw(struct nand_chip *chip, u8 *buf, 97762306a36Sopenharmony_ci int oob_required, int page) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true); 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip, 98362306a36Sopenharmony_ci const u8 *buf, bool oob_required, 98462306a36Sopenharmony_ci int page, bool raw) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 98762306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 98862306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 98962306a36Sopenharmony_ci int ret; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci atmel_hsmc_nand_select_target(nand, chip->cur_cs); 99262306a36Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci atmel_nfc_copy_to_sram(chip, buf, false); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci nc->op.cmds[0] = NAND_CMD_SEQIN; 99762306a36Sopenharmony_ci nc->op.ncmds = 1; 99862306a36Sopenharmony_ci atmel_nfc_set_op_addr(chip, page, 0x0); 99962306a36Sopenharmony_ci nc->op.cs = nand->activecs->id; 100062306a36Sopenharmony_ci nc->op.data = ATMEL_NFC_WRITE_DATA; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw); 100362306a36Sopenharmony_ci if (ret) 100462306a36Sopenharmony_ci return ret; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci ret = atmel_nfc_exec_op(nc, false); 100762306a36Sopenharmony_ci if (ret) { 100862306a36Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 100962306a36Sopenharmony_ci dev_err(nc->base.dev, 101062306a36Sopenharmony_ci "Failed to transfer NAND page data (err = %d)\n", 101162306a36Sopenharmony_ci ret); 101262306a36Sopenharmony_ci return ret; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ret = atmel_nand_pmecc_generate_eccbytes(chip, raw); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (ret) 102062306a36Sopenharmony_ci return ret; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci nand_write_data_op(chip, chip->oob_poi, mtd->oobsize, false); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_page(struct nand_chip *chip, 102862306a36Sopenharmony_ci const u8 *buf, int oob_required, 102962306a36Sopenharmony_ci int page) 103062306a36Sopenharmony_ci{ 103162306a36Sopenharmony_ci return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, 103262306a36Sopenharmony_ci false); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_write_page_raw(struct nand_chip *chip, 103662306a36Sopenharmony_ci const u8 *buf, 103762306a36Sopenharmony_ci int oob_required, int page) 103862306a36Sopenharmony_ci{ 103962306a36Sopenharmony_ci return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page, 104062306a36Sopenharmony_ci true); 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf, 104462306a36Sopenharmony_ci bool oob_required, int page, 104562306a36Sopenharmony_ci bool raw) 104662306a36Sopenharmony_ci{ 104762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 104862306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 104962306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 105062306a36Sopenharmony_ci int ret; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci atmel_hsmc_nand_select_target(nand, chip->cur_cs); 105362306a36Sopenharmony_ci nc = to_hsmc_nand_controller(chip->controller); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci /* 105662306a36Sopenharmony_ci * Optimized read page accessors only work when the NAND R/B pin is 105762306a36Sopenharmony_ci * connected to a native SoC R/B pin. If that's not the case, fallback 105862306a36Sopenharmony_ci * to the non-optimized one. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) 106162306a36Sopenharmony_ci return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, 106262306a36Sopenharmony_ci raw); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (mtd->writesize > 512) 106762306a36Sopenharmony_ci nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci atmel_nfc_set_op_addr(chip, page, 0x0); 107062306a36Sopenharmony_ci nc->op.cs = nand->activecs->id; 107162306a36Sopenharmony_ci nc->op.data = ATMEL_NFC_READ_DATA; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw); 107462306a36Sopenharmony_ci if (ret) 107562306a36Sopenharmony_ci return ret; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci ret = atmel_nfc_exec_op(nc, false); 107862306a36Sopenharmony_ci if (ret) { 107962306a36Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 108062306a36Sopenharmony_ci dev_err(nc->base.dev, 108162306a36Sopenharmony_ci "Failed to load NAND page data (err = %d)\n", 108262306a36Sopenharmony_ci ret); 108362306a36Sopenharmony_ci return ret; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci atmel_nfc_copy_from_sram(chip, buf, true); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci ret = atmel_nand_pmecc_correct_data(chip, buf, raw); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci atmel_nand_pmecc_disable(chip, raw); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return ret; 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_page(struct nand_chip *chip, u8 *buf, 109662306a36Sopenharmony_ci int oob_required, int page) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, 109962306a36Sopenharmony_ci false); 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic int atmel_hsmc_nand_pmecc_read_page_raw(struct nand_chip *chip, 110362306a36Sopenharmony_ci u8 *buf, int oob_required, 110462306a36Sopenharmony_ci int page) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page, 110762306a36Sopenharmony_ci true); 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic int atmel_nand_pmecc_init(struct nand_chip *chip) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci const struct nand_ecc_props *requirements = 111362306a36Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 111462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 111562306a36Sopenharmony_ci struct nand_device *nanddev = mtd_to_nanddev(mtd); 111662306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 111762306a36Sopenharmony_ci struct atmel_nand_controller *nc; 111862306a36Sopenharmony_ci struct atmel_pmecc_user_req req; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci nc = to_nand_controller(chip->controller); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (!nc->pmecc) { 112362306a36Sopenharmony_ci dev_err(nc->dev, "HW ECC not supported\n"); 112462306a36Sopenharmony_ci return -ENOTSUPP; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (nc->caps->legacy_of_bindings) { 112862306a36Sopenharmony_ci u32 val; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap", 113162306a36Sopenharmony_ci &val)) 113262306a36Sopenharmony_ci chip->ecc.strength = val; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (!of_property_read_u32(nc->dev->of_node, 113562306a36Sopenharmony_ci "atmel,pmecc-sector-size", 113662306a36Sopenharmony_ci &val)) 113762306a36Sopenharmony_ci chip->ecc.size = val; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) 114162306a36Sopenharmony_ci req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; 114262306a36Sopenharmony_ci else if (chip->ecc.strength) 114362306a36Sopenharmony_ci req.ecc.strength = chip->ecc.strength; 114462306a36Sopenharmony_ci else if (requirements->strength) 114562306a36Sopenharmony_ci req.ecc.strength = requirements->strength; 114662306a36Sopenharmony_ci else 114762306a36Sopenharmony_ci req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (chip->ecc.size) 115062306a36Sopenharmony_ci req.ecc.sectorsize = chip->ecc.size; 115162306a36Sopenharmony_ci else if (requirements->step_size) 115262306a36Sopenharmony_ci req.ecc.sectorsize = requirements->step_size; 115362306a36Sopenharmony_ci else 115462306a36Sopenharmony_ci req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci req.pagesize = mtd->writesize; 115762306a36Sopenharmony_ci req.oobsize = mtd->oobsize; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (mtd->writesize <= 512) { 116062306a36Sopenharmony_ci req.ecc.bytes = 4; 116162306a36Sopenharmony_ci req.ecc.ooboffset = 0; 116262306a36Sopenharmony_ci } else { 116362306a36Sopenharmony_ci req.ecc.bytes = mtd->oobsize - 2; 116462306a36Sopenharmony_ci req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req); 116862306a36Sopenharmony_ci if (IS_ERR(nand->pmecc)) 116962306a36Sopenharmony_ci return PTR_ERR(nand->pmecc); 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_BCH; 117262306a36Sopenharmony_ci chip->ecc.size = req.ecc.sectorsize; 117362306a36Sopenharmony_ci chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors; 117462306a36Sopenharmony_ci chip->ecc.strength = req.ecc.strength; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci chip->options |= NAND_NO_SUBPAGE_WRITE; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout()); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci return 0; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int atmel_nand_ecc_init(struct nand_chip *chip) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct atmel_nand_controller *nc; 118662306a36Sopenharmony_ci int ret; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci nc = to_nand_controller(chip->controller); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci switch (chip->ecc.engine_type) { 119162306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_NONE: 119262306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_SOFT: 119362306a36Sopenharmony_ci /* 119462306a36Sopenharmony_ci * Nothing to do, the core will initialize everything for us. 119562306a36Sopenharmony_ci */ 119662306a36Sopenharmony_ci break; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci case NAND_ECC_ENGINE_TYPE_ON_HOST: 119962306a36Sopenharmony_ci ret = atmel_nand_pmecc_init(chip); 120062306a36Sopenharmony_ci if (ret) 120162306a36Sopenharmony_ci return ret; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci chip->ecc.read_page = atmel_nand_pmecc_read_page; 120462306a36Sopenharmony_ci chip->ecc.write_page = atmel_nand_pmecc_write_page; 120562306a36Sopenharmony_ci chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw; 120662306a36Sopenharmony_ci chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw; 120762306a36Sopenharmony_ci break; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci default: 121062306a36Sopenharmony_ci /* Other modes are not supported. */ 121162306a36Sopenharmony_ci dev_err(nc->dev, "Unsupported ECC mode: %d\n", 121262306a36Sopenharmony_ci chip->ecc.engine_type); 121362306a36Sopenharmony_ci return -ENOTSUPP; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci return 0; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic int atmel_hsmc_nand_ecc_init(struct nand_chip *chip) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci int ret; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci ret = atmel_nand_ecc_init(chip); 122462306a36Sopenharmony_ci if (ret) 122562306a36Sopenharmony_ci return ret; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 122862306a36Sopenharmony_ci return 0; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* Adjust the ECC operations for the HSMC IP. */ 123162306a36Sopenharmony_ci chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page; 123262306a36Sopenharmony_ci chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; 123362306a36Sopenharmony_ci chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; 123462306a36Sopenharmony_ci chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci return 0; 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, 124062306a36Sopenharmony_ci const struct nand_interface_config *conf, 124162306a36Sopenharmony_ci struct atmel_smc_cs_conf *smcconf) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci u32 ncycles, totalcycles, timeps, mckperiodps; 124462306a36Sopenharmony_ci struct atmel_nand_controller *nc; 124562306a36Sopenharmony_ci int ret; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci /* DDR interface not supported. */ 125062306a36Sopenharmony_ci if (!nand_interface_is_sdr(conf)) 125162306a36Sopenharmony_ci return -ENOTSUPP; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* 125462306a36Sopenharmony_ci * tRC < 30ns implies EDO mode. This controller does not support this 125562306a36Sopenharmony_ci * mode. 125662306a36Sopenharmony_ci */ 125762306a36Sopenharmony_ci if (conf->timings.sdr.tRC_min < 30000) 125862306a36Sopenharmony_ci return -ENOTSUPP; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci atmel_smc_cs_conf_init(smcconf); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci mckperiodps = NSEC_PER_SEC / clk_get_rate(nc->mck); 126362306a36Sopenharmony_ci mckperiodps *= 1000; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* 126662306a36Sopenharmony_ci * Set write pulse timing. This one is easy to extract: 126762306a36Sopenharmony_ci * 126862306a36Sopenharmony_ci * NWE_PULSE = tWP 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tWP_min, mckperiodps); 127162306a36Sopenharmony_ci totalcycles = ncycles; 127262306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NWE_SHIFT, 127362306a36Sopenharmony_ci ncycles); 127462306a36Sopenharmony_ci if (ret) 127562306a36Sopenharmony_ci return ret; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * The write setup timing depends on the operation done on the NAND. 127962306a36Sopenharmony_ci * All operations goes through the same data bus, but the operation 128062306a36Sopenharmony_ci * type depends on the address we are writing to (ALE/CLE address 128162306a36Sopenharmony_ci * lines). 128262306a36Sopenharmony_ci * Since we have no way to differentiate the different operations at 128362306a36Sopenharmony_ci * the SMC level, we must consider the worst case (the biggest setup 128462306a36Sopenharmony_ci * time among all operation types): 128562306a36Sopenharmony_ci * 128662306a36Sopenharmony_ci * NWE_SETUP = max(tCLS, tCS, tALS, tDS) - NWE_PULSE 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_ci timeps = max3(conf->timings.sdr.tCLS_min, conf->timings.sdr.tCS_min, 128962306a36Sopenharmony_ci conf->timings.sdr.tALS_min); 129062306a36Sopenharmony_ci timeps = max(timeps, conf->timings.sdr.tDS_min); 129162306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(timeps, mckperiodps); 129262306a36Sopenharmony_ci ncycles = ncycles > totalcycles ? ncycles - totalcycles : 0; 129362306a36Sopenharmony_ci totalcycles += ncycles; 129462306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_setup(smcconf, ATMEL_SMC_NWE_SHIFT, 129562306a36Sopenharmony_ci ncycles); 129662306a36Sopenharmony_ci if (ret) 129762306a36Sopenharmony_ci return ret; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci /* 130062306a36Sopenharmony_ci * As for the write setup timing, the write hold timing depends on the 130162306a36Sopenharmony_ci * operation done on the NAND: 130262306a36Sopenharmony_ci * 130362306a36Sopenharmony_ci * NWE_HOLD = max(tCLH, tCH, tALH, tDH, tWH) 130462306a36Sopenharmony_ci */ 130562306a36Sopenharmony_ci timeps = max3(conf->timings.sdr.tCLH_min, conf->timings.sdr.tCH_min, 130662306a36Sopenharmony_ci conf->timings.sdr.tALH_min); 130762306a36Sopenharmony_ci timeps = max3(timeps, conf->timings.sdr.tDH_min, 130862306a36Sopenharmony_ci conf->timings.sdr.tWH_min); 130962306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(timeps, mckperiodps); 131062306a36Sopenharmony_ci totalcycles += ncycles; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* 131362306a36Sopenharmony_ci * The write cycle timing is directly matching tWC, but is also 131462306a36Sopenharmony_ci * dependent on the other timings on the setup and hold timings we 131562306a36Sopenharmony_ci * calculated earlier, which gives: 131662306a36Sopenharmony_ci * 131762306a36Sopenharmony_ci * NWE_CYCLE = max(tWC, NWE_SETUP + NWE_PULSE + NWE_HOLD) 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tWC_min, mckperiodps); 132062306a36Sopenharmony_ci ncycles = max(totalcycles, ncycles); 132162306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NWE_SHIFT, 132262306a36Sopenharmony_ci ncycles); 132362306a36Sopenharmony_ci if (ret) 132462306a36Sopenharmony_ci return ret; 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci /* 132762306a36Sopenharmony_ci * We don't want the CS line to be toggled between each byte/word 132862306a36Sopenharmony_ci * transfer to the NAND. The only way to guarantee that is to have the 132962306a36Sopenharmony_ci * NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: 133062306a36Sopenharmony_ci * 133162306a36Sopenharmony_ci * NCS_WR_PULSE = NWE_CYCLE 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_WR_SHIFT, 133462306a36Sopenharmony_ci ncycles); 133562306a36Sopenharmony_ci if (ret) 133662306a36Sopenharmony_ci return ret; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* 133962306a36Sopenharmony_ci * As for the write setup timing, the read hold timing depends on the 134062306a36Sopenharmony_ci * operation done on the NAND: 134162306a36Sopenharmony_ci * 134262306a36Sopenharmony_ci * NRD_HOLD = max(tREH, tRHOH) 134362306a36Sopenharmony_ci */ 134462306a36Sopenharmony_ci timeps = max(conf->timings.sdr.tREH_min, conf->timings.sdr.tRHOH_min); 134562306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(timeps, mckperiodps); 134662306a36Sopenharmony_ci totalcycles = ncycles; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* 134962306a36Sopenharmony_ci * TDF = tRHZ - NRD_HOLD 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRHZ_max, mckperiodps); 135262306a36Sopenharmony_ci ncycles -= totalcycles; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci /* 135562306a36Sopenharmony_ci * In ONFI 4.0 specs, tRHZ has been increased to support EDO NANDs and 135662306a36Sopenharmony_ci * we might end up with a config that does not fit in the TDF field. 135762306a36Sopenharmony_ci * Just take the max value in this case and hope that the NAND is more 135862306a36Sopenharmony_ci * tolerant than advertised. 135962306a36Sopenharmony_ci */ 136062306a36Sopenharmony_ci if (ncycles > ATMEL_SMC_MODE_TDF_MAX) 136162306a36Sopenharmony_ci ncycles = ATMEL_SMC_MODE_TDF_MAX; 136262306a36Sopenharmony_ci else if (ncycles < ATMEL_SMC_MODE_TDF_MIN) 136362306a36Sopenharmony_ci ncycles = ATMEL_SMC_MODE_TDF_MIN; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles) | 136662306a36Sopenharmony_ci ATMEL_SMC_MODE_TDFMODE_OPTIMIZED; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * Read pulse timing directly matches tRP: 137062306a36Sopenharmony_ci * 137162306a36Sopenharmony_ci * NRD_PULSE = tRP 137262306a36Sopenharmony_ci */ 137362306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRP_min, mckperiodps); 137462306a36Sopenharmony_ci totalcycles += ncycles; 137562306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NRD_SHIFT, 137662306a36Sopenharmony_ci ncycles); 137762306a36Sopenharmony_ci if (ret) 137862306a36Sopenharmony_ci return ret; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* 138162306a36Sopenharmony_ci * The write cycle timing is directly matching tWC, but is also 138262306a36Sopenharmony_ci * dependent on the setup and hold timings we calculated earlier, 138362306a36Sopenharmony_ci * which gives: 138462306a36Sopenharmony_ci * 138562306a36Sopenharmony_ci * NRD_CYCLE = max(tRC, NRD_PULSE + NRD_HOLD) 138662306a36Sopenharmony_ci * 138762306a36Sopenharmony_ci * NRD_SETUP is always 0. 138862306a36Sopenharmony_ci */ 138962306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRC_min, mckperiodps); 139062306a36Sopenharmony_ci ncycles = max(totalcycles, ncycles); 139162306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_cycle(smcconf, ATMEL_SMC_NRD_SHIFT, 139262306a36Sopenharmony_ci ncycles); 139362306a36Sopenharmony_ci if (ret) 139462306a36Sopenharmony_ci return ret; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci /* 139762306a36Sopenharmony_ci * We don't want the CS line to be toggled between each byte/word 139862306a36Sopenharmony_ci * transfer from the NAND. The only way to guarantee that is to have 139962306a36Sopenharmony_ci * the NCS_{WR,RD}_{SETUP,HOLD} timings set to 0, which in turn means: 140062306a36Sopenharmony_ci * 140162306a36Sopenharmony_ci * NCS_RD_PULSE = NRD_CYCLE 140262306a36Sopenharmony_ci */ 140362306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_pulse(smcconf, ATMEL_SMC_NCS_RD_SHIFT, 140462306a36Sopenharmony_ci ncycles); 140562306a36Sopenharmony_ci if (ret) 140662306a36Sopenharmony_ci return ret; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci /* Txxx timings are directly matching tXXX ones. */ 140962306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tCLR_min, mckperiodps); 141062306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 141162306a36Sopenharmony_ci ATMEL_HSMC_TIMINGS_TCLR_SHIFT, 141262306a36Sopenharmony_ci ncycles); 141362306a36Sopenharmony_ci if (ret) 141462306a36Sopenharmony_ci return ret; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tADL_min, mckperiodps); 141762306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 141862306a36Sopenharmony_ci ATMEL_HSMC_TIMINGS_TADL_SHIFT, 141962306a36Sopenharmony_ci ncycles); 142062306a36Sopenharmony_ci /* 142162306a36Sopenharmony_ci * Version 4 of the ONFI spec mandates that tADL be at least 400 142262306a36Sopenharmony_ci * nanoseconds, but, depending on the master clock rate, 400 ns may not 142362306a36Sopenharmony_ci * fit in the tADL field of the SMC reg. We need to relax the check and 142462306a36Sopenharmony_ci * accept the -ERANGE return code. 142562306a36Sopenharmony_ci * 142662306a36Sopenharmony_ci * Note that previous versions of the ONFI spec had a lower tADL_min 142762306a36Sopenharmony_ci * (100 or 200 ns). It's not clear why this timing constraint got 142862306a36Sopenharmony_ci * increased but it seems most NANDs are fine with values lower than 142962306a36Sopenharmony_ci * 400ns, so we should be safe. 143062306a36Sopenharmony_ci */ 143162306a36Sopenharmony_ci if (ret && ret != -ERANGE) 143262306a36Sopenharmony_ci return ret; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); 143562306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 143662306a36Sopenharmony_ci ATMEL_HSMC_TIMINGS_TAR_SHIFT, 143762306a36Sopenharmony_ci ncycles); 143862306a36Sopenharmony_ci if (ret) 143962306a36Sopenharmony_ci return ret; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tRR_min, mckperiodps); 144262306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 144362306a36Sopenharmony_ci ATMEL_HSMC_TIMINGS_TRR_SHIFT, 144462306a36Sopenharmony_ci ncycles); 144562306a36Sopenharmony_ci if (ret) 144662306a36Sopenharmony_ci return ret; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci ncycles = DIV_ROUND_UP(conf->timings.sdr.tWB_max, mckperiodps); 144962306a36Sopenharmony_ci ret = atmel_smc_cs_conf_set_timing(smcconf, 145062306a36Sopenharmony_ci ATMEL_HSMC_TIMINGS_TWB_SHIFT, 145162306a36Sopenharmony_ci ncycles); 145262306a36Sopenharmony_ci if (ret) 145362306a36Sopenharmony_ci return ret; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* Attach the CS line to the NFC logic. */ 145662306a36Sopenharmony_ci smcconf->timings |= ATMEL_HSMC_TIMINGS_NFSEL; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* Set the appropriate data bus width. */ 145962306a36Sopenharmony_ci if (nand->base.options & NAND_BUSWIDTH_16) 146062306a36Sopenharmony_ci smcconf->mode |= ATMEL_SMC_MODE_DBW_16; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* Operate in NRD/NWE READ/WRITEMODE. */ 146362306a36Sopenharmony_ci smcconf->mode |= ATMEL_SMC_MODE_READMODE_NRD | 146462306a36Sopenharmony_ci ATMEL_SMC_MODE_WRITEMODE_NWE; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_cistatic int atmel_smc_nand_setup_interface(struct atmel_nand *nand, 147062306a36Sopenharmony_ci int csline, 147162306a36Sopenharmony_ci const struct nand_interface_config *conf) 147262306a36Sopenharmony_ci{ 147362306a36Sopenharmony_ci struct atmel_nand_controller *nc; 147462306a36Sopenharmony_ci struct atmel_smc_cs_conf smcconf; 147562306a36Sopenharmony_ci struct atmel_nand_cs *cs; 147662306a36Sopenharmony_ci int ret; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); 148162306a36Sopenharmony_ci if (ret) 148262306a36Sopenharmony_ci return ret; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 148562306a36Sopenharmony_ci return 0; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci cs = &nand->cs[csline]; 148862306a36Sopenharmony_ci cs->smcconf = smcconf; 148962306a36Sopenharmony_ci atmel_smc_cs_conf_apply(nc->smc, cs->id, &cs->smcconf); 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int atmel_hsmc_nand_setup_interface(struct atmel_nand *nand, 149562306a36Sopenharmony_ci int csline, 149662306a36Sopenharmony_ci const struct nand_interface_config *conf) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 149962306a36Sopenharmony_ci struct atmel_smc_cs_conf smcconf; 150062306a36Sopenharmony_ci struct atmel_nand_cs *cs; 150162306a36Sopenharmony_ci int ret; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci nc = to_hsmc_nand_controller(nand->base.controller); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci ret = atmel_smc_nand_prepare_smcconf(nand, conf, &smcconf); 150662306a36Sopenharmony_ci if (ret) 150762306a36Sopenharmony_ci return ret; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci cs = &nand->cs[csline]; 151362306a36Sopenharmony_ci cs->smcconf = smcconf; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (cs->rb.type == ATMEL_NAND_NATIVE_RB) 151662306a36Sopenharmony_ci cs->smcconf.timings |= ATMEL_HSMC_TIMINGS_RBNSEL(cs->rb.id); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci atmel_hsmc_cs_conf_apply(nc->base.smc, nc->hsmc_layout, cs->id, 151962306a36Sopenharmony_ci &cs->smcconf); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci return 0; 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_cistatic int atmel_nand_setup_interface(struct nand_chip *chip, int csline, 152562306a36Sopenharmony_ci const struct nand_interface_config *conf) 152662306a36Sopenharmony_ci{ 152762306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 152862306a36Sopenharmony_ci const struct nand_sdr_timings *sdr; 152962306a36Sopenharmony_ci struct atmel_nand_controller *nc; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci sdr = nand_get_sdr_timings(conf); 153262306a36Sopenharmony_ci if (IS_ERR(sdr)) 153362306a36Sopenharmony_ci return PTR_ERR(sdr); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (csline >= nand->numcs || 153862306a36Sopenharmony_ci (csline < 0 && csline != NAND_DATA_IFACE_CHECK_ONLY)) 153962306a36Sopenharmony_ci return -EINVAL; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci return nc->caps->ops->setup_interface(nand, csline, conf); 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic int atmel_nand_exec_op(struct nand_chip *chip, 154562306a36Sopenharmony_ci const struct nand_operation *op, 154662306a36Sopenharmony_ci bool check_only) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 154962306a36Sopenharmony_ci struct atmel_nand_controller *nc; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci nc = to_nand_controller(nand->base.controller); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci return nc->caps->ops->exec_op(nand, op, check_only); 155462306a36Sopenharmony_ci} 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_cistatic void atmel_nand_init(struct atmel_nand_controller *nc, 155762306a36Sopenharmony_ci struct atmel_nand *nand) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci struct nand_chip *chip = &nand->base; 156062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci mtd->dev.parent = nc->dev; 156362306a36Sopenharmony_ci nand->base.controller = &nc->base; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (!nc->mck || !nc->caps->ops->setup_interface) 156662306a36Sopenharmony_ci chip->options |= NAND_KEEP_TIMINGS; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci /* 156962306a36Sopenharmony_ci * Use a bounce buffer when the buffer passed by the MTD user is not 157062306a36Sopenharmony_ci * suitable for DMA. 157162306a36Sopenharmony_ci */ 157262306a36Sopenharmony_ci if (nc->dmac) 157362306a36Sopenharmony_ci chip->options |= NAND_USES_DMA; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci /* Default to HW ECC if pmecc is available. */ 157662306a36Sopenharmony_ci if (nc->pmecc) 157762306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic void atmel_smc_nand_init(struct atmel_nand_controller *nc, 158162306a36Sopenharmony_ci struct atmel_nand *nand) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci struct nand_chip *chip = &nand->base; 158462306a36Sopenharmony_ci struct atmel_smc_nand_controller *smc_nc; 158562306a36Sopenharmony_ci int i; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci atmel_nand_init(nc, nand); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci smc_nc = to_smc_nand_controller(chip->controller); 159062306a36Sopenharmony_ci if (!smc_nc->ebi_csa_regmap) 159162306a36Sopenharmony_ci return; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci /* Attach the CS to the NAND Flash logic. */ 159462306a36Sopenharmony_ci for (i = 0; i < nand->numcs; i++) 159562306a36Sopenharmony_ci regmap_update_bits(smc_nc->ebi_csa_regmap, 159662306a36Sopenharmony_ci smc_nc->ebi_csa->offs, 159762306a36Sopenharmony_ci BIT(nand->cs[i].id), BIT(nand->cs[i].id)); 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (smc_nc->ebi_csa->nfd0_on_d16) 160062306a36Sopenharmony_ci regmap_update_bits(smc_nc->ebi_csa_regmap, 160162306a36Sopenharmony_ci smc_nc->ebi_csa->offs, 160262306a36Sopenharmony_ci smc_nc->ebi_csa->nfd0_on_d16, 160362306a36Sopenharmony_ci smc_nc->ebi_csa->nfd0_on_d16); 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic int atmel_nand_controller_remove_nand(struct atmel_nand *nand) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct nand_chip *chip = &nand->base; 160962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 161062306a36Sopenharmony_ci int ret; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci ret = mtd_device_unregister(mtd); 161362306a36Sopenharmony_ci if (ret) 161462306a36Sopenharmony_ci return ret; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci nand_cleanup(chip); 161762306a36Sopenharmony_ci list_del(&nand->node); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci return 0; 162062306a36Sopenharmony_ci} 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_cistatic struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc, 162362306a36Sopenharmony_ci struct device_node *np, 162462306a36Sopenharmony_ci int reg_cells) 162562306a36Sopenharmony_ci{ 162662306a36Sopenharmony_ci struct atmel_nand *nand; 162762306a36Sopenharmony_ci struct gpio_desc *gpio; 162862306a36Sopenharmony_ci int numcs, ret, i; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci numcs = of_property_count_elems_of_size(np, "reg", 163162306a36Sopenharmony_ci reg_cells * sizeof(u32)); 163262306a36Sopenharmony_ci if (numcs < 1) { 163362306a36Sopenharmony_ci dev_err(nc->dev, "Missing or invalid reg property\n"); 163462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci nand = devm_kzalloc(nc->dev, struct_size(nand, cs, numcs), GFP_KERNEL); 163862306a36Sopenharmony_ci if (!nand) 163962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci nand->numcs = numcs; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci gpio = devm_fwnode_gpiod_get(nc->dev, of_fwnode_handle(np), 164462306a36Sopenharmony_ci "det", GPIOD_IN, "nand-det"); 164562306a36Sopenharmony_ci if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { 164662306a36Sopenharmony_ci dev_err(nc->dev, 164762306a36Sopenharmony_ci "Failed to get detect gpio (err = %ld)\n", 164862306a36Sopenharmony_ci PTR_ERR(gpio)); 164962306a36Sopenharmony_ci return ERR_CAST(gpio); 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!IS_ERR(gpio)) 165362306a36Sopenharmony_ci nand->cdgpio = gpio; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci for (i = 0; i < numcs; i++) { 165662306a36Sopenharmony_ci struct resource res; 165762306a36Sopenharmony_ci u32 val; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &res); 166062306a36Sopenharmony_ci if (ret) { 166162306a36Sopenharmony_ci dev_err(nc->dev, "Invalid reg property (err = %d)\n", 166262306a36Sopenharmony_ci ret); 166362306a36Sopenharmony_ci return ERR_PTR(ret); 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci ret = of_property_read_u32_index(np, "reg", i * reg_cells, 166762306a36Sopenharmony_ci &val); 166862306a36Sopenharmony_ci if (ret) { 166962306a36Sopenharmony_ci dev_err(nc->dev, "Invalid reg property (err = %d)\n", 167062306a36Sopenharmony_ci ret); 167162306a36Sopenharmony_ci return ERR_PTR(ret); 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci nand->cs[i].id = val; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci nand->cs[i].io.dma = res.start; 167762306a36Sopenharmony_ci nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res); 167862306a36Sopenharmony_ci if (IS_ERR(nand->cs[i].io.virt)) 167962306a36Sopenharmony_ci return ERR_CAST(nand->cs[i].io.virt); 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci if (!of_property_read_u32(np, "atmel,rb", &val)) { 168262306a36Sopenharmony_ci if (val > ATMEL_NFC_MAX_RB_ID) 168362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB; 168662306a36Sopenharmony_ci nand->cs[i].rb.id = val; 168762306a36Sopenharmony_ci } else { 168862306a36Sopenharmony_ci gpio = devm_fwnode_gpiod_get_index(nc->dev, 168962306a36Sopenharmony_ci of_fwnode_handle(np), 169062306a36Sopenharmony_ci "rb", i, GPIOD_IN, 169162306a36Sopenharmony_ci "nand-rb"); 169262306a36Sopenharmony_ci if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { 169362306a36Sopenharmony_ci dev_err(nc->dev, 169462306a36Sopenharmony_ci "Failed to get R/B gpio (err = %ld)\n", 169562306a36Sopenharmony_ci PTR_ERR(gpio)); 169662306a36Sopenharmony_ci return ERR_CAST(gpio); 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci if (!IS_ERR(gpio)) { 170062306a36Sopenharmony_ci nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB; 170162306a36Sopenharmony_ci nand->cs[i].rb.gpio = gpio; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci gpio = devm_fwnode_gpiod_get_index(nc->dev, 170662306a36Sopenharmony_ci of_fwnode_handle(np), 170762306a36Sopenharmony_ci "cs", i, GPIOD_OUT_HIGH, 170862306a36Sopenharmony_ci "nand-cs"); 170962306a36Sopenharmony_ci if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) { 171062306a36Sopenharmony_ci dev_err(nc->dev, 171162306a36Sopenharmony_ci "Failed to get CS gpio (err = %ld)\n", 171262306a36Sopenharmony_ci PTR_ERR(gpio)); 171362306a36Sopenharmony_ci return ERR_CAST(gpio); 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci if (!IS_ERR(gpio)) 171762306a36Sopenharmony_ci nand->cs[i].csgpio = gpio; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci nand_set_flash_node(&nand->base, np); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci return nand; 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic int 172662306a36Sopenharmony_ciatmel_nand_controller_add_nand(struct atmel_nand_controller *nc, 172762306a36Sopenharmony_ci struct atmel_nand *nand) 172862306a36Sopenharmony_ci{ 172962306a36Sopenharmony_ci struct nand_chip *chip = &nand->base; 173062306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 173162306a36Sopenharmony_ci int ret; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci /* No card inserted, skip this NAND. */ 173462306a36Sopenharmony_ci if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) { 173562306a36Sopenharmony_ci dev_info(nc->dev, "No SmartMedia card inserted.\n"); 173662306a36Sopenharmony_ci return 0; 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci nc->caps->ops->nand_init(nc, nand); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci ret = nand_scan(chip, nand->numcs); 174262306a36Sopenharmony_ci if (ret) { 174362306a36Sopenharmony_ci dev_err(nc->dev, "NAND scan failed: %d\n", ret); 174462306a36Sopenharmony_ci return ret; 174562306a36Sopenharmony_ci } 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 174862306a36Sopenharmony_ci if (ret) { 174962306a36Sopenharmony_ci dev_err(nc->dev, "Failed to register mtd device: %d\n", ret); 175062306a36Sopenharmony_ci nand_cleanup(chip); 175162306a36Sopenharmony_ci return ret; 175262306a36Sopenharmony_ci } 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci list_add_tail(&nand->node, &nc->chips); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci return 0; 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic int 176062306a36Sopenharmony_ciatmel_nand_controller_remove_nands(struct atmel_nand_controller *nc) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci struct atmel_nand *nand, *tmp; 176362306a36Sopenharmony_ci int ret; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci list_for_each_entry_safe(nand, tmp, &nc->chips, node) { 176662306a36Sopenharmony_ci ret = atmel_nand_controller_remove_nand(nand); 176762306a36Sopenharmony_ci if (ret) 176862306a36Sopenharmony_ci return ret; 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci return 0; 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cistatic int 177562306a36Sopenharmony_ciatmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc) 177662306a36Sopenharmony_ci{ 177762306a36Sopenharmony_ci struct device *dev = nc->dev; 177862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 177962306a36Sopenharmony_ci struct atmel_nand *nand; 178062306a36Sopenharmony_ci struct gpio_desc *gpio; 178162306a36Sopenharmony_ci struct resource *res; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci /* 178462306a36Sopenharmony_ci * Legacy bindings only allow connecting a single NAND with a unique CS 178562306a36Sopenharmony_ci * line to the controller. 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_ci nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs), 178862306a36Sopenharmony_ci GFP_KERNEL); 178962306a36Sopenharmony_ci if (!nand) 179062306a36Sopenharmony_ci return -ENOMEM; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci nand->numcs = 1; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci nand->cs[0].io.virt = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 179562306a36Sopenharmony_ci if (IS_ERR(nand->cs[0].io.virt)) 179662306a36Sopenharmony_ci return PTR_ERR(nand->cs[0].io.virt); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci nand->cs[0].io.dma = res->start; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* 180162306a36Sopenharmony_ci * The old driver was hardcoding the CS id to 3 for all sama5 180262306a36Sopenharmony_ci * controllers. Since this id is only meaningful for the sama5 180362306a36Sopenharmony_ci * controller we can safely assign this id to 3 no matter the 180462306a36Sopenharmony_ci * controller. 180562306a36Sopenharmony_ci * If one wants to connect a NAND to a different CS line, he will 180662306a36Sopenharmony_ci * have to use the new bindings. 180762306a36Sopenharmony_ci */ 180862306a36Sopenharmony_ci nand->cs[0].id = 3; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci /* R/B GPIO. */ 181162306a36Sopenharmony_ci gpio = devm_gpiod_get_index_optional(dev, NULL, 0, GPIOD_IN); 181262306a36Sopenharmony_ci if (IS_ERR(gpio)) { 181362306a36Sopenharmony_ci dev_err(dev, "Failed to get R/B gpio (err = %ld)\n", 181462306a36Sopenharmony_ci PTR_ERR(gpio)); 181562306a36Sopenharmony_ci return PTR_ERR(gpio); 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci if (gpio) { 181962306a36Sopenharmony_ci nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB; 182062306a36Sopenharmony_ci nand->cs[0].rb.gpio = gpio; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci /* CS GPIO. */ 182462306a36Sopenharmony_ci gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH); 182562306a36Sopenharmony_ci if (IS_ERR(gpio)) { 182662306a36Sopenharmony_ci dev_err(dev, "Failed to get CS gpio (err = %ld)\n", 182762306a36Sopenharmony_ci PTR_ERR(gpio)); 182862306a36Sopenharmony_ci return PTR_ERR(gpio); 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci nand->cs[0].csgpio = gpio; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* Card detect GPIO. */ 183462306a36Sopenharmony_ci gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN); 183562306a36Sopenharmony_ci if (IS_ERR(gpio)) { 183662306a36Sopenharmony_ci dev_err(dev, 183762306a36Sopenharmony_ci "Failed to get detect gpio (err = %ld)\n", 183862306a36Sopenharmony_ci PTR_ERR(gpio)); 183962306a36Sopenharmony_ci return PTR_ERR(gpio); 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci nand->cdgpio = gpio; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci nand_set_flash_node(&nand->base, nc->dev->of_node); 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci return atmel_nand_controller_add_nand(nc, nand); 184762306a36Sopenharmony_ci} 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci struct device_node *np, *nand_np; 185262306a36Sopenharmony_ci struct device *dev = nc->dev; 185362306a36Sopenharmony_ci int ret, reg_cells; 185462306a36Sopenharmony_ci u32 val; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci /* We do not retrieve the SMC syscon when parsing old DTs. */ 185762306a36Sopenharmony_ci if (nc->caps->legacy_of_bindings) 185862306a36Sopenharmony_ci return atmel_nand_controller_legacy_add_nands(nc); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci np = dev->of_node; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci ret = of_property_read_u32(np, "#address-cells", &val); 186362306a36Sopenharmony_ci if (ret) { 186462306a36Sopenharmony_ci dev_err(dev, "missing #address-cells property\n"); 186562306a36Sopenharmony_ci return ret; 186662306a36Sopenharmony_ci } 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci reg_cells = val; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci ret = of_property_read_u32(np, "#size-cells", &val); 187162306a36Sopenharmony_ci if (ret) { 187262306a36Sopenharmony_ci dev_err(dev, "missing #size-cells property\n"); 187362306a36Sopenharmony_ci return ret; 187462306a36Sopenharmony_ci } 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci reg_cells += val; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci for_each_child_of_node(np, nand_np) { 187962306a36Sopenharmony_ci struct atmel_nand *nand; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci nand = atmel_nand_create(nc, nand_np, reg_cells); 188262306a36Sopenharmony_ci if (IS_ERR(nand)) { 188362306a36Sopenharmony_ci ret = PTR_ERR(nand); 188462306a36Sopenharmony_ci goto err; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci ret = atmel_nand_controller_add_nand(nc, nand); 188862306a36Sopenharmony_ci if (ret) 188962306a36Sopenharmony_ci goto err; 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci return 0; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_cierr: 189562306a36Sopenharmony_ci atmel_nand_controller_remove_nands(nc); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return ret; 189862306a36Sopenharmony_ci} 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_cistatic void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci if (nc->dmac) 190362306a36Sopenharmony_ci dma_release_channel(nc->dmac); 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci clk_put(nc->mck); 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9260_ebi_csa = { 190962306a36Sopenharmony_ci .offs = AT91SAM9260_MATRIX_EBICSA, 191062306a36Sopenharmony_ci}; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9261_ebi_csa = { 191362306a36Sopenharmony_ci .offs = AT91SAM9261_MATRIX_EBICSA, 191462306a36Sopenharmony_ci}; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9263_ebi_csa = { 191762306a36Sopenharmony_ci .offs = AT91SAM9263_MATRIX_EBI0CSA, 191862306a36Sopenharmony_ci}; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9rl_ebi_csa = { 192162306a36Sopenharmony_ci .offs = AT91SAM9RL_MATRIX_EBICSA, 192262306a36Sopenharmony_ci}; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9g45_ebi_csa = { 192562306a36Sopenharmony_ci .offs = AT91SAM9G45_MATRIX_EBICSA, 192662306a36Sopenharmony_ci}; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9n12_ebi_csa = { 192962306a36Sopenharmony_ci .offs = AT91SAM9N12_MATRIX_EBICSA, 193062306a36Sopenharmony_ci}; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg at91sam9x5_ebi_csa = { 193362306a36Sopenharmony_ci .offs = AT91SAM9X5_MATRIX_EBICSA, 193462306a36Sopenharmony_ci}; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_cistatic const struct atmel_smc_nand_ebi_csa_cfg sam9x60_ebi_csa = { 193762306a36Sopenharmony_ci .offs = AT91_SFR_CCFG_EBICSA, 193862306a36Sopenharmony_ci .nfd0_on_d16 = AT91_SFR_CCFG_NFD0_ON_D16, 193962306a36Sopenharmony_ci}; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused atmel_ebi_csa_regmap_of_ids[] = { 194262306a36Sopenharmony_ci { 194362306a36Sopenharmony_ci .compatible = "atmel,at91sam9260-matrix", 194462306a36Sopenharmony_ci .data = &at91sam9260_ebi_csa, 194562306a36Sopenharmony_ci }, 194662306a36Sopenharmony_ci { 194762306a36Sopenharmony_ci .compatible = "atmel,at91sam9261-matrix", 194862306a36Sopenharmony_ci .data = &at91sam9261_ebi_csa, 194962306a36Sopenharmony_ci }, 195062306a36Sopenharmony_ci { 195162306a36Sopenharmony_ci .compatible = "atmel,at91sam9263-matrix", 195262306a36Sopenharmony_ci .data = &at91sam9263_ebi_csa, 195362306a36Sopenharmony_ci }, 195462306a36Sopenharmony_ci { 195562306a36Sopenharmony_ci .compatible = "atmel,at91sam9rl-matrix", 195662306a36Sopenharmony_ci .data = &at91sam9rl_ebi_csa, 195762306a36Sopenharmony_ci }, 195862306a36Sopenharmony_ci { 195962306a36Sopenharmony_ci .compatible = "atmel,at91sam9g45-matrix", 196062306a36Sopenharmony_ci .data = &at91sam9g45_ebi_csa, 196162306a36Sopenharmony_ci }, 196262306a36Sopenharmony_ci { 196362306a36Sopenharmony_ci .compatible = "atmel,at91sam9n12-matrix", 196462306a36Sopenharmony_ci .data = &at91sam9n12_ebi_csa, 196562306a36Sopenharmony_ci }, 196662306a36Sopenharmony_ci { 196762306a36Sopenharmony_ci .compatible = "atmel,at91sam9x5-matrix", 196862306a36Sopenharmony_ci .data = &at91sam9x5_ebi_csa, 196962306a36Sopenharmony_ci }, 197062306a36Sopenharmony_ci { 197162306a36Sopenharmony_ci .compatible = "microchip,sam9x60-sfr", 197262306a36Sopenharmony_ci .data = &sam9x60_ebi_csa, 197362306a36Sopenharmony_ci }, 197462306a36Sopenharmony_ci { /* sentinel */ }, 197562306a36Sopenharmony_ci}; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic int atmel_nand_attach_chip(struct nand_chip *chip) 197862306a36Sopenharmony_ci{ 197962306a36Sopenharmony_ci struct atmel_nand_controller *nc = to_nand_controller(chip->controller); 198062306a36Sopenharmony_ci struct atmel_nand *nand = to_atmel_nand(chip); 198162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 198262306a36Sopenharmony_ci int ret; 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci ret = nc->caps->ops->ecc_init(chip); 198562306a36Sopenharmony_ci if (ret) 198662306a36Sopenharmony_ci return ret; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (nc->caps->legacy_of_bindings || !nc->dev->of_node) { 198962306a36Sopenharmony_ci /* 199062306a36Sopenharmony_ci * We keep the MTD name unchanged to avoid breaking platforms 199162306a36Sopenharmony_ci * where the MTD cmdline parser is used and the bootloader 199262306a36Sopenharmony_ci * has not been updated to use the new naming scheme. 199362306a36Sopenharmony_ci */ 199462306a36Sopenharmony_ci mtd->name = "atmel_nand"; 199562306a36Sopenharmony_ci } else if (!mtd->name) { 199662306a36Sopenharmony_ci /* 199762306a36Sopenharmony_ci * If the new bindings are used and the bootloader has not been 199862306a36Sopenharmony_ci * updated to pass a new mtdparts parameter on the cmdline, you 199962306a36Sopenharmony_ci * should define the following property in your nand node: 200062306a36Sopenharmony_ci * 200162306a36Sopenharmony_ci * label = "atmel_nand"; 200262306a36Sopenharmony_ci * 200362306a36Sopenharmony_ci * This way, mtd->name will be set by the core when 200462306a36Sopenharmony_ci * nand_set_flash_node() is called. 200562306a36Sopenharmony_ci */ 200662306a36Sopenharmony_ci mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL, 200762306a36Sopenharmony_ci "%s:nand.%d", dev_name(nc->dev), 200862306a36Sopenharmony_ci nand->cs[0].id); 200962306a36Sopenharmony_ci if (!mtd->name) { 201062306a36Sopenharmony_ci dev_err(nc->dev, "Failed to allocate mtd->name\n"); 201162306a36Sopenharmony_ci return -ENOMEM; 201262306a36Sopenharmony_ci } 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci return 0; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic const struct nand_controller_ops atmel_nand_controller_ops = { 201962306a36Sopenharmony_ci .attach_chip = atmel_nand_attach_chip, 202062306a36Sopenharmony_ci .setup_interface = atmel_nand_setup_interface, 202162306a36Sopenharmony_ci .exec_op = atmel_nand_exec_op, 202262306a36Sopenharmony_ci}; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_cistatic int atmel_nand_controller_init(struct atmel_nand_controller *nc, 202562306a36Sopenharmony_ci struct platform_device *pdev, 202662306a36Sopenharmony_ci const struct atmel_nand_controller_caps *caps) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 202962306a36Sopenharmony_ci struct device_node *np = dev->of_node; 203062306a36Sopenharmony_ci int ret; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci nand_controller_init(&nc->base); 203362306a36Sopenharmony_ci nc->base.ops = &atmel_nand_controller_ops; 203462306a36Sopenharmony_ci INIT_LIST_HEAD(&nc->chips); 203562306a36Sopenharmony_ci nc->dev = dev; 203662306a36Sopenharmony_ci nc->caps = caps; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci platform_set_drvdata(pdev, nc); 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci nc->pmecc = devm_atmel_pmecc_get(dev); 204162306a36Sopenharmony_ci if (IS_ERR(nc->pmecc)) 204262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(nc->pmecc), 204362306a36Sopenharmony_ci "Could not get PMECC object\n"); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci if (nc->caps->has_dma && !atmel_nand_avoid_dma) { 204662306a36Sopenharmony_ci dma_cap_mask_t mask; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci dma_cap_zero(mask); 204962306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, mask); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci nc->dmac = dma_request_channel(mask, NULL, NULL); 205262306a36Sopenharmony_ci if (!nc->dmac) 205362306a36Sopenharmony_ci dev_err(nc->dev, "Failed to request DMA channel\n"); 205462306a36Sopenharmony_ci } 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* We do not retrieve the SMC syscon when parsing old DTs. */ 205762306a36Sopenharmony_ci if (nc->caps->legacy_of_bindings) 205862306a36Sopenharmony_ci return 0; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci nc->mck = of_clk_get(dev->parent->of_node, 0); 206162306a36Sopenharmony_ci if (IS_ERR(nc->mck)) { 206262306a36Sopenharmony_ci dev_err(dev, "Failed to retrieve MCK clk\n"); 206362306a36Sopenharmony_ci ret = PTR_ERR(nc->mck); 206462306a36Sopenharmony_ci goto out_release_dma; 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); 206862306a36Sopenharmony_ci if (!np) { 206962306a36Sopenharmony_ci dev_err(dev, "Missing or invalid atmel,smc property\n"); 207062306a36Sopenharmony_ci ret = -EINVAL; 207162306a36Sopenharmony_ci goto out_release_dma; 207262306a36Sopenharmony_ci } 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci nc->smc = syscon_node_to_regmap(np); 207562306a36Sopenharmony_ci of_node_put(np); 207662306a36Sopenharmony_ci if (IS_ERR(nc->smc)) { 207762306a36Sopenharmony_ci ret = PTR_ERR(nc->smc); 207862306a36Sopenharmony_ci dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret); 207962306a36Sopenharmony_ci goto out_release_dma; 208062306a36Sopenharmony_ci } 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci return 0; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ciout_release_dma: 208562306a36Sopenharmony_ci if (nc->dmac) 208662306a36Sopenharmony_ci dma_release_channel(nc->dmac); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci return ret; 208962306a36Sopenharmony_ci} 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_cistatic int 209262306a36Sopenharmony_ciatmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc) 209362306a36Sopenharmony_ci{ 209462306a36Sopenharmony_ci struct device *dev = nc->base.dev; 209562306a36Sopenharmony_ci const struct of_device_id *match; 209662306a36Sopenharmony_ci struct device_node *np; 209762306a36Sopenharmony_ci int ret; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci /* We do not retrieve the EBICSA regmap when parsing old DTs. */ 210062306a36Sopenharmony_ci if (nc->base.caps->legacy_of_bindings) 210162306a36Sopenharmony_ci return 0; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci np = of_parse_phandle(dev->parent->of_node, 210462306a36Sopenharmony_ci nc->base.caps->ebi_csa_regmap_name, 0); 210562306a36Sopenharmony_ci if (!np) 210662306a36Sopenharmony_ci return 0; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci match = of_match_node(atmel_ebi_csa_regmap_of_ids, np); 210962306a36Sopenharmony_ci if (!match) { 211062306a36Sopenharmony_ci of_node_put(np); 211162306a36Sopenharmony_ci return 0; 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci nc->ebi_csa_regmap = syscon_node_to_regmap(np); 211562306a36Sopenharmony_ci of_node_put(np); 211662306a36Sopenharmony_ci if (IS_ERR(nc->ebi_csa_regmap)) { 211762306a36Sopenharmony_ci ret = PTR_ERR(nc->ebi_csa_regmap); 211862306a36Sopenharmony_ci dev_err(dev, "Could not get EBICSA regmap (err = %d)\n", ret); 211962306a36Sopenharmony_ci return ret; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci nc->ebi_csa = (struct atmel_smc_nand_ebi_csa_cfg *)match->data; 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci /* 212562306a36Sopenharmony_ci * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1 212662306a36Sopenharmony_ci * add 4 to ->ebi_csa->offs. 212762306a36Sopenharmony_ci */ 212862306a36Sopenharmony_ci if (of_device_is_compatible(dev->parent->of_node, 212962306a36Sopenharmony_ci "atmel,at91sam9263-ebi1")) 213062306a36Sopenharmony_ci nc->ebi_csa->offs += 4; 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci return 0; 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cistatic int 213662306a36Sopenharmony_ciatmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) 213762306a36Sopenharmony_ci{ 213862306a36Sopenharmony_ci struct regmap_config regmap_conf = { 213962306a36Sopenharmony_ci .reg_bits = 32, 214062306a36Sopenharmony_ci .val_bits = 32, 214162306a36Sopenharmony_ci .reg_stride = 4, 214262306a36Sopenharmony_ci }; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci struct device *dev = nc->base.dev; 214562306a36Sopenharmony_ci struct device_node *nand_np, *nfc_np; 214662306a36Sopenharmony_ci void __iomem *iomem; 214762306a36Sopenharmony_ci struct resource res; 214862306a36Sopenharmony_ci int ret; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci nand_np = dev->of_node; 215162306a36Sopenharmony_ci nfc_np = of_get_compatible_child(dev->of_node, "atmel,sama5d3-nfc"); 215262306a36Sopenharmony_ci if (!nfc_np) { 215362306a36Sopenharmony_ci dev_err(dev, "Could not find device node for sama5d3-nfc\n"); 215462306a36Sopenharmony_ci return -ENODEV; 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci nc->clk = of_clk_get(nfc_np, 0); 215862306a36Sopenharmony_ci if (IS_ERR(nc->clk)) { 215962306a36Sopenharmony_ci ret = PTR_ERR(nc->clk); 216062306a36Sopenharmony_ci dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n", 216162306a36Sopenharmony_ci ret); 216262306a36Sopenharmony_ci goto out; 216362306a36Sopenharmony_ci } 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci ret = clk_prepare_enable(nc->clk); 216662306a36Sopenharmony_ci if (ret) { 216762306a36Sopenharmony_ci dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n", 216862306a36Sopenharmony_ci ret); 216962306a36Sopenharmony_ci goto out; 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci nc->irq = of_irq_get(nand_np, 0); 217362306a36Sopenharmony_ci if (nc->irq <= 0) { 217462306a36Sopenharmony_ci ret = nc->irq ?: -ENXIO; 217562306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 217662306a36Sopenharmony_ci dev_err(dev, "Failed to get IRQ number (err = %d)\n", 217762306a36Sopenharmony_ci ret); 217862306a36Sopenharmony_ci goto out; 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci ret = of_address_to_resource(nfc_np, 0, &res); 218262306a36Sopenharmony_ci if (ret) { 218362306a36Sopenharmony_ci dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n", 218462306a36Sopenharmony_ci ret); 218562306a36Sopenharmony_ci goto out; 218662306a36Sopenharmony_ci } 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci iomem = devm_ioremap_resource(dev, &res); 218962306a36Sopenharmony_ci if (IS_ERR(iomem)) { 219062306a36Sopenharmony_ci ret = PTR_ERR(iomem); 219162306a36Sopenharmony_ci goto out; 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci regmap_conf.name = "nfc-io"; 219562306a36Sopenharmony_ci regmap_conf.max_register = resource_size(&res) - 4; 219662306a36Sopenharmony_ci nc->io = devm_regmap_init_mmio(dev, iomem, ®map_conf); 219762306a36Sopenharmony_ci if (IS_ERR(nc->io)) { 219862306a36Sopenharmony_ci ret = PTR_ERR(nc->io); 219962306a36Sopenharmony_ci dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", 220062306a36Sopenharmony_ci ret); 220162306a36Sopenharmony_ci goto out; 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci ret = of_address_to_resource(nfc_np, 1, &res); 220562306a36Sopenharmony_ci if (ret) { 220662306a36Sopenharmony_ci dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n", 220762306a36Sopenharmony_ci ret); 220862306a36Sopenharmony_ci goto out; 220962306a36Sopenharmony_ci } 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci iomem = devm_ioremap_resource(dev, &res); 221262306a36Sopenharmony_ci if (IS_ERR(iomem)) { 221362306a36Sopenharmony_ci ret = PTR_ERR(iomem); 221462306a36Sopenharmony_ci goto out; 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci regmap_conf.name = "smc"; 221862306a36Sopenharmony_ci regmap_conf.max_register = resource_size(&res) - 4; 221962306a36Sopenharmony_ci nc->base.smc = devm_regmap_init_mmio(dev, iomem, ®map_conf); 222062306a36Sopenharmony_ci if (IS_ERR(nc->base.smc)) { 222162306a36Sopenharmony_ci ret = PTR_ERR(nc->base.smc); 222262306a36Sopenharmony_ci dev_err(dev, "Could not create NFC IO regmap (err = %d)\n", 222362306a36Sopenharmony_ci ret); 222462306a36Sopenharmony_ci goto out; 222562306a36Sopenharmony_ci } 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_ci ret = of_address_to_resource(nfc_np, 2, &res); 222862306a36Sopenharmony_ci if (ret) { 222962306a36Sopenharmony_ci dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n", 223062306a36Sopenharmony_ci ret); 223162306a36Sopenharmony_ci goto out; 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci nc->sram.virt = devm_ioremap_resource(dev, &res); 223562306a36Sopenharmony_ci if (IS_ERR(nc->sram.virt)) { 223662306a36Sopenharmony_ci ret = PTR_ERR(nc->sram.virt); 223762306a36Sopenharmony_ci goto out; 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci nc->sram.dma = res.start; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ciout: 224362306a36Sopenharmony_ci of_node_put(nfc_np); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci return ret; 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_cistatic int 224962306a36Sopenharmony_ciatmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc) 225062306a36Sopenharmony_ci{ 225162306a36Sopenharmony_ci struct device *dev = nc->base.dev; 225262306a36Sopenharmony_ci struct device_node *np; 225362306a36Sopenharmony_ci int ret; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0); 225662306a36Sopenharmony_ci if (!np) { 225762306a36Sopenharmony_ci dev_err(dev, "Missing or invalid atmel,smc property\n"); 225862306a36Sopenharmony_ci return -EINVAL; 225962306a36Sopenharmony_ci } 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci nc->hsmc_layout = atmel_hsmc_get_reg_layout(np); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci nc->irq = of_irq_get(np, 0); 226462306a36Sopenharmony_ci of_node_put(np); 226562306a36Sopenharmony_ci if (nc->irq <= 0) { 226662306a36Sopenharmony_ci ret = nc->irq ?: -ENXIO; 226762306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 226862306a36Sopenharmony_ci dev_err(dev, "Failed to get IRQ number (err = %d)\n", 226962306a36Sopenharmony_ci ret); 227062306a36Sopenharmony_ci return ret; 227162306a36Sopenharmony_ci } 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0); 227462306a36Sopenharmony_ci if (!np) { 227562306a36Sopenharmony_ci dev_err(dev, "Missing or invalid atmel,nfc-io property\n"); 227662306a36Sopenharmony_ci return -EINVAL; 227762306a36Sopenharmony_ci } 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci nc->io = syscon_node_to_regmap(np); 228062306a36Sopenharmony_ci of_node_put(np); 228162306a36Sopenharmony_ci if (IS_ERR(nc->io)) { 228262306a36Sopenharmony_ci ret = PTR_ERR(nc->io); 228362306a36Sopenharmony_ci dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret); 228462306a36Sopenharmony_ci return ret; 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node, 228862306a36Sopenharmony_ci "atmel,nfc-sram", 0); 228962306a36Sopenharmony_ci if (!nc->sram.pool) { 229062306a36Sopenharmony_ci dev_err(nc->base.dev, "Missing SRAM\n"); 229162306a36Sopenharmony_ci return -ENOMEM; 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci 229462306a36Sopenharmony_ci nc->sram.virt = (void __iomem *)gen_pool_dma_alloc(nc->sram.pool, 229562306a36Sopenharmony_ci ATMEL_NFC_SRAM_SIZE, 229662306a36Sopenharmony_ci &nc->sram.dma); 229762306a36Sopenharmony_ci if (!nc->sram.virt) { 229862306a36Sopenharmony_ci dev_err(nc->base.dev, 229962306a36Sopenharmony_ci "Could not allocate memory from the NFC SRAM pool\n"); 230062306a36Sopenharmony_ci return -ENOMEM; 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci return 0; 230462306a36Sopenharmony_ci} 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_cistatic int 230762306a36Sopenharmony_ciatmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc) 230862306a36Sopenharmony_ci{ 230962306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *hsmc_nc; 231062306a36Sopenharmony_ci int ret; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci ret = atmel_nand_controller_remove_nands(nc); 231362306a36Sopenharmony_ci if (ret) 231462306a36Sopenharmony_ci return ret; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base); 231762306a36Sopenharmony_ci regmap_write(hsmc_nc->base.smc, ATMEL_HSMC_NFC_CTRL, 231862306a36Sopenharmony_ci ATMEL_HSMC_NFC_CTRL_DIS); 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci if (hsmc_nc->sram.pool) 232162306a36Sopenharmony_ci gen_pool_free(hsmc_nc->sram.pool, 232262306a36Sopenharmony_ci (unsigned long)hsmc_nc->sram.virt, 232362306a36Sopenharmony_ci ATMEL_NFC_SRAM_SIZE); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci if (hsmc_nc->clk) { 232662306a36Sopenharmony_ci clk_disable_unprepare(hsmc_nc->clk); 232762306a36Sopenharmony_ci clk_put(hsmc_nc->clk); 232862306a36Sopenharmony_ci } 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci atmel_nand_controller_cleanup(nc); 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci return 0; 233362306a36Sopenharmony_ci} 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_cistatic int atmel_hsmc_nand_controller_probe(struct platform_device *pdev, 233662306a36Sopenharmony_ci const struct atmel_nand_controller_caps *caps) 233762306a36Sopenharmony_ci{ 233862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 233962306a36Sopenharmony_ci struct atmel_hsmc_nand_controller *nc; 234062306a36Sopenharmony_ci int ret; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); 234362306a36Sopenharmony_ci if (!nc) 234462306a36Sopenharmony_ci return -ENOMEM; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci ret = atmel_nand_controller_init(&nc->base, pdev, caps); 234762306a36Sopenharmony_ci if (ret) 234862306a36Sopenharmony_ci return ret; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci if (caps->legacy_of_bindings) 235162306a36Sopenharmony_ci ret = atmel_hsmc_nand_controller_legacy_init(nc); 235262306a36Sopenharmony_ci else 235362306a36Sopenharmony_ci ret = atmel_hsmc_nand_controller_init(nc); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci if (ret) 235662306a36Sopenharmony_ci return ret; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci /* Make sure all irqs are masked before registering our IRQ handler. */ 235962306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff); 236062306a36Sopenharmony_ci ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt, 236162306a36Sopenharmony_ci IRQF_SHARED, "nfc", nc); 236262306a36Sopenharmony_ci if (ret) { 236362306a36Sopenharmony_ci dev_err(dev, 236462306a36Sopenharmony_ci "Could not get register NFC interrupt handler (err = %d)\n", 236562306a36Sopenharmony_ci ret); 236662306a36Sopenharmony_ci goto err; 236762306a36Sopenharmony_ci } 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci /* Initial NFC configuration. */ 237062306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG, 237162306a36Sopenharmony_ci ATMEL_HSMC_NFC_CFG_DTO_MAX); 237262306a36Sopenharmony_ci regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL, 237362306a36Sopenharmony_ci ATMEL_HSMC_NFC_CTRL_EN); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci ret = atmel_nand_controller_add_nands(&nc->base); 237662306a36Sopenharmony_ci if (ret) 237762306a36Sopenharmony_ci goto err; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci return 0; 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_cierr: 238262306a36Sopenharmony_ci atmel_hsmc_nand_controller_remove(&nc->base); 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci return ret; 238562306a36Sopenharmony_ci} 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_cistatic const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = { 238862306a36Sopenharmony_ci .probe = atmel_hsmc_nand_controller_probe, 238962306a36Sopenharmony_ci .remove = atmel_hsmc_nand_controller_remove, 239062306a36Sopenharmony_ci .ecc_init = atmel_hsmc_nand_ecc_init, 239162306a36Sopenharmony_ci .nand_init = atmel_nand_init, 239262306a36Sopenharmony_ci .setup_interface = atmel_hsmc_nand_setup_interface, 239362306a36Sopenharmony_ci .exec_op = atmel_hsmc_nand_exec_op, 239462306a36Sopenharmony_ci}; 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sama5_nc_caps = { 239762306a36Sopenharmony_ci .has_dma = true, 239862306a36Sopenharmony_ci .ale_offs = BIT(21), 239962306a36Sopenharmony_ci .cle_offs = BIT(22), 240062306a36Sopenharmony_ci .ops = &atmel_hsmc_nc_ops, 240162306a36Sopenharmony_ci}; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci/* Only used to parse old bindings. */ 240462306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sama5_nand_caps = { 240562306a36Sopenharmony_ci .has_dma = true, 240662306a36Sopenharmony_ci .ale_offs = BIT(21), 240762306a36Sopenharmony_ci .cle_offs = BIT(22), 240862306a36Sopenharmony_ci .ops = &atmel_hsmc_nc_ops, 240962306a36Sopenharmony_ci .legacy_of_bindings = true, 241062306a36Sopenharmony_ci}; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_cistatic int atmel_smc_nand_controller_probe(struct platform_device *pdev, 241362306a36Sopenharmony_ci const struct atmel_nand_controller_caps *caps) 241462306a36Sopenharmony_ci{ 241562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 241662306a36Sopenharmony_ci struct atmel_smc_nand_controller *nc; 241762306a36Sopenharmony_ci int ret; 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL); 242062306a36Sopenharmony_ci if (!nc) 242162306a36Sopenharmony_ci return -ENOMEM; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci ret = atmel_nand_controller_init(&nc->base, pdev, caps); 242462306a36Sopenharmony_ci if (ret) 242562306a36Sopenharmony_ci return ret; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci ret = atmel_smc_nand_controller_init(nc); 242862306a36Sopenharmony_ci if (ret) 242962306a36Sopenharmony_ci return ret; 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci return atmel_nand_controller_add_nands(&nc->base); 243262306a36Sopenharmony_ci} 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_cistatic int 243562306a36Sopenharmony_ciatmel_smc_nand_controller_remove(struct atmel_nand_controller *nc) 243662306a36Sopenharmony_ci{ 243762306a36Sopenharmony_ci int ret; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci ret = atmel_nand_controller_remove_nands(nc); 244062306a36Sopenharmony_ci if (ret) 244162306a36Sopenharmony_ci return ret; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci atmel_nand_controller_cleanup(nc); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci return 0; 244662306a36Sopenharmony_ci} 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci/* 244962306a36Sopenharmony_ci * The SMC reg layout of at91rm9200 is completely different which prevents us 245062306a36Sopenharmony_ci * from re-using atmel_smc_nand_setup_interface() for the 245162306a36Sopenharmony_ci * ->setup_interface() hook. 245262306a36Sopenharmony_ci * At this point, there's no support for the at91rm9200 SMC IP, so we leave 245362306a36Sopenharmony_ci * ->setup_interface() unassigned. 245462306a36Sopenharmony_ci */ 245562306a36Sopenharmony_cistatic const struct atmel_nand_controller_ops at91rm9200_nc_ops = { 245662306a36Sopenharmony_ci .probe = atmel_smc_nand_controller_probe, 245762306a36Sopenharmony_ci .remove = atmel_smc_nand_controller_remove, 245862306a36Sopenharmony_ci .ecc_init = atmel_nand_ecc_init, 245962306a36Sopenharmony_ci .nand_init = atmel_smc_nand_init, 246062306a36Sopenharmony_ci .exec_op = atmel_smc_nand_exec_op, 246162306a36Sopenharmony_ci}; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = { 246462306a36Sopenharmony_ci .ale_offs = BIT(21), 246562306a36Sopenharmony_ci .cle_offs = BIT(22), 246662306a36Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 246762306a36Sopenharmony_ci .ops = &at91rm9200_nc_ops, 246862306a36Sopenharmony_ci}; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_cistatic const struct atmel_nand_controller_ops atmel_smc_nc_ops = { 247162306a36Sopenharmony_ci .probe = atmel_smc_nand_controller_probe, 247262306a36Sopenharmony_ci .remove = atmel_smc_nand_controller_remove, 247362306a36Sopenharmony_ci .ecc_init = atmel_nand_ecc_init, 247462306a36Sopenharmony_ci .nand_init = atmel_smc_nand_init, 247562306a36Sopenharmony_ci .setup_interface = atmel_smc_nand_setup_interface, 247662306a36Sopenharmony_ci .exec_op = atmel_smc_nand_exec_op, 247762306a36Sopenharmony_ci}; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9260_nc_caps = { 248062306a36Sopenharmony_ci .ale_offs = BIT(21), 248162306a36Sopenharmony_ci .cle_offs = BIT(22), 248262306a36Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 248362306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 248462306a36Sopenharmony_ci}; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = { 248762306a36Sopenharmony_ci .ale_offs = BIT(22), 248862306a36Sopenharmony_ci .cle_offs = BIT(21), 248962306a36Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 249062306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 249162306a36Sopenharmony_ci}; 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = { 249462306a36Sopenharmony_ci .has_dma = true, 249562306a36Sopenharmony_ci .ale_offs = BIT(21), 249662306a36Sopenharmony_ci .cle_offs = BIT(22), 249762306a36Sopenharmony_ci .ebi_csa_regmap_name = "atmel,matrix", 249862306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 249962306a36Sopenharmony_ci}; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps microchip_sam9x60_nc_caps = { 250262306a36Sopenharmony_ci .has_dma = true, 250362306a36Sopenharmony_ci .ale_offs = BIT(21), 250462306a36Sopenharmony_ci .cle_offs = BIT(22), 250562306a36Sopenharmony_ci .ebi_csa_regmap_name = "microchip,sfr", 250662306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 250762306a36Sopenharmony_ci}; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci/* Only used to parse old bindings. */ 251062306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = { 251162306a36Sopenharmony_ci .ale_offs = BIT(21), 251262306a36Sopenharmony_ci .cle_offs = BIT(22), 251362306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 251462306a36Sopenharmony_ci .legacy_of_bindings = true, 251562306a36Sopenharmony_ci}; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = { 251862306a36Sopenharmony_ci .ale_offs = BIT(22), 251962306a36Sopenharmony_ci .cle_offs = BIT(21), 252062306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 252162306a36Sopenharmony_ci .legacy_of_bindings = true, 252262306a36Sopenharmony_ci}; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_cistatic const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = { 252562306a36Sopenharmony_ci .has_dma = true, 252662306a36Sopenharmony_ci .ale_offs = BIT(21), 252762306a36Sopenharmony_ci .cle_offs = BIT(22), 252862306a36Sopenharmony_ci .ops = &atmel_smc_nc_ops, 252962306a36Sopenharmony_ci .legacy_of_bindings = true, 253062306a36Sopenharmony_ci}; 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_cistatic const struct of_device_id atmel_nand_controller_of_ids[] = { 253362306a36Sopenharmony_ci { 253462306a36Sopenharmony_ci .compatible = "atmel,at91rm9200-nand-controller", 253562306a36Sopenharmony_ci .data = &atmel_rm9200_nc_caps, 253662306a36Sopenharmony_ci }, 253762306a36Sopenharmony_ci { 253862306a36Sopenharmony_ci .compatible = "atmel,at91sam9260-nand-controller", 253962306a36Sopenharmony_ci .data = &atmel_sam9260_nc_caps, 254062306a36Sopenharmony_ci }, 254162306a36Sopenharmony_ci { 254262306a36Sopenharmony_ci .compatible = "atmel,at91sam9261-nand-controller", 254362306a36Sopenharmony_ci .data = &atmel_sam9261_nc_caps, 254462306a36Sopenharmony_ci }, 254562306a36Sopenharmony_ci { 254662306a36Sopenharmony_ci .compatible = "atmel,at91sam9g45-nand-controller", 254762306a36Sopenharmony_ci .data = &atmel_sam9g45_nc_caps, 254862306a36Sopenharmony_ci }, 254962306a36Sopenharmony_ci { 255062306a36Sopenharmony_ci .compatible = "atmel,sama5d3-nand-controller", 255162306a36Sopenharmony_ci .data = &atmel_sama5_nc_caps, 255262306a36Sopenharmony_ci }, 255362306a36Sopenharmony_ci { 255462306a36Sopenharmony_ci .compatible = "microchip,sam9x60-nand-controller", 255562306a36Sopenharmony_ci .data = µchip_sam9x60_nc_caps, 255662306a36Sopenharmony_ci }, 255762306a36Sopenharmony_ci /* Support for old/deprecated bindings: */ 255862306a36Sopenharmony_ci { 255962306a36Sopenharmony_ci .compatible = "atmel,at91rm9200-nand", 256062306a36Sopenharmony_ci .data = &atmel_rm9200_nand_caps, 256162306a36Sopenharmony_ci }, 256262306a36Sopenharmony_ci { 256362306a36Sopenharmony_ci .compatible = "atmel,sama5d4-nand", 256462306a36Sopenharmony_ci .data = &atmel_rm9200_nand_caps, 256562306a36Sopenharmony_ci }, 256662306a36Sopenharmony_ci { 256762306a36Sopenharmony_ci .compatible = "atmel,sama5d2-nand", 256862306a36Sopenharmony_ci .data = &atmel_rm9200_nand_caps, 256962306a36Sopenharmony_ci }, 257062306a36Sopenharmony_ci { /* sentinel */ }, 257162306a36Sopenharmony_ci}; 257262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids); 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_cistatic int atmel_nand_controller_probe(struct platform_device *pdev) 257562306a36Sopenharmony_ci{ 257662306a36Sopenharmony_ci const struct atmel_nand_controller_caps *caps; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci if (pdev->id_entry) 257962306a36Sopenharmony_ci caps = (void *)pdev->id_entry->driver_data; 258062306a36Sopenharmony_ci else 258162306a36Sopenharmony_ci caps = of_device_get_match_data(&pdev->dev); 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci if (!caps) { 258462306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not retrieve NFC caps\n"); 258562306a36Sopenharmony_ci return -EINVAL; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci if (caps->legacy_of_bindings) { 258962306a36Sopenharmony_ci struct device_node *nfc_node; 259062306a36Sopenharmony_ci u32 ale_offs = 21; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci /* 259362306a36Sopenharmony_ci * If we are parsing legacy DT props and the DT contains a 259462306a36Sopenharmony_ci * valid NFC node, forward the request to the sama5 logic. 259562306a36Sopenharmony_ci */ 259662306a36Sopenharmony_ci nfc_node = of_get_compatible_child(pdev->dev.of_node, 259762306a36Sopenharmony_ci "atmel,sama5d3-nfc"); 259862306a36Sopenharmony_ci if (nfc_node) { 259962306a36Sopenharmony_ci caps = &atmel_sama5_nand_caps; 260062306a36Sopenharmony_ci of_node_put(nfc_node); 260162306a36Sopenharmony_ci } 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci /* 260462306a36Sopenharmony_ci * Even if the compatible says we are dealing with an 260562306a36Sopenharmony_ci * at91rm9200 controller, the atmel,nand-has-dma specify that 260662306a36Sopenharmony_ci * this controller supports DMA, which means we are in fact 260762306a36Sopenharmony_ci * dealing with an at91sam9g45+ controller. 260862306a36Sopenharmony_ci */ 260962306a36Sopenharmony_ci if (!caps->has_dma && 261062306a36Sopenharmony_ci of_property_read_bool(pdev->dev.of_node, 261162306a36Sopenharmony_ci "atmel,nand-has-dma")) 261262306a36Sopenharmony_ci caps = &atmel_sam9g45_nand_caps; 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci /* 261562306a36Sopenharmony_ci * All SoCs except the at91sam9261 are assigning ALE to A21 and 261662306a36Sopenharmony_ci * CLE to A22. If atmel,nand-addr-offset != 21 this means we're 261762306a36Sopenharmony_ci * actually dealing with an at91sam9261 controller. 261862306a36Sopenharmony_ci */ 261962306a36Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, 262062306a36Sopenharmony_ci "atmel,nand-addr-offset", &ale_offs); 262162306a36Sopenharmony_ci if (ale_offs != 21) 262262306a36Sopenharmony_ci caps = &atmel_sam9261_nand_caps; 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci return caps->ops->probe(pdev, caps); 262662306a36Sopenharmony_ci} 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_cistatic void atmel_nand_controller_remove(struct platform_device *pdev) 262962306a36Sopenharmony_ci{ 263062306a36Sopenharmony_ci struct atmel_nand_controller *nc = platform_get_drvdata(pdev); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci WARN_ON(nc->caps->ops->remove(nc)); 263362306a36Sopenharmony_ci} 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_cistatic __maybe_unused int atmel_nand_controller_resume(struct device *dev) 263662306a36Sopenharmony_ci{ 263762306a36Sopenharmony_ci struct atmel_nand_controller *nc = dev_get_drvdata(dev); 263862306a36Sopenharmony_ci struct atmel_nand *nand; 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci if (nc->pmecc) 264162306a36Sopenharmony_ci atmel_pmecc_reset(nc->pmecc); 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci list_for_each_entry(nand, &nc->chips, node) { 264462306a36Sopenharmony_ci int i; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci for (i = 0; i < nand->numcs; i++) 264762306a36Sopenharmony_ci nand_reset(&nand->base, i); 264862306a36Sopenharmony_ci } 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci return 0; 265162306a36Sopenharmony_ci} 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_nand_controller_pm_ops, NULL, 265462306a36Sopenharmony_ci atmel_nand_controller_resume); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_cistatic struct platform_driver atmel_nand_controller_driver = { 265762306a36Sopenharmony_ci .driver = { 265862306a36Sopenharmony_ci .name = "atmel-nand-controller", 265962306a36Sopenharmony_ci .of_match_table = atmel_nand_controller_of_ids, 266062306a36Sopenharmony_ci .pm = &atmel_nand_controller_pm_ops, 266162306a36Sopenharmony_ci }, 266262306a36Sopenharmony_ci .probe = atmel_nand_controller_probe, 266362306a36Sopenharmony_ci .remove_new = atmel_nand_controller_remove, 266462306a36Sopenharmony_ci}; 266562306a36Sopenharmony_cimodule_platform_driver(atmel_nand_controller_driver); 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 266862306a36Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 266962306a36Sopenharmony_ciMODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs"); 267062306a36Sopenharmony_ciMODULE_ALIAS("platform:atmel-nand-controller"); 2671