18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2018 Stefan Agner <stefan@agner.ch> 48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Lucas Stach <dev@lynxeye.de> 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/completion.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/reset.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define COMMAND 0x00 238c2ecf20Sopenharmony_ci#define COMMAND_GO BIT(31) 248c2ecf20Sopenharmony_ci#define COMMAND_CLE BIT(30) 258c2ecf20Sopenharmony_ci#define COMMAND_ALE BIT(29) 268c2ecf20Sopenharmony_ci#define COMMAND_PIO BIT(28) 278c2ecf20Sopenharmony_ci#define COMMAND_TX BIT(27) 288c2ecf20Sopenharmony_ci#define COMMAND_RX BIT(26) 298c2ecf20Sopenharmony_ci#define COMMAND_SEC_CMD BIT(25) 308c2ecf20Sopenharmony_ci#define COMMAND_AFT_DAT BIT(24) 318c2ecf20Sopenharmony_ci#define COMMAND_TRANS_SIZE(size) ((((size) - 1) & 0xf) << 20) 328c2ecf20Sopenharmony_ci#define COMMAND_A_VALID BIT(19) 338c2ecf20Sopenharmony_ci#define COMMAND_B_VALID BIT(18) 348c2ecf20Sopenharmony_ci#define COMMAND_RD_STATUS_CHK BIT(17) 358c2ecf20Sopenharmony_ci#define COMMAND_RBSY_CHK BIT(16) 368c2ecf20Sopenharmony_ci#define COMMAND_CE(x) BIT(8 + ((x) & 0x7)) 378c2ecf20Sopenharmony_ci#define COMMAND_CLE_SIZE(size) ((((size) - 1) & 0x3) << 4) 388c2ecf20Sopenharmony_ci#define COMMAND_ALE_SIZE(size) ((((size) - 1) & 0xf) << 0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define STATUS 0x04 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define ISR 0x08 438c2ecf20Sopenharmony_ci#define ISR_CORRFAIL_ERR BIT(24) 448c2ecf20Sopenharmony_ci#define ISR_UND BIT(7) 458c2ecf20Sopenharmony_ci#define ISR_OVR BIT(6) 468c2ecf20Sopenharmony_ci#define ISR_CMD_DONE BIT(5) 478c2ecf20Sopenharmony_ci#define ISR_ECC_ERR BIT(4) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define IER 0x0c 508c2ecf20Sopenharmony_ci#define IER_ERR_TRIG_VAL(x) (((x) & 0xf) << 16) 518c2ecf20Sopenharmony_ci#define IER_UND BIT(7) 528c2ecf20Sopenharmony_ci#define IER_OVR BIT(6) 538c2ecf20Sopenharmony_ci#define IER_CMD_DONE BIT(5) 548c2ecf20Sopenharmony_ci#define IER_ECC_ERR BIT(4) 558c2ecf20Sopenharmony_ci#define IER_GIE BIT(0) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define CONFIG 0x10 588c2ecf20Sopenharmony_ci#define CONFIG_HW_ECC BIT(31) 598c2ecf20Sopenharmony_ci#define CONFIG_ECC_SEL BIT(30) 608c2ecf20Sopenharmony_ci#define CONFIG_ERR_COR BIT(29) 618c2ecf20Sopenharmony_ci#define CONFIG_PIPE_EN BIT(28) 628c2ecf20Sopenharmony_ci#define CONFIG_TVAL_4 (0 << 24) 638c2ecf20Sopenharmony_ci#define CONFIG_TVAL_6 (1 << 24) 648c2ecf20Sopenharmony_ci#define CONFIG_TVAL_8 (2 << 24) 658c2ecf20Sopenharmony_ci#define CONFIG_SKIP_SPARE BIT(23) 668c2ecf20Sopenharmony_ci#define CONFIG_BUS_WIDTH_16 BIT(21) 678c2ecf20Sopenharmony_ci#define CONFIG_COM_BSY BIT(20) 688c2ecf20Sopenharmony_ci#define CONFIG_PS_256 (0 << 16) 698c2ecf20Sopenharmony_ci#define CONFIG_PS_512 (1 << 16) 708c2ecf20Sopenharmony_ci#define CONFIG_PS_1024 (2 << 16) 718c2ecf20Sopenharmony_ci#define CONFIG_PS_2048 (3 << 16) 728c2ecf20Sopenharmony_ci#define CONFIG_PS_4096 (4 << 16) 738c2ecf20Sopenharmony_ci#define CONFIG_SKIP_SPARE_SIZE_4 (0 << 14) 748c2ecf20Sopenharmony_ci#define CONFIG_SKIP_SPARE_SIZE_8 (1 << 14) 758c2ecf20Sopenharmony_ci#define CONFIG_SKIP_SPARE_SIZE_12 (2 << 14) 768c2ecf20Sopenharmony_ci#define CONFIG_SKIP_SPARE_SIZE_16 (3 << 14) 778c2ecf20Sopenharmony_ci#define CONFIG_TAG_BYTE_SIZE(x) ((x) & 0xff) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define TIMING_1 0x14 808c2ecf20Sopenharmony_ci#define TIMING_TRP_RESP(x) (((x) & 0xf) << 28) 818c2ecf20Sopenharmony_ci#define TIMING_TWB(x) (((x) & 0xf) << 24) 828c2ecf20Sopenharmony_ci#define TIMING_TCR_TAR_TRR(x) (((x) & 0xf) << 20) 838c2ecf20Sopenharmony_ci#define TIMING_TWHR(x) (((x) & 0xf) << 16) 848c2ecf20Sopenharmony_ci#define TIMING_TCS(x) (((x) & 0x3) << 14) 858c2ecf20Sopenharmony_ci#define TIMING_TWH(x) (((x) & 0x3) << 12) 868c2ecf20Sopenharmony_ci#define TIMING_TWP(x) (((x) & 0xf) << 8) 878c2ecf20Sopenharmony_ci#define TIMING_TRH(x) (((x) & 0x3) << 4) 888c2ecf20Sopenharmony_ci#define TIMING_TRP(x) (((x) & 0xf) << 0) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define RESP 0x18 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define TIMING_2 0x1c 938c2ecf20Sopenharmony_ci#define TIMING_TADL(x) ((x) & 0xf) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define CMD_REG1 0x20 968c2ecf20Sopenharmony_ci#define CMD_REG2 0x24 978c2ecf20Sopenharmony_ci#define ADDR_REG1 0x28 988c2ecf20Sopenharmony_ci#define ADDR_REG2 0x2c 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define DMA_MST_CTRL 0x30 1018c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_GO BIT(31) 1028c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_IN (0 << 30) 1038c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_OUT BIT(30) 1048c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_PERF_EN BIT(29) 1058c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_IE_DONE BIT(28) 1068c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_REUSE BIT(27) 1078c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_BURST_1 (2 << 24) 1088c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_BURST_4 (3 << 24) 1098c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_BURST_8 (4 << 24) 1108c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_BURST_16 (5 << 24) 1118c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_IS_DONE BIT(20) 1128c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_EN_A BIT(2) 1138c2ecf20Sopenharmony_ci#define DMA_MST_CTRL_EN_B BIT(1) 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define DMA_CFG_A 0x34 1168c2ecf20Sopenharmony_ci#define DMA_CFG_B 0x38 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci#define FIFO_CTRL 0x3c 1198c2ecf20Sopenharmony_ci#define FIFO_CTRL_CLR_ALL BIT(3) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define DATA_PTR 0x40 1228c2ecf20Sopenharmony_ci#define TAG_PTR 0x44 1238c2ecf20Sopenharmony_ci#define ECC_PTR 0x48 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define DEC_STATUS 0x4c 1268c2ecf20Sopenharmony_ci#define DEC_STATUS_A_ECC_FAIL BIT(1) 1278c2ecf20Sopenharmony_ci#define DEC_STATUS_ERR_COUNT_MASK 0x00ff0000 1288c2ecf20Sopenharmony_ci#define DEC_STATUS_ERR_COUNT_SHIFT 16 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define HWSTATUS_CMD 0x50 1318c2ecf20Sopenharmony_ci#define HWSTATUS_MASK 0x54 1328c2ecf20Sopenharmony_ci#define HWSTATUS_RDSTATUS_MASK(x) (((x) & 0xff) << 24) 1338c2ecf20Sopenharmony_ci#define HWSTATUS_RDSTATUS_VALUE(x) (((x) & 0xff) << 16) 1348c2ecf20Sopenharmony_ci#define HWSTATUS_RBSY_MASK(x) (((x) & 0xff) << 8) 1358c2ecf20Sopenharmony_ci#define HWSTATUS_RBSY_VALUE(x) (((x) & 0xff) << 0) 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define BCH_CONFIG 0xcc 1388c2ecf20Sopenharmony_ci#define BCH_ENABLE BIT(0) 1398c2ecf20Sopenharmony_ci#define BCH_TVAL_4 (0 << 4) 1408c2ecf20Sopenharmony_ci#define BCH_TVAL_8 (1 << 4) 1418c2ecf20Sopenharmony_ci#define BCH_TVAL_14 (2 << 4) 1428c2ecf20Sopenharmony_ci#define BCH_TVAL_16 (3 << 4) 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define DEC_STAT_RESULT 0xd0 1458c2ecf20Sopenharmony_ci#define DEC_STAT_BUF 0xd4 1468c2ecf20Sopenharmony_ci#define DEC_STAT_BUF_FAIL_SEC_FLAG_MASK 0xff000000 1478c2ecf20Sopenharmony_ci#define DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT 24 1488c2ecf20Sopenharmony_ci#define DEC_STAT_BUF_CORR_SEC_FLAG_MASK 0x00ff0000 1498c2ecf20Sopenharmony_ci#define DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT 16 1508c2ecf20Sopenharmony_ci#define DEC_STAT_BUF_MAX_CORR_CNT_MASK 0x00001f00 1518c2ecf20Sopenharmony_ci#define DEC_STAT_BUF_MAX_CORR_CNT_SHIFT 8 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#define OFFSET(val, off) ((val) < (off) ? 0 : (val) - (off)) 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci#define SKIP_SPARE_BYTES 4 1568c2ecf20Sopenharmony_ci#define BITS_PER_STEP_RS 18 1578c2ecf20Sopenharmony_ci#define BITS_PER_STEP_BCH 13 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define INT_MASK (IER_UND | IER_OVR | IER_CMD_DONE | IER_GIE) 1608c2ecf20Sopenharmony_ci#define HWSTATUS_CMD_DEFAULT NAND_STATUS_READY 1618c2ecf20Sopenharmony_ci#define HWSTATUS_MASK_DEFAULT (HWSTATUS_RDSTATUS_MASK(1) | \ 1628c2ecf20Sopenharmony_ci HWSTATUS_RDSTATUS_VALUE(0) | \ 1638c2ecf20Sopenharmony_ci HWSTATUS_RBSY_MASK(NAND_STATUS_READY) | \ 1648c2ecf20Sopenharmony_ci HWSTATUS_RBSY_VALUE(NAND_STATUS_READY)) 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistruct tegra_nand_controller { 1678c2ecf20Sopenharmony_ci struct nand_controller controller; 1688c2ecf20Sopenharmony_ci struct device *dev; 1698c2ecf20Sopenharmony_ci void __iomem *regs; 1708c2ecf20Sopenharmony_ci int irq; 1718c2ecf20Sopenharmony_ci struct clk *clk; 1728c2ecf20Sopenharmony_ci struct completion command_complete; 1738c2ecf20Sopenharmony_ci struct completion dma_complete; 1748c2ecf20Sopenharmony_ci bool last_read_error; 1758c2ecf20Sopenharmony_ci int cur_cs; 1768c2ecf20Sopenharmony_ci struct nand_chip *chip; 1778c2ecf20Sopenharmony_ci}; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistruct tegra_nand_chip { 1808c2ecf20Sopenharmony_ci struct nand_chip chip; 1818c2ecf20Sopenharmony_ci struct gpio_desc *wp_gpio; 1828c2ecf20Sopenharmony_ci struct mtd_oob_region ecc; 1838c2ecf20Sopenharmony_ci u32 config; 1848c2ecf20Sopenharmony_ci u32 config_ecc; 1858c2ecf20Sopenharmony_ci u32 bch_config; 1868c2ecf20Sopenharmony_ci int cs[1]; 1878c2ecf20Sopenharmony_ci}; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic inline struct tegra_nand_controller * 1908c2ecf20Sopenharmony_ci to_tegra_ctrl(struct nand_controller *hw_ctrl) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return container_of(hw_ctrl, struct tegra_nand_controller, controller); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic inline struct tegra_nand_chip *to_tegra_chip(struct nand_chip *chip) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci return container_of(chip, struct tegra_nand_chip, chip); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int tegra_nand_ooblayout_rs_ecc(struct mtd_info *mtd, int section, 2018c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 2048c2ecf20Sopenharmony_ci int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_RS * chip->ecc.strength, 2058c2ecf20Sopenharmony_ci BITS_PER_BYTE); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (section > 0) 2088c2ecf20Sopenharmony_ci return -ERANGE; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci oobregion->offset = SKIP_SPARE_BYTES; 2118c2ecf20Sopenharmony_ci oobregion->length = round_up(bytes_per_step * chip->ecc.steps, 4); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int tegra_nand_ooblayout_no_free(struct mtd_info *mtd, int section, 2178c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci return -ERANGE; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops tegra_nand_oob_rs_ops = { 2238c2ecf20Sopenharmony_ci .ecc = tegra_nand_ooblayout_rs_ecc, 2248c2ecf20Sopenharmony_ci .free = tegra_nand_ooblayout_no_free, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int tegra_nand_ooblayout_bch_ecc(struct mtd_info *mtd, int section, 2288c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 2318c2ecf20Sopenharmony_ci int bytes_per_step = DIV_ROUND_UP(BITS_PER_STEP_BCH * chip->ecc.strength, 2328c2ecf20Sopenharmony_ci BITS_PER_BYTE); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (section > 0) 2358c2ecf20Sopenharmony_ci return -ERANGE; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci oobregion->offset = SKIP_SPARE_BYTES; 2388c2ecf20Sopenharmony_ci oobregion->length = round_up(bytes_per_step * chip->ecc.steps, 4); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops tegra_nand_oob_bch_ops = { 2448c2ecf20Sopenharmony_ci .ecc = tegra_nand_ooblayout_bch_ecc, 2458c2ecf20Sopenharmony_ci .free = tegra_nand_ooblayout_no_free, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic irqreturn_t tegra_nand_irq(int irq, void *data) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = data; 2518c2ecf20Sopenharmony_ci u32 isr, dma; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci isr = readl_relaxed(ctrl->regs + ISR); 2548c2ecf20Sopenharmony_ci dma = readl_relaxed(ctrl->regs + DMA_MST_CTRL); 2558c2ecf20Sopenharmony_ci dev_dbg(ctrl->dev, "isr %08x\n", isr); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!isr && !(dma & DMA_MST_CTRL_IS_DONE)) 2588c2ecf20Sopenharmony_ci return IRQ_NONE; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * The bit name is somewhat missleading: This is also set when 2628c2ecf20Sopenharmony_ci * HW ECC was successful. The data sheet states: 2638c2ecf20Sopenharmony_ci * Correctable OR Un-correctable errors occurred in the DMA transfer... 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci if (isr & ISR_CORRFAIL_ERR) 2668c2ecf20Sopenharmony_ci ctrl->last_read_error = true; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (isr & ISR_CMD_DONE) 2698c2ecf20Sopenharmony_ci complete(&ctrl->command_complete); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (isr & ISR_UND) 2728c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "FIFO underrun\n"); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (isr & ISR_OVR) 2758c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "FIFO overrun\n"); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* handle DMA interrupts */ 2788c2ecf20Sopenharmony_ci if (dma & DMA_MST_CTRL_IS_DONE) { 2798c2ecf20Sopenharmony_ci writel_relaxed(dma, ctrl->regs + DMA_MST_CTRL); 2808c2ecf20Sopenharmony_ci complete(&ctrl->dma_complete); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* clear interrupts */ 2848c2ecf20Sopenharmony_ci writel_relaxed(isr, ctrl->regs + ISR); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const char * const tegra_nand_reg_names[] = { 2908c2ecf20Sopenharmony_ci "COMMAND", 2918c2ecf20Sopenharmony_ci "STATUS", 2928c2ecf20Sopenharmony_ci "ISR", 2938c2ecf20Sopenharmony_ci "IER", 2948c2ecf20Sopenharmony_ci "CONFIG", 2958c2ecf20Sopenharmony_ci "TIMING", 2968c2ecf20Sopenharmony_ci NULL, 2978c2ecf20Sopenharmony_ci "TIMING2", 2988c2ecf20Sopenharmony_ci "CMD_REG1", 2998c2ecf20Sopenharmony_ci "CMD_REG2", 3008c2ecf20Sopenharmony_ci "ADDR_REG1", 3018c2ecf20Sopenharmony_ci "ADDR_REG2", 3028c2ecf20Sopenharmony_ci "DMA_MST_CTRL", 3038c2ecf20Sopenharmony_ci "DMA_CFG_A", 3048c2ecf20Sopenharmony_ci "DMA_CFG_B", 3058c2ecf20Sopenharmony_ci "FIFO_CTRL", 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic void tegra_nand_dump_reg(struct tegra_nand_controller *ctrl) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci u32 reg; 3118c2ecf20Sopenharmony_ci int i; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Tegra NAND controller register dump\n"); 3148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tegra_nand_reg_names); i++) { 3158c2ecf20Sopenharmony_ci const char *reg_name = tegra_nand_reg_names[i]; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!reg_name) 3188c2ecf20Sopenharmony_ci continue; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci reg = readl_relaxed(ctrl->regs + (i * 4)); 3218c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "%s: 0x%08x\n", reg_name, reg); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic void tegra_nand_controller_abort(struct tegra_nand_controller *ctrl) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci u32 isr, dma; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci disable_irq(ctrl->irq); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Abort current command/DMA operation */ 3328c2ecf20Sopenharmony_ci writel_relaxed(0, ctrl->regs + DMA_MST_CTRL); 3338c2ecf20Sopenharmony_ci writel_relaxed(0, ctrl->regs + COMMAND); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* clear interrupts */ 3368c2ecf20Sopenharmony_ci isr = readl_relaxed(ctrl->regs + ISR); 3378c2ecf20Sopenharmony_ci writel_relaxed(isr, ctrl->regs + ISR); 3388c2ecf20Sopenharmony_ci dma = readl_relaxed(ctrl->regs + DMA_MST_CTRL); 3398c2ecf20Sopenharmony_ci writel_relaxed(dma, ctrl->regs + DMA_MST_CTRL); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci reinit_completion(&ctrl->command_complete); 3428c2ecf20Sopenharmony_ci reinit_completion(&ctrl->dma_complete); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci enable_irq(ctrl->irq); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int tegra_nand_cmd(struct nand_chip *chip, 3488c2ecf20Sopenharmony_ci const struct nand_subop *subop) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci const struct nand_op_instr *instr; 3518c2ecf20Sopenharmony_ci const struct nand_op_instr *instr_data_in = NULL; 3528c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 3538c2ecf20Sopenharmony_ci unsigned int op_id, size = 0, offset = 0; 3548c2ecf20Sopenharmony_ci bool first_cmd = true; 3558c2ecf20Sopenharmony_ci u32 reg, cmd = 0; 3568c2ecf20Sopenharmony_ci int ret; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (op_id = 0; op_id < subop->ninstrs; op_id++) { 3598c2ecf20Sopenharmony_ci unsigned int naddrs, i; 3608c2ecf20Sopenharmony_ci const u8 *addrs; 3618c2ecf20Sopenharmony_ci u32 addr1 = 0, addr2 = 0; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci instr = &subop->instrs[op_id]; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci switch (instr->type) { 3668c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 3678c2ecf20Sopenharmony_ci if (first_cmd) { 3688c2ecf20Sopenharmony_ci cmd |= COMMAND_CLE; 3698c2ecf20Sopenharmony_ci writel_relaxed(instr->ctx.cmd.opcode, 3708c2ecf20Sopenharmony_ci ctrl->regs + CMD_REG1); 3718c2ecf20Sopenharmony_ci } else { 3728c2ecf20Sopenharmony_ci cmd |= COMMAND_SEC_CMD; 3738c2ecf20Sopenharmony_ci writel_relaxed(instr->ctx.cmd.opcode, 3748c2ecf20Sopenharmony_ci ctrl->regs + CMD_REG2); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci first_cmd = false; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 3808c2ecf20Sopenharmony_ci offset = nand_subop_get_addr_start_off(subop, op_id); 3818c2ecf20Sopenharmony_ci naddrs = nand_subop_get_num_addr_cyc(subop, op_id); 3828c2ecf20Sopenharmony_ci addrs = &instr->ctx.addr.addrs[offset]; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(naddrs); 3858c2ecf20Sopenharmony_ci for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) 3868c2ecf20Sopenharmony_ci addr1 |= *addrs++ << (BITS_PER_BYTE * i); 3878c2ecf20Sopenharmony_ci naddrs -= i; 3888c2ecf20Sopenharmony_ci for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) 3898c2ecf20Sopenharmony_ci addr2 |= *addrs++ << (BITS_PER_BYTE * i); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci writel_relaxed(addr1, ctrl->regs + ADDR_REG1); 3928c2ecf20Sopenharmony_ci writel_relaxed(addr2, ctrl->regs + ADDR_REG2); 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 3968c2ecf20Sopenharmony_ci size = nand_subop_get_data_len(subop, op_id); 3978c2ecf20Sopenharmony_ci offset = nand_subop_get_data_start_off(subop, op_id); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci cmd |= COMMAND_TRANS_SIZE(size) | COMMAND_PIO | 4008c2ecf20Sopenharmony_ci COMMAND_RX | COMMAND_A_VALID; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci instr_data_in = instr; 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 4068c2ecf20Sopenharmony_ci size = nand_subop_get_data_len(subop, op_id); 4078c2ecf20Sopenharmony_ci offset = nand_subop_get_data_start_off(subop, op_id); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci cmd |= COMMAND_TRANS_SIZE(size) | COMMAND_PIO | 4108c2ecf20Sopenharmony_ci COMMAND_TX | COMMAND_A_VALID; 4118c2ecf20Sopenharmony_ci memcpy(®, instr->ctx.data.buf.out + offset, size); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci writel_relaxed(reg, ctrl->regs + RESP); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 4178c2ecf20Sopenharmony_ci cmd |= COMMAND_RBSY_CHK; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci cmd |= COMMAND_GO | COMMAND_CE(ctrl->cur_cs); 4238c2ecf20Sopenharmony_ci writel_relaxed(cmd, ctrl->regs + COMMAND); 4248c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ctrl->command_complete, 4258c2ecf20Sopenharmony_ci msecs_to_jiffies(500)); 4268c2ecf20Sopenharmony_ci if (!ret) { 4278c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "COMMAND timeout\n"); 4288c2ecf20Sopenharmony_ci tegra_nand_dump_reg(ctrl); 4298c2ecf20Sopenharmony_ci tegra_nand_controller_abort(ctrl); 4308c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (instr_data_in) { 4348c2ecf20Sopenharmony_ci reg = readl_relaxed(ctrl->regs + RESP); 4358c2ecf20Sopenharmony_ci memcpy(instr_data_in->ctx.data.buf.in + offset, ®, size); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic const struct nand_op_parser tegra_nand_op_parser = NAND_OP_PARSER( 4428c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(tegra_nand_cmd, 4438c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 4448c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 4458c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 4468c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)), 4478c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(tegra_nand_cmd, 4488c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 4)), 4498c2ecf20Sopenharmony_ci NAND_OP_PARSER_PATTERN(tegra_nand_cmd, 4508c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 4518c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8), 4528c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_CMD_ELEM(true), 4538c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), 4548c2ecf20Sopenharmony_ci NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)), 4558c2ecf20Sopenharmony_ci ); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void tegra_nand_select_target(struct nand_chip *chip, 4588c2ecf20Sopenharmony_ci unsigned int die_nr) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct tegra_nand_chip *nand = to_tegra_chip(chip); 4618c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ctrl->cur_cs = nand->cs[die_nr]; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int tegra_nand_exec_op(struct nand_chip *chip, 4678c2ecf20Sopenharmony_ci const struct nand_operation *op, 4688c2ecf20Sopenharmony_ci bool check_only) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci if (!check_only) 4718c2ecf20Sopenharmony_ci tegra_nand_select_target(chip, op->cs); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci return nand_op_parser_exec_op(chip, &tegra_nand_op_parser, op, 4748c2ecf20Sopenharmony_ci check_only); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl, 4788c2ecf20Sopenharmony_ci struct nand_chip *chip, bool enable) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct tegra_nand_chip *nand = to_tegra_chip(chip); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (chip->ecc.algo == NAND_ECC_ALGO_BCH && enable) 4838c2ecf20Sopenharmony_ci writel_relaxed(nand->bch_config, ctrl->regs + BCH_CONFIG); 4848c2ecf20Sopenharmony_ci else 4858c2ecf20Sopenharmony_ci writel_relaxed(0, ctrl->regs + BCH_CONFIG); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (enable) 4888c2ecf20Sopenharmony_ci writel_relaxed(nand->config_ecc, ctrl->regs + CONFIG); 4898c2ecf20Sopenharmony_ci else 4908c2ecf20Sopenharmony_ci writel_relaxed(nand->config, ctrl->regs + CONFIG); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int tegra_nand_page_xfer(struct mtd_info *mtd, struct nand_chip *chip, 4948c2ecf20Sopenharmony_ci void *buf, void *oob_buf, int oob_len, int page, 4958c2ecf20Sopenharmony_ci bool read) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 4988c2ecf20Sopenharmony_ci enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 4998c2ecf20Sopenharmony_ci dma_addr_t dma_addr = 0, dma_addr_oob = 0; 5008c2ecf20Sopenharmony_ci u32 addr1, cmd, dma_ctrl; 5018c2ecf20Sopenharmony_ci int ret; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci tegra_nand_select_target(chip, chip->cur_cs); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (read) { 5068c2ecf20Sopenharmony_ci writel_relaxed(NAND_CMD_READ0, ctrl->regs + CMD_REG1); 5078c2ecf20Sopenharmony_ci writel_relaxed(NAND_CMD_READSTART, ctrl->regs + CMD_REG2); 5088c2ecf20Sopenharmony_ci } else { 5098c2ecf20Sopenharmony_ci writel_relaxed(NAND_CMD_SEQIN, ctrl->regs + CMD_REG1); 5108c2ecf20Sopenharmony_ci writel_relaxed(NAND_CMD_PAGEPROG, ctrl->regs + CMD_REG2); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci cmd = COMMAND_CLE | COMMAND_SEC_CMD; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* Lower 16-bits are column, by default 0 */ 5158c2ecf20Sopenharmony_ci addr1 = page << 16; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!buf) 5188c2ecf20Sopenharmony_ci addr1 |= mtd->writesize; 5198c2ecf20Sopenharmony_ci writel_relaxed(addr1, ctrl->regs + ADDR_REG1); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (chip->options & NAND_ROW_ADDR_3) { 5228c2ecf20Sopenharmony_ci writel_relaxed(page >> 16, ctrl->regs + ADDR_REG2); 5238c2ecf20Sopenharmony_ci cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(5); 5248c2ecf20Sopenharmony_ci } else { 5258c2ecf20Sopenharmony_ci cmd |= COMMAND_ALE | COMMAND_ALE_SIZE(4); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (buf) { 5298c2ecf20Sopenharmony_ci dma_addr = dma_map_single(ctrl->dev, buf, mtd->writesize, dir); 5308c2ecf20Sopenharmony_ci ret = dma_mapping_error(ctrl->dev, dma_addr); 5318c2ecf20Sopenharmony_ci if (ret) { 5328c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "dma mapping error\n"); 5338c2ecf20Sopenharmony_ci return -EINVAL; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci writel_relaxed(mtd->writesize - 1, ctrl->regs + DMA_CFG_A); 5378c2ecf20Sopenharmony_ci writel_relaxed(dma_addr, ctrl->regs + DATA_PTR); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (oob_buf) { 5418c2ecf20Sopenharmony_ci dma_addr_oob = dma_map_single(ctrl->dev, oob_buf, mtd->oobsize, 5428c2ecf20Sopenharmony_ci dir); 5438c2ecf20Sopenharmony_ci ret = dma_mapping_error(ctrl->dev, dma_addr_oob); 5448c2ecf20Sopenharmony_ci if (ret) { 5458c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "dma mapping error\n"); 5468c2ecf20Sopenharmony_ci ret = -EINVAL; 5478c2ecf20Sopenharmony_ci goto err_unmap_dma_page; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci writel_relaxed(oob_len - 1, ctrl->regs + DMA_CFG_B); 5518c2ecf20Sopenharmony_ci writel_relaxed(dma_addr_oob, ctrl->regs + TAG_PTR); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci dma_ctrl = DMA_MST_CTRL_GO | DMA_MST_CTRL_PERF_EN | 5558c2ecf20Sopenharmony_ci DMA_MST_CTRL_IE_DONE | DMA_MST_CTRL_IS_DONE | 5568c2ecf20Sopenharmony_ci DMA_MST_CTRL_BURST_16; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (buf) 5598c2ecf20Sopenharmony_ci dma_ctrl |= DMA_MST_CTRL_EN_A; 5608c2ecf20Sopenharmony_ci if (oob_buf) 5618c2ecf20Sopenharmony_ci dma_ctrl |= DMA_MST_CTRL_EN_B; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (read) 5648c2ecf20Sopenharmony_ci dma_ctrl |= DMA_MST_CTRL_IN | DMA_MST_CTRL_REUSE; 5658c2ecf20Sopenharmony_ci else 5668c2ecf20Sopenharmony_ci dma_ctrl |= DMA_MST_CTRL_OUT; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci writel_relaxed(dma_ctrl, ctrl->regs + DMA_MST_CTRL); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci cmd |= COMMAND_GO | COMMAND_RBSY_CHK | COMMAND_TRANS_SIZE(9) | 5718c2ecf20Sopenharmony_ci COMMAND_CE(ctrl->cur_cs); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (buf) 5748c2ecf20Sopenharmony_ci cmd |= COMMAND_A_VALID; 5758c2ecf20Sopenharmony_ci if (oob_buf) 5768c2ecf20Sopenharmony_ci cmd |= COMMAND_B_VALID; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (read) 5798c2ecf20Sopenharmony_ci cmd |= COMMAND_RX; 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci cmd |= COMMAND_TX | COMMAND_AFT_DAT; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci writel_relaxed(cmd, ctrl->regs + COMMAND); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ctrl->command_complete, 5868c2ecf20Sopenharmony_ci msecs_to_jiffies(500)); 5878c2ecf20Sopenharmony_ci if (!ret) { 5888c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "COMMAND timeout\n"); 5898c2ecf20Sopenharmony_ci tegra_nand_dump_reg(ctrl); 5908c2ecf20Sopenharmony_ci tegra_nand_controller_abort(ctrl); 5918c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 5928c2ecf20Sopenharmony_ci goto err_unmap_dma; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&ctrl->dma_complete, 5968c2ecf20Sopenharmony_ci msecs_to_jiffies(500)); 5978c2ecf20Sopenharmony_ci if (!ret) { 5988c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "DMA timeout\n"); 5998c2ecf20Sopenharmony_ci tegra_nand_dump_reg(ctrl); 6008c2ecf20Sopenharmony_ci tegra_nand_controller_abort(ctrl); 6018c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 6028c2ecf20Sopenharmony_ci goto err_unmap_dma; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci ret = 0; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cierr_unmap_dma: 6078c2ecf20Sopenharmony_ci if (oob_buf) 6088c2ecf20Sopenharmony_ci dma_unmap_single(ctrl->dev, dma_addr_oob, mtd->oobsize, dir); 6098c2ecf20Sopenharmony_cierr_unmap_dma_page: 6108c2ecf20Sopenharmony_ci if (buf) 6118c2ecf20Sopenharmony_ci dma_unmap_single(ctrl->dev, dma_addr, mtd->writesize, dir); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return ret; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int tegra_nand_read_page_raw(struct nand_chip *chip, u8 *buf, 6178c2ecf20Sopenharmony_ci int oob_required, int page) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6208c2ecf20Sopenharmony_ci void *oob_buf = oob_required ? chip->oob_poi : NULL; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return tegra_nand_page_xfer(mtd, chip, buf, oob_buf, 6238c2ecf20Sopenharmony_ci mtd->oobsize, page, true); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int tegra_nand_write_page_raw(struct nand_chip *chip, const u8 *buf, 6278c2ecf20Sopenharmony_ci int oob_required, int page) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6308c2ecf20Sopenharmony_ci void *oob_buf = oob_required ? chip->oob_poi : NULL; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return tegra_nand_page_xfer(mtd, chip, (void *)buf, oob_buf, 6338c2ecf20Sopenharmony_ci mtd->oobsize, page, false); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int tegra_nand_read_oob(struct nand_chip *chip, int page) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return tegra_nand_page_xfer(mtd, chip, NULL, chip->oob_poi, 6418c2ecf20Sopenharmony_ci mtd->oobsize, page, true); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int tegra_nand_write_oob(struct nand_chip *chip, int page) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return tegra_nand_page_xfer(mtd, chip, NULL, chip->oob_poi, 6498c2ecf20Sopenharmony_ci mtd->oobsize, page, false); 6508c2ecf20Sopenharmony_ci} 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_cistatic int tegra_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf, 6538c2ecf20Sopenharmony_ci int oob_required, int page) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6568c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 6578c2ecf20Sopenharmony_ci struct tegra_nand_chip *nand = to_tegra_chip(chip); 6588c2ecf20Sopenharmony_ci void *oob_buf = oob_required ? chip->oob_poi : NULL; 6598c2ecf20Sopenharmony_ci u32 dec_stat, max_corr_cnt; 6608c2ecf20Sopenharmony_ci unsigned long fail_sec_flag; 6618c2ecf20Sopenharmony_ci int ret; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci tegra_nand_hw_ecc(ctrl, chip, true); 6648c2ecf20Sopenharmony_ci ret = tegra_nand_page_xfer(mtd, chip, buf, oob_buf, 0, page, true); 6658c2ecf20Sopenharmony_ci tegra_nand_hw_ecc(ctrl, chip, false); 6668c2ecf20Sopenharmony_ci if (ret) 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* No correctable or un-correctable errors, page must have 0 bitflips */ 6708c2ecf20Sopenharmony_ci if (!ctrl->last_read_error) 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* 6748c2ecf20Sopenharmony_ci * Correctable or un-correctable errors occurred. Use DEC_STAT_BUF 6758c2ecf20Sopenharmony_ci * which contains information for all ECC selections. 6768c2ecf20Sopenharmony_ci * 6778c2ecf20Sopenharmony_ci * Note that since we do not use Command Queues DEC_RESULT does not 6788c2ecf20Sopenharmony_ci * state the number of pages we can read from the DEC_STAT_BUF. But 6798c2ecf20Sopenharmony_ci * since CORRFAIL_ERR did occur during page read we do have a valid 6808c2ecf20Sopenharmony_ci * result in DEC_STAT_BUF. 6818c2ecf20Sopenharmony_ci */ 6828c2ecf20Sopenharmony_ci ctrl->last_read_error = false; 6838c2ecf20Sopenharmony_ci dec_stat = readl_relaxed(ctrl->regs + DEC_STAT_BUF); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci fail_sec_flag = (dec_stat & DEC_STAT_BUF_FAIL_SEC_FLAG_MASK) >> 6868c2ecf20Sopenharmony_ci DEC_STAT_BUF_FAIL_SEC_FLAG_SHIFT; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci max_corr_cnt = (dec_stat & DEC_STAT_BUF_MAX_CORR_CNT_MASK) >> 6898c2ecf20Sopenharmony_ci DEC_STAT_BUF_MAX_CORR_CNT_SHIFT; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (fail_sec_flag) { 6928c2ecf20Sopenharmony_ci int bit, max_bitflips = 0; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci /* 6958c2ecf20Sopenharmony_ci * Since we do not support subpage writes, a complete page 6968c2ecf20Sopenharmony_ci * is either written or not. We can take a shortcut here by 6978c2ecf20Sopenharmony_ci * checking wheather any of the sector has been successful 6988c2ecf20Sopenharmony_ci * read. If at least one sectors has been read successfully, 6998c2ecf20Sopenharmony_ci * the page must have been a written previously. It cannot 7008c2ecf20Sopenharmony_ci * be an erased page. 7018c2ecf20Sopenharmony_ci * 7028c2ecf20Sopenharmony_ci * E.g. controller might return fail_sec_flag with 0x4, which 7038c2ecf20Sopenharmony_ci * would mean only the third sector failed to correct. The 7048c2ecf20Sopenharmony_ci * page must have been written and the third sector is really 7058c2ecf20Sopenharmony_ci * not correctable anymore. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci if (fail_sec_flag ^ GENMASK(chip->ecc.steps - 1, 0)) { 7088c2ecf20Sopenharmony_ci mtd->ecc_stats.failed += hweight8(fail_sec_flag); 7098c2ecf20Sopenharmony_ci return max_corr_cnt; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* 7138c2ecf20Sopenharmony_ci * All sectors failed to correct, but the ECC isn't smart 7148c2ecf20Sopenharmony_ci * enough to figure out if a page is really just erased. 7158c2ecf20Sopenharmony_ci * Read OOB data and check whether data/OOB is completely 7168c2ecf20Sopenharmony_ci * erased or if error correction just failed for all sub- 7178c2ecf20Sopenharmony_ci * pages. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci ret = tegra_nand_read_oob(chip, page); 7208c2ecf20Sopenharmony_ci if (ret < 0) 7218c2ecf20Sopenharmony_ci return ret; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci for_each_set_bit(bit, &fail_sec_flag, chip->ecc.steps) { 7248c2ecf20Sopenharmony_ci u8 *data = buf + (chip->ecc.size * bit); 7258c2ecf20Sopenharmony_ci u8 *oob = chip->oob_poi + nand->ecc.offset + 7268c2ecf20Sopenharmony_ci (chip->ecc.bytes * bit); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ret = nand_check_erased_ecc_chunk(data, chip->ecc.size, 7298c2ecf20Sopenharmony_ci oob, chip->ecc.bytes, 7308c2ecf20Sopenharmony_ci NULL, 0, 7318c2ecf20Sopenharmony_ci chip->ecc.strength); 7328c2ecf20Sopenharmony_ci if (ret < 0) { 7338c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 7348c2ecf20Sopenharmony_ci } else { 7358c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += ret; 7368c2ecf20Sopenharmony_ci max_bitflips = max(ret, max_bitflips); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return max_t(unsigned int, max_corr_cnt, max_bitflips); 7418c2ecf20Sopenharmony_ci } else { 7428c2ecf20Sopenharmony_ci int corr_sec_flag; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci corr_sec_flag = (dec_stat & DEC_STAT_BUF_CORR_SEC_FLAG_MASK) >> 7458c2ecf20Sopenharmony_ci DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* 7488c2ecf20Sopenharmony_ci * The value returned in the register is the maximum of 7498c2ecf20Sopenharmony_ci * bitflips encountered in any of the ECC regions. As there is 7508c2ecf20Sopenharmony_ci * no way to get the number of bitflips in a specific regions 7518c2ecf20Sopenharmony_ci * we are not able to deliver correct stats but instead 7528c2ecf20Sopenharmony_ci * overestimate the number of corrected bitflips by assuming 7538c2ecf20Sopenharmony_ci * that all regions where errors have been corrected 7548c2ecf20Sopenharmony_ci * encountered the maximum number of bitflips. 7558c2ecf20Sopenharmony_ci */ 7568c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected += max_corr_cnt * hweight8(corr_sec_flag); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return max_corr_cnt; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int tegra_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf, 7638c2ecf20Sopenharmony_ci int oob_required, int page) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7668c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 7678c2ecf20Sopenharmony_ci void *oob_buf = oob_required ? chip->oob_poi : NULL; 7688c2ecf20Sopenharmony_ci int ret; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci tegra_nand_hw_ecc(ctrl, chip, true); 7718c2ecf20Sopenharmony_ci ret = tegra_nand_page_xfer(mtd, chip, (void *)buf, oob_buf, 7728c2ecf20Sopenharmony_ci 0, page, false); 7738c2ecf20Sopenharmony_ci tegra_nand_hw_ecc(ctrl, chip, false); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return ret; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic void tegra_nand_setup_timing(struct tegra_nand_controller *ctrl, 7798c2ecf20Sopenharmony_ci const struct nand_sdr_timings *timings) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci /* 7828c2ecf20Sopenharmony_ci * The period (and all other timings in this function) is in ps, 7838c2ecf20Sopenharmony_ci * so need to take care here to avoid integer overflows. 7848c2ecf20Sopenharmony_ci */ 7858c2ecf20Sopenharmony_ci unsigned int rate = clk_get_rate(ctrl->clk) / 1000000; 7868c2ecf20Sopenharmony_ci unsigned int period = DIV_ROUND_UP(1000000, rate); 7878c2ecf20Sopenharmony_ci u32 val, reg = 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci val = DIV_ROUND_UP(max3(timings->tAR_min, timings->tRR_min, 7908c2ecf20Sopenharmony_ci timings->tRC_min), period); 7918c2ecf20Sopenharmony_ci reg |= TIMING_TCR_TAR_TRR(OFFSET(val, 3)); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci val = DIV_ROUND_UP(max(max(timings->tCS_min, timings->tCH_min), 7948c2ecf20Sopenharmony_ci max(timings->tALS_min, timings->tALH_min)), 7958c2ecf20Sopenharmony_ci period); 7968c2ecf20Sopenharmony_ci reg |= TIMING_TCS(OFFSET(val, 2)); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci val = DIV_ROUND_UP(max(timings->tRP_min, timings->tREA_max) + 6000, 7998c2ecf20Sopenharmony_ci period); 8008c2ecf20Sopenharmony_ci reg |= TIMING_TRP(OFFSET(val, 1)) | TIMING_TRP_RESP(OFFSET(val, 1)); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci reg |= TIMING_TWB(OFFSET(DIV_ROUND_UP(timings->tWB_max, period), 1)); 8038c2ecf20Sopenharmony_ci reg |= TIMING_TWHR(OFFSET(DIV_ROUND_UP(timings->tWHR_min, period), 1)); 8048c2ecf20Sopenharmony_ci reg |= TIMING_TWH(OFFSET(DIV_ROUND_UP(timings->tWH_min, period), 1)); 8058c2ecf20Sopenharmony_ci reg |= TIMING_TWP(OFFSET(DIV_ROUND_UP(timings->tWP_min, period), 1)); 8068c2ecf20Sopenharmony_ci reg |= TIMING_TRH(OFFSET(DIV_ROUND_UP(timings->tREH_min, period), 1)); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci writel_relaxed(reg, ctrl->regs + TIMING_1); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci val = DIV_ROUND_UP(timings->tADL_min, period); 8118c2ecf20Sopenharmony_ci reg = TIMING_TADL(OFFSET(val, 3)); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci writel_relaxed(reg, ctrl->regs + TIMING_2); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic int tegra_nand_setup_interface(struct nand_chip *chip, int csline, 8178c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 8208c2ecf20Sopenharmony_ci const struct nand_sdr_timings *timings; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci timings = nand_get_sdr_timings(conf); 8238c2ecf20Sopenharmony_ci if (IS_ERR(timings)) 8248c2ecf20Sopenharmony_ci return PTR_ERR(timings); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci tegra_nand_setup_timing(ctrl, timings); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic const int rs_strength_bootable[] = { 4 }; 8358c2ecf20Sopenharmony_cistatic const int rs_strength[] = { 4, 6, 8 }; 8368c2ecf20Sopenharmony_cistatic const int bch_strength_bootable[] = { 8, 16 }; 8378c2ecf20Sopenharmony_cistatic const int bch_strength[] = { 4, 8, 14, 16 }; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_cistatic int tegra_nand_get_strength(struct nand_chip *chip, const int *strength, 8408c2ecf20Sopenharmony_ci int strength_len, int bits_per_step, 8418c2ecf20Sopenharmony_ci int oobsize) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci struct nand_device *base = mtd_to_nanddev(nand_to_mtd(chip)); 8448c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 8458c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(base); 8468c2ecf20Sopenharmony_ci bool maximize = base->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH; 8478c2ecf20Sopenharmony_ci int i; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* 8508c2ecf20Sopenharmony_ci * Loop through available strengths. Backwards in case we try to 8518c2ecf20Sopenharmony_ci * maximize the BCH strength. 8528c2ecf20Sopenharmony_ci */ 8538c2ecf20Sopenharmony_ci for (i = 0; i < strength_len; i++) { 8548c2ecf20Sopenharmony_ci int strength_sel, bytes_per_step, bytes_per_page; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (maximize) { 8578c2ecf20Sopenharmony_ci strength_sel = strength[strength_len - i - 1]; 8588c2ecf20Sopenharmony_ci } else { 8598c2ecf20Sopenharmony_ci strength_sel = strength[i]; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (strength_sel < requirements->strength) 8628c2ecf20Sopenharmony_ci continue; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci bytes_per_step = DIV_ROUND_UP(bits_per_step * strength_sel, 8668c2ecf20Sopenharmony_ci BITS_PER_BYTE); 8678c2ecf20Sopenharmony_ci bytes_per_page = round_up(bytes_per_step * chip->ecc.steps, 4); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* Check whether strength fits OOB */ 8708c2ecf20Sopenharmony_ci if (bytes_per_page < (oobsize - SKIP_SPARE_BYTES)) 8718c2ecf20Sopenharmony_ci return strength_sel; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return -EINVAL; 8758c2ecf20Sopenharmony_ci} 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cistatic int tegra_nand_select_strength(struct nand_chip *chip, int oobsize) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci const int *strength; 8808c2ecf20Sopenharmony_ci int strength_len, bits_per_step; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci switch (chip->ecc.algo) { 8838c2ecf20Sopenharmony_ci case NAND_ECC_ALGO_RS: 8848c2ecf20Sopenharmony_ci bits_per_step = BITS_PER_STEP_RS; 8858c2ecf20Sopenharmony_ci if (chip->options & NAND_IS_BOOT_MEDIUM) { 8868c2ecf20Sopenharmony_ci strength = rs_strength_bootable; 8878c2ecf20Sopenharmony_ci strength_len = ARRAY_SIZE(rs_strength_bootable); 8888c2ecf20Sopenharmony_ci } else { 8898c2ecf20Sopenharmony_ci strength = rs_strength; 8908c2ecf20Sopenharmony_ci strength_len = ARRAY_SIZE(rs_strength); 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci break; 8938c2ecf20Sopenharmony_ci case NAND_ECC_ALGO_BCH: 8948c2ecf20Sopenharmony_ci bits_per_step = BITS_PER_STEP_BCH; 8958c2ecf20Sopenharmony_ci if (chip->options & NAND_IS_BOOT_MEDIUM) { 8968c2ecf20Sopenharmony_ci strength = bch_strength_bootable; 8978c2ecf20Sopenharmony_ci strength_len = ARRAY_SIZE(bch_strength_bootable); 8988c2ecf20Sopenharmony_ci } else { 8998c2ecf20Sopenharmony_ci strength = bch_strength; 9008c2ecf20Sopenharmony_ci strength_len = ARRAY_SIZE(bch_strength); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci default: 9048c2ecf20Sopenharmony_ci return -EINVAL; 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci return tegra_nand_get_strength(chip, strength, strength_len, 9088c2ecf20Sopenharmony_ci bits_per_step, oobsize); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic int tegra_nand_attach_chip(struct nand_chip *chip) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller); 9148c2ecf20Sopenharmony_ci const struct nand_ecc_props *requirements = 9158c2ecf20Sopenharmony_ci nanddev_get_ecc_requirements(&chip->base); 9168c2ecf20Sopenharmony_ci struct tegra_nand_chip *nand = to_tegra_chip(chip); 9178c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 9188c2ecf20Sopenharmony_ci int bits_per_step; 9198c2ecf20Sopenharmony_ci int ret; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (chip->bbt_options & NAND_BBT_USE_FLASH) 9228c2ecf20Sopenharmony_ci chip->bbt_options |= NAND_BBT_NO_OOB; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 9258c2ecf20Sopenharmony_ci chip->ecc.size = 512; 9268c2ecf20Sopenharmony_ci chip->ecc.steps = mtd->writesize / chip->ecc.size; 9278c2ecf20Sopenharmony_ci if (requirements->step_size != 512) { 9288c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Unsupported step size %d\n", 9298c2ecf20Sopenharmony_ci requirements->step_size); 9308c2ecf20Sopenharmony_ci return -EINVAL; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci chip->ecc.read_page = tegra_nand_read_page_hwecc; 9348c2ecf20Sopenharmony_ci chip->ecc.write_page = tegra_nand_write_page_hwecc; 9358c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = tegra_nand_read_page_raw; 9368c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = tegra_nand_write_page_raw; 9378c2ecf20Sopenharmony_ci chip->ecc.read_oob = tegra_nand_read_oob; 9388c2ecf20Sopenharmony_ci chip->ecc.write_oob = tegra_nand_write_oob; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 9418c2ecf20Sopenharmony_ci nand->config |= CONFIG_BUS_WIDTH_16; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (chip->ecc.algo == NAND_ECC_ALGO_UNKNOWN) { 9448c2ecf20Sopenharmony_ci if (mtd->writesize < 2048) 9458c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_RS; 9468c2ecf20Sopenharmony_ci else 9478c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_BCH; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (chip->ecc.algo == NAND_ECC_ALGO_BCH && mtd->writesize < 2048) { 9518c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "BCH supports 2K or 4K page size only\n"); 9528c2ecf20Sopenharmony_ci return -EINVAL; 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci if (!chip->ecc.strength) { 9568c2ecf20Sopenharmony_ci ret = tegra_nand_select_strength(chip, mtd->oobsize); 9578c2ecf20Sopenharmony_ci if (ret < 0) { 9588c2ecf20Sopenharmony_ci dev_err(ctrl->dev, 9598c2ecf20Sopenharmony_ci "No valid strength found, minimum %d\n", 9608c2ecf20Sopenharmony_ci requirements->strength); 9618c2ecf20Sopenharmony_ci return ret; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci chip->ecc.strength = ret; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci nand->config_ecc = CONFIG_PIPE_EN | CONFIG_SKIP_SPARE | 9688c2ecf20Sopenharmony_ci CONFIG_SKIP_SPARE_SIZE_4; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci switch (chip->ecc.algo) { 9718c2ecf20Sopenharmony_ci case NAND_ECC_ALGO_RS: 9728c2ecf20Sopenharmony_ci bits_per_step = BITS_PER_STEP_RS * chip->ecc.strength; 9738c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &tegra_nand_oob_rs_ops); 9748c2ecf20Sopenharmony_ci nand->config_ecc |= CONFIG_HW_ECC | CONFIG_ECC_SEL | 9758c2ecf20Sopenharmony_ci CONFIG_ERR_COR; 9768c2ecf20Sopenharmony_ci switch (chip->ecc.strength) { 9778c2ecf20Sopenharmony_ci case 4: 9788c2ecf20Sopenharmony_ci nand->config_ecc |= CONFIG_TVAL_4; 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci case 6: 9818c2ecf20Sopenharmony_ci nand->config_ecc |= CONFIG_TVAL_6; 9828c2ecf20Sopenharmony_ci break; 9838c2ecf20Sopenharmony_ci case 8: 9848c2ecf20Sopenharmony_ci nand->config_ecc |= CONFIG_TVAL_8; 9858c2ecf20Sopenharmony_ci break; 9868c2ecf20Sopenharmony_ci default: 9878c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "ECC strength %d not supported\n", 9888c2ecf20Sopenharmony_ci chip->ecc.strength); 9898c2ecf20Sopenharmony_ci return -EINVAL; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci break; 9928c2ecf20Sopenharmony_ci case NAND_ECC_ALGO_BCH: 9938c2ecf20Sopenharmony_ci bits_per_step = BITS_PER_STEP_BCH * chip->ecc.strength; 9948c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &tegra_nand_oob_bch_ops); 9958c2ecf20Sopenharmony_ci nand->bch_config = BCH_ENABLE; 9968c2ecf20Sopenharmony_ci switch (chip->ecc.strength) { 9978c2ecf20Sopenharmony_ci case 4: 9988c2ecf20Sopenharmony_ci nand->bch_config |= BCH_TVAL_4; 9998c2ecf20Sopenharmony_ci break; 10008c2ecf20Sopenharmony_ci case 8: 10018c2ecf20Sopenharmony_ci nand->bch_config |= BCH_TVAL_8; 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci case 14: 10048c2ecf20Sopenharmony_ci nand->bch_config |= BCH_TVAL_14; 10058c2ecf20Sopenharmony_ci break; 10068c2ecf20Sopenharmony_ci case 16: 10078c2ecf20Sopenharmony_ci nand->bch_config |= BCH_TVAL_16; 10088c2ecf20Sopenharmony_ci break; 10098c2ecf20Sopenharmony_ci default: 10108c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "ECC strength %d not supported\n", 10118c2ecf20Sopenharmony_ci chip->ecc.strength); 10128c2ecf20Sopenharmony_ci return -EINVAL; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci break; 10158c2ecf20Sopenharmony_ci default: 10168c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "ECC algorithm not supported\n"); 10178c2ecf20Sopenharmony_ci return -EINVAL; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci dev_info(ctrl->dev, "Using %s with strength %d per 512 byte step\n", 10218c2ecf20Sopenharmony_ci chip->ecc.algo == NAND_ECC_ALGO_BCH ? "BCH" : "RS", 10228c2ecf20Sopenharmony_ci chip->ecc.strength); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci chip->ecc.bytes = DIV_ROUND_UP(bits_per_step, BITS_PER_BYTE); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci switch (mtd->writesize) { 10278c2ecf20Sopenharmony_ci case 256: 10288c2ecf20Sopenharmony_ci nand->config |= CONFIG_PS_256; 10298c2ecf20Sopenharmony_ci break; 10308c2ecf20Sopenharmony_ci case 512: 10318c2ecf20Sopenharmony_ci nand->config |= CONFIG_PS_512; 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci case 1024: 10348c2ecf20Sopenharmony_ci nand->config |= CONFIG_PS_1024; 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci case 2048: 10378c2ecf20Sopenharmony_ci nand->config |= CONFIG_PS_2048; 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci case 4096: 10408c2ecf20Sopenharmony_ci nand->config |= CONFIG_PS_4096; 10418c2ecf20Sopenharmony_ci break; 10428c2ecf20Sopenharmony_ci default: 10438c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Unsupported writesize %d\n", 10448c2ecf20Sopenharmony_ci mtd->writesize); 10458c2ecf20Sopenharmony_ci return -ENODEV; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* Store complete configuration for HW ECC in config_ecc */ 10498c2ecf20Sopenharmony_ci nand->config_ecc |= nand->config; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* Non-HW ECC read/writes complete OOB */ 10528c2ecf20Sopenharmony_ci nand->config |= CONFIG_TAG_BYTE_SIZE(mtd->oobsize - 1); 10538c2ecf20Sopenharmony_ci writel_relaxed(nand->config, ctrl->regs + CONFIG); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci return 0; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic const struct nand_controller_ops tegra_nand_controller_ops = { 10598c2ecf20Sopenharmony_ci .attach_chip = &tegra_nand_attach_chip, 10608c2ecf20Sopenharmony_ci .exec_op = tegra_nand_exec_op, 10618c2ecf20Sopenharmony_ci .setup_interface = tegra_nand_setup_interface, 10628c2ecf20Sopenharmony_ci}; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic int tegra_nand_chips_init(struct device *dev, 10658c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 10688c2ecf20Sopenharmony_ci struct device_node *np_nand; 10698c2ecf20Sopenharmony_ci int nsels, nchips = of_get_child_count(np); 10708c2ecf20Sopenharmony_ci struct tegra_nand_chip *nand; 10718c2ecf20Sopenharmony_ci struct mtd_info *mtd; 10728c2ecf20Sopenharmony_ci struct nand_chip *chip; 10738c2ecf20Sopenharmony_ci int ret; 10748c2ecf20Sopenharmony_ci u32 cs; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (nchips != 1) { 10778c2ecf20Sopenharmony_ci dev_err(dev, "Currently only one NAND chip supported\n"); 10788c2ecf20Sopenharmony_ci return -EINVAL; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci np_nand = of_get_next_child(np, NULL); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci nsels = of_property_count_elems_of_size(np_nand, "reg", sizeof(u32)); 10848c2ecf20Sopenharmony_ci if (nsels != 1) { 10858c2ecf20Sopenharmony_ci dev_err(dev, "Missing/invalid reg property\n"); 10868c2ecf20Sopenharmony_ci return -EINVAL; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Retrieve CS id, currently only single die NAND supported */ 10908c2ecf20Sopenharmony_ci ret = of_property_read_u32(np_nand, "reg", &cs); 10918c2ecf20Sopenharmony_ci if (ret) { 10928c2ecf20Sopenharmony_ci dev_err(dev, "could not retrieve reg property: %d\n", ret); 10938c2ecf20Sopenharmony_ci return ret; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci nand = devm_kzalloc(dev, sizeof(*nand), GFP_KERNEL); 10978c2ecf20Sopenharmony_ci if (!nand) 10988c2ecf20Sopenharmony_ci return -ENOMEM; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci nand->cs[0] = cs; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci nand->wp_gpio = devm_gpiod_get_optional(dev, "wp", GPIOD_OUT_LOW); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci if (IS_ERR(nand->wp_gpio)) { 11058c2ecf20Sopenharmony_ci ret = PTR_ERR(nand->wp_gpio); 11068c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request WP GPIO: %d\n", ret); 11078c2ecf20Sopenharmony_ci return ret; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci chip = &nand->chip; 11118c2ecf20Sopenharmony_ci chip->controller = &ctrl->controller; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci mtd = nand_to_mtd(chip); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci mtd->dev.parent = dev; 11168c2ecf20Sopenharmony_ci mtd->owner = THIS_MODULE; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci nand_set_flash_node(chip, np_nand); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (!mtd->name) 11218c2ecf20Sopenharmony_ci mtd->name = "tegra_nand"; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USES_DMA; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci ret = nand_scan(chip, 1); 11268c2ecf20Sopenharmony_ci if (ret) 11278c2ecf20Sopenharmony_ci return ret; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci mtd_ooblayout_ecc(mtd, 0, &nand->ecc); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 11328c2ecf20Sopenharmony_ci if (ret) { 11338c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register mtd device: %d\n", ret); 11348c2ecf20Sopenharmony_ci nand_cleanup(chip); 11358c2ecf20Sopenharmony_ci return ret; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci ctrl->chip = chip; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int tegra_nand_probe(struct platform_device *pdev) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci struct reset_control *rst; 11468c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl; 11478c2ecf20Sopenharmony_ci struct resource *res; 11488c2ecf20Sopenharmony_ci int err = 0; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); 11518c2ecf20Sopenharmony_ci if (!ctrl) 11528c2ecf20Sopenharmony_ci return -ENOMEM; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci ctrl->dev = &pdev->dev; 11558c2ecf20Sopenharmony_ci nand_controller_init(&ctrl->controller); 11568c2ecf20Sopenharmony_ci ctrl->controller.ops = &tegra_nand_controller_ops; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11598c2ecf20Sopenharmony_ci ctrl->regs = devm_ioremap_resource(&pdev->dev, res); 11608c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->regs)) 11618c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->regs); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci rst = devm_reset_control_get(&pdev->dev, "nand"); 11648c2ecf20Sopenharmony_ci if (IS_ERR(rst)) 11658c2ecf20Sopenharmony_ci return PTR_ERR(rst); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci ctrl->clk = devm_clk_get(&pdev->dev, "nand"); 11688c2ecf20Sopenharmony_ci if (IS_ERR(ctrl->clk)) 11698c2ecf20Sopenharmony_ci return PTR_ERR(ctrl->clk); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci err = clk_prepare_enable(ctrl->clk); 11728c2ecf20Sopenharmony_ci if (err) 11738c2ecf20Sopenharmony_ci return err; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci err = reset_control_reset(rst); 11768c2ecf20Sopenharmony_ci if (err) { 11778c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Failed to reset HW: %d\n", err); 11788c2ecf20Sopenharmony_ci goto err_disable_clk; 11798c2ecf20Sopenharmony_ci } 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD); 11828c2ecf20Sopenharmony_ci writel_relaxed(HWSTATUS_MASK_DEFAULT, ctrl->regs + HWSTATUS_MASK); 11838c2ecf20Sopenharmony_ci writel_relaxed(INT_MASK, ctrl->regs + IER); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci init_completion(&ctrl->command_complete); 11868c2ecf20Sopenharmony_ci init_completion(&ctrl->dma_complete); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci ctrl->irq = platform_get_irq(pdev, 0); 11898c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, ctrl->irq, tegra_nand_irq, 0, 11908c2ecf20Sopenharmony_ci dev_name(&pdev->dev), ctrl); 11918c2ecf20Sopenharmony_ci if (err) { 11928c2ecf20Sopenharmony_ci dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err); 11938c2ecf20Sopenharmony_ci goto err_disable_clk; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci err = tegra_nand_chips_init(ctrl->dev, ctrl); 11998c2ecf20Sopenharmony_ci if (err) 12008c2ecf20Sopenharmony_ci goto err_disable_clk; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ctrl); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci return 0; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cierr_disable_clk: 12078c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->clk); 12088c2ecf20Sopenharmony_ci return err; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_cistatic int tegra_nand_remove(struct platform_device *pdev) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct tegra_nand_controller *ctrl = platform_get_drvdata(pdev); 12148c2ecf20Sopenharmony_ci struct nand_chip *chip = ctrl->chip; 12158c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 12168c2ecf20Sopenharmony_ci int ret; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci ret = mtd_device_unregister(mtd); 12198c2ecf20Sopenharmony_ci if (ret) 12208c2ecf20Sopenharmony_ci return ret; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci nand_cleanup(chip); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci clk_disable_unprepare(ctrl->clk); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci return 0; 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_nand_of_match[] = { 12308c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-nand" }, 12318c2ecf20Sopenharmony_ci { /* sentinel */ } 12328c2ecf20Sopenharmony_ci}; 12338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_nand_of_match); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic struct platform_driver tegra_nand_driver = { 12368c2ecf20Sopenharmony_ci .driver = { 12378c2ecf20Sopenharmony_ci .name = "tegra-nand", 12388c2ecf20Sopenharmony_ci .of_match_table = tegra_nand_of_match, 12398c2ecf20Sopenharmony_ci }, 12408c2ecf20Sopenharmony_ci .probe = tegra_nand_probe, 12418c2ecf20Sopenharmony_ci .remove = tegra_nand_remove, 12428c2ecf20Sopenharmony_ci}; 12438c2ecf20Sopenharmony_cimodule_platform_driver(tegra_nand_driver); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra NAND driver"); 12468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@nvidia.com>"); 12478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lucas Stach <dev@lynxeye.de>"); 12488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefan Agner <stefan@agner.ch>"); 12498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1250