18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * NAND Flash Controller Device Driver 48c2ecf20Sopenharmony_ci * Copyright © 2009-2010, Intel Corporation and its suppliers. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2017-2019 Socionext Inc. 78c2ecf20Sopenharmony_ci * Reworked by Masahiro Yamada <yamada.masahiro@socionext.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 118c2ecf20Sopenharmony_ci#include <linux/completion.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 178c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "denali.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DENALI_NAND_NAME "denali-nand" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* for Indexed Addressing */ 268c2ecf20Sopenharmony_ci#define DENALI_INDEXED_CTRL 0x00 278c2ecf20Sopenharmony_ci#define DENALI_INDEXED_DATA 0x10 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define DENALI_MAP00 (0 << 26) /* direct access to buffer */ 308c2ecf20Sopenharmony_ci#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */ 318c2ecf20Sopenharmony_ci#define DENALI_MAP10 (2 << 26) /* high-level control plane */ 328c2ecf20Sopenharmony_ci#define DENALI_MAP11 (3 << 26) /* direct controller access */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* MAP11 access cycle type */ 358c2ecf20Sopenharmony_ci#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */ 368c2ecf20Sopenharmony_ci#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */ 378c2ecf20Sopenharmony_ci#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define DENALI_BANK(denali) ((denali)->active_bank << 24) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define DENALI_INVALID_BANK -1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic struct denali_chip *to_denali_chip(struct nand_chip *chip) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return container_of(chip, struct denali_chip, chip); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct denali_controller *to_denali_controller(struct nand_chip *chip) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return container_of(chip->controller, struct denali_controller, 518c2ecf20Sopenharmony_ci controller); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Direct Addressing - the slave address forms the control information (command 568c2ecf20Sopenharmony_ci * type, bank, block, and page address). The slave data is the actual data to 578c2ecf20Sopenharmony_ci * be transferred. This mode requires 28 bits of address region allocated. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_cistatic u32 denali_direct_read(struct denali_controller *denali, u32 addr) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci return ioread32(denali->host + addr); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void denali_direct_write(struct denali_controller *denali, u32 addr, 658c2ecf20Sopenharmony_ci u32 data) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci iowrite32(data, denali->host + addr); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * Indexed Addressing - address translation module intervenes in passing the 728c2ecf20Sopenharmony_ci * control information. This mode reduces the required address range. The 738c2ecf20Sopenharmony_ci * control information and transferred data are latched by the registers in 748c2ecf20Sopenharmony_ci * the translation module. 758c2ecf20Sopenharmony_ci */ 768c2ecf20Sopenharmony_cistatic u32 denali_indexed_read(struct denali_controller *denali, u32 addr) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); 798c2ecf20Sopenharmony_ci return ioread32(denali->host + DENALI_INDEXED_DATA); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void denali_indexed_write(struct denali_controller *denali, u32 addr, 838c2ecf20Sopenharmony_ci u32 data) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci iowrite32(addr, denali->host + DENALI_INDEXED_CTRL); 868c2ecf20Sopenharmony_ci iowrite32(data, denali->host + DENALI_INDEXED_DATA); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void denali_enable_irq(struct denali_controller *denali) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (i = 0; i < denali->nbanks; i++) 948c2ecf20Sopenharmony_ci iowrite32(U32_MAX, denali->reg + INTR_EN(i)); 958c2ecf20Sopenharmony_ci iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void denali_disable_irq(struct denali_controller *denali) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci int i; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (i = 0; i < denali->nbanks; i++) 1038c2ecf20Sopenharmony_ci iowrite32(0, denali->reg + INTR_EN(i)); 1048c2ecf20Sopenharmony_ci iowrite32(0, denali->reg + GLOBAL_INT_ENABLE); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void denali_clear_irq(struct denali_controller *denali, 1088c2ecf20Sopenharmony_ci int bank, u32 irq_status) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci /* write one to clear bits */ 1118c2ecf20Sopenharmony_ci iowrite32(irq_status, denali->reg + INTR_STATUS(bank)); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void denali_clear_irq_all(struct denali_controller *denali) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int i; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci for (i = 0; i < denali->nbanks; i++) 1198c2ecf20Sopenharmony_ci denali_clear_irq(denali, i, U32_MAX); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic irqreturn_t denali_isr(int irq, void *dev_id) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct denali_controller *denali = dev_id; 1258c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 1268c2ecf20Sopenharmony_ci u32 irq_status; 1278c2ecf20Sopenharmony_ci int i; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci spin_lock(&denali->irq_lock); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci for (i = 0; i < denali->nbanks; i++) { 1328c2ecf20Sopenharmony_ci irq_status = ioread32(denali->reg + INTR_STATUS(i)); 1338c2ecf20Sopenharmony_ci if (irq_status) 1348c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci denali_clear_irq(denali, i, irq_status); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (i != denali->active_bank) 1398c2ecf20Sopenharmony_ci continue; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci denali->irq_status |= irq_status; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (denali->irq_status & denali->irq_mask) 1448c2ecf20Sopenharmony_ci complete(&denali->complete); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci spin_unlock(&denali->irq_lock); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void denali_reset_irq(struct denali_controller *denali) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned long flags; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci spin_lock_irqsave(&denali->irq_lock, flags); 1578c2ecf20Sopenharmony_ci denali->irq_status = 0; 1588c2ecf20Sopenharmony_ci denali->irq_mask = 0; 1598c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&denali->irq_lock, flags); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic u32 denali_wait_for_irq(struct denali_controller *denali, u32 irq_mask) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci unsigned long time_left, flags; 1658c2ecf20Sopenharmony_ci u32 irq_status; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci spin_lock_irqsave(&denali->irq_lock, flags); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci irq_status = denali->irq_status; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (irq_mask & irq_status) { 1728c2ecf20Sopenharmony_ci /* return immediately if the IRQ has already happened. */ 1738c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&denali->irq_lock, flags); 1748c2ecf20Sopenharmony_ci return irq_status; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci denali->irq_mask = irq_mask; 1788c2ecf20Sopenharmony_ci reinit_completion(&denali->complete); 1798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&denali->irq_lock, flags); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci time_left = wait_for_completion_timeout(&denali->complete, 1828c2ecf20Sopenharmony_ci msecs_to_jiffies(1000)); 1838c2ecf20Sopenharmony_ci if (!time_left) { 1848c2ecf20Sopenharmony_ci dev_err(denali->dev, "timeout while waiting for irq 0x%x\n", 1858c2ecf20Sopenharmony_ci irq_mask); 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return denali->irq_status; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic void denali_select_target(struct nand_chip *chip, int cs) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 1958c2ecf20Sopenharmony_ci struct denali_chip_sel *sel = &to_denali_chip(chip)->sels[cs]; 1968c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci denali->active_bank = sel->bank; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci iowrite32(1 << (chip->phys_erase_shift - chip->page_shift), 2018c2ecf20Sopenharmony_ci denali->reg + PAGES_PER_BLOCK); 2028c2ecf20Sopenharmony_ci iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0, 2038c2ecf20Sopenharmony_ci denali->reg + DEVICE_WIDTH); 2048c2ecf20Sopenharmony_ci iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE); 2058c2ecf20Sopenharmony_ci iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE); 2068c2ecf20Sopenharmony_ci iowrite32(chip->options & NAND_ROW_ADDR_3 ? 2078c2ecf20Sopenharmony_ci 0 : TWO_ROW_ADDR_CYCLES__FLAG, 2088c2ecf20Sopenharmony_ci denali->reg + TWO_ROW_ADDR_CYCLES); 2098c2ecf20Sopenharmony_ci iowrite32(FIELD_PREP(ECC_CORRECTION__ERASE_THRESHOLD, 1) | 2108c2ecf20Sopenharmony_ci FIELD_PREP(ECC_CORRECTION__VALUE, chip->ecc.strength), 2118c2ecf20Sopenharmony_ci denali->reg + ECC_CORRECTION); 2128c2ecf20Sopenharmony_ci iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE); 2138c2ecf20Sopenharmony_ci iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE); 2148c2ecf20Sopenharmony_ci iowrite32(chip->ecc.steps, denali->reg + CFG_NUM_DATA_BLOCKS); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (chip->options & NAND_KEEP_TIMINGS) 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* update timing registers unless NAND_KEEP_TIMINGS is set */ 2208c2ecf20Sopenharmony_ci iowrite32(sel->hwhr2_and_we_2_re, denali->reg + TWHR2_AND_WE_2_RE); 2218c2ecf20Sopenharmony_ci iowrite32(sel->tcwaw_and_addr_2_data, 2228c2ecf20Sopenharmony_ci denali->reg + TCWAW_AND_ADDR_2_DATA); 2238c2ecf20Sopenharmony_ci iowrite32(sel->re_2_we, denali->reg + RE_2_WE); 2248c2ecf20Sopenharmony_ci iowrite32(sel->acc_clks, denali->reg + ACC_CLKS); 2258c2ecf20Sopenharmony_ci iowrite32(sel->rdwr_en_lo_cnt, denali->reg + RDWR_EN_LO_CNT); 2268c2ecf20Sopenharmony_ci iowrite32(sel->rdwr_en_hi_cnt, denali->reg + RDWR_EN_HI_CNT); 2278c2ecf20Sopenharmony_ci iowrite32(sel->cs_setup_cnt, denali->reg + CS_SETUP_CNT); 2288c2ecf20Sopenharmony_ci iowrite32(sel->re_2_re, denali->reg + RE_2_RE); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int denali_change_column(struct nand_chip *chip, unsigned int offset, 2328c2ecf20Sopenharmony_ci void *buf, unsigned int len, bool write) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci if (write) 2358c2ecf20Sopenharmony_ci return nand_change_write_column_op(chip, offset, buf, len, 2368c2ecf20Sopenharmony_ci false); 2378c2ecf20Sopenharmony_ci else 2388c2ecf20Sopenharmony_ci return nand_change_read_column_op(chip, offset, buf, len, 2398c2ecf20Sopenharmony_ci false); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int denali_payload_xfer(struct nand_chip *chip, void *buf, bool write) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 2458c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 2468c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 2478c2ecf20Sopenharmony_ci int writesize = mtd->writesize; 2488c2ecf20Sopenharmony_ci int oob_skip = denali->oob_skip_bytes; 2498c2ecf20Sopenharmony_ci int ret, i, pos, len; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 2528c2ecf20Sopenharmony_ci pos = i * (ecc->size + ecc->bytes); 2538c2ecf20Sopenharmony_ci len = ecc->size; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (pos >= writesize) { 2568c2ecf20Sopenharmony_ci pos += oob_skip; 2578c2ecf20Sopenharmony_ci } else if (pos + len > writesize) { 2588c2ecf20Sopenharmony_ci /* This chunk overwraps the BBM area. Must be split */ 2598c2ecf20Sopenharmony_ci ret = denali_change_column(chip, pos, buf, 2608c2ecf20Sopenharmony_ci writesize - pos, write); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci buf += writesize - pos; 2658c2ecf20Sopenharmony_ci len -= writesize - pos; 2668c2ecf20Sopenharmony_ci pos = writesize + oob_skip; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci ret = denali_change_column(chip, pos, buf, len, write); 2708c2ecf20Sopenharmony_ci if (ret) 2718c2ecf20Sopenharmony_ci return ret; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci buf += len; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int denali_oob_xfer(struct nand_chip *chip, void *buf, bool write) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 2828c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 2838c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 2848c2ecf20Sopenharmony_ci int writesize = mtd->writesize; 2858c2ecf20Sopenharmony_ci int oobsize = mtd->oobsize; 2868c2ecf20Sopenharmony_ci int oob_skip = denali->oob_skip_bytes; 2878c2ecf20Sopenharmony_ci int ret, i, pos, len; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* BBM at the beginning of the OOB area */ 2908c2ecf20Sopenharmony_ci ret = denali_change_column(chip, writesize, buf, oob_skip, write); 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci return ret; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci buf += oob_skip; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 2978c2ecf20Sopenharmony_ci pos = ecc->size + i * (ecc->size + ecc->bytes); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (i == ecc->steps - 1) 3008c2ecf20Sopenharmony_ci /* The last chunk includes OOB free */ 3018c2ecf20Sopenharmony_ci len = writesize + oobsize - pos - oob_skip; 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci len = ecc->bytes; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (pos >= writesize) { 3068c2ecf20Sopenharmony_ci pos += oob_skip; 3078c2ecf20Sopenharmony_ci } else if (pos + len > writesize) { 3088c2ecf20Sopenharmony_ci /* This chunk overwraps the BBM area. Must be split */ 3098c2ecf20Sopenharmony_ci ret = denali_change_column(chip, pos, buf, 3108c2ecf20Sopenharmony_ci writesize - pos, write); 3118c2ecf20Sopenharmony_ci if (ret) 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci buf += writesize - pos; 3158c2ecf20Sopenharmony_ci len -= writesize - pos; 3168c2ecf20Sopenharmony_ci pos = writesize + oob_skip; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci ret = denali_change_column(chip, pos, buf, len, write); 3208c2ecf20Sopenharmony_ci if (ret) 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci buf += len; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int denali_read_raw(struct nand_chip *chip, void *buf, void *oob_buf, 3308c2ecf20Sopenharmony_ci int page) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci int ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!buf && !oob_buf) 3358c2ecf20Sopenharmony_ci return -EINVAL; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = nand_read_page_op(chip, page, 0, NULL, 0); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci return ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (buf) { 3428c2ecf20Sopenharmony_ci ret = denali_payload_xfer(chip, buf, false); 3438c2ecf20Sopenharmony_ci if (ret) 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (oob_buf) { 3488c2ecf20Sopenharmony_ci ret = denali_oob_xfer(chip, oob_buf, false); 3498c2ecf20Sopenharmony_ci if (ret) 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int denali_write_raw(struct nand_chip *chip, const void *buf, 3578c2ecf20Sopenharmony_ci const void *oob_buf, int page) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci int ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (!buf && !oob_buf) 3628c2ecf20Sopenharmony_ci return -EINVAL; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0); 3658c2ecf20Sopenharmony_ci if (ret) 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (buf) { 3698c2ecf20Sopenharmony_ci ret = denali_payload_xfer(chip, (void *)buf, true); 3708c2ecf20Sopenharmony_ci if (ret) 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (oob_buf) { 3758c2ecf20Sopenharmony_ci ret = denali_oob_xfer(chip, (void *)oob_buf, true); 3768c2ecf20Sopenharmony_ci if (ret) 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int denali_read_page_raw(struct nand_chip *chip, u8 *buf, 3848c2ecf20Sopenharmony_ci int oob_required, int page) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci return denali_read_raw(chip, buf, oob_required ? chip->oob_poi : NULL, 3878c2ecf20Sopenharmony_ci page); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic int denali_write_page_raw(struct nand_chip *chip, const u8 *buf, 3918c2ecf20Sopenharmony_ci int oob_required, int page) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci return denali_write_raw(chip, buf, oob_required ? chip->oob_poi : NULL, 3948c2ecf20Sopenharmony_ci page); 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int denali_read_oob(struct nand_chip *chip, int page) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci return denali_read_raw(chip, NULL, chip->oob_poi, page); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int denali_write_oob(struct nand_chip *chip, int page) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci return denali_write_raw(chip, NULL, chip->oob_poi, page); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int denali_check_erased_page(struct nand_chip *chip, u8 *buf, 4088c2ecf20Sopenharmony_ci unsigned long uncor_ecc_flags, 4098c2ecf20Sopenharmony_ci unsigned int max_bitflips) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 4128c2ecf20Sopenharmony_ci struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats; 4138c2ecf20Sopenharmony_ci struct nand_ecc_ctrl *ecc = &chip->ecc; 4148c2ecf20Sopenharmony_ci u8 *ecc_code = chip->oob_poi + denali->oob_skip_bytes; 4158c2ecf20Sopenharmony_ci int i, stat; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (i = 0; i < ecc->steps; i++) { 4188c2ecf20Sopenharmony_ci if (!(uncor_ecc_flags & BIT(i))) 4198c2ecf20Sopenharmony_ci continue; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci stat = nand_check_erased_ecc_chunk(buf, ecc->size, ecc_code, 4228c2ecf20Sopenharmony_ci ecc->bytes, NULL, 0, 4238c2ecf20Sopenharmony_ci ecc->strength); 4248c2ecf20Sopenharmony_ci if (stat < 0) { 4258c2ecf20Sopenharmony_ci ecc_stats->failed++; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci ecc_stats->corrected += stat; 4288c2ecf20Sopenharmony_ci max_bitflips = max_t(unsigned int, max_bitflips, stat); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci buf += ecc->size; 4328c2ecf20Sopenharmony_ci ecc_code += ecc->bytes; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return max_bitflips; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int denali_hw_ecc_fixup(struct nand_chip *chip, 4398c2ecf20Sopenharmony_ci unsigned long *uncor_ecc_flags) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 4428c2ecf20Sopenharmony_ci struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats; 4438c2ecf20Sopenharmony_ci int bank = denali->active_bank; 4448c2ecf20Sopenharmony_ci u32 ecc_cor; 4458c2ecf20Sopenharmony_ci unsigned int max_bitflips; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank)); 4488c2ecf20Sopenharmony_ci ecc_cor >>= ECC_COR_INFO__SHIFT(bank); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) { 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * This flag is set when uncorrectable error occurs at least in 4538c2ecf20Sopenharmony_ci * one ECC sector. We can not know "how many sectors", or 4548c2ecf20Sopenharmony_ci * "which sector(s)". We need erase-page check for all sectors. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0); 4578c2ecf20Sopenharmony_ci return 0; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci max_bitflips = FIELD_GET(ECC_COR_INFO__MAX_ERRORS, ecc_cor); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci /* 4638c2ecf20Sopenharmony_ci * The register holds the maximum of per-sector corrected bitflips. 4648c2ecf20Sopenharmony_ci * This is suitable for the return value of the ->read_page() callback. 4658c2ecf20Sopenharmony_ci * Unfortunately, we can not know the total number of corrected bits in 4668c2ecf20Sopenharmony_ci * the page. Increase the stats by max_bitflips. (compromised solution) 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci ecc_stats->corrected += max_bitflips; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return max_bitflips; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int denali_sw_ecc_fixup(struct nand_chip *chip, 4748c2ecf20Sopenharmony_ci unsigned long *uncor_ecc_flags, u8 *buf) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 4778c2ecf20Sopenharmony_ci struct mtd_ecc_stats *ecc_stats = &nand_to_mtd(chip)->ecc_stats; 4788c2ecf20Sopenharmony_ci unsigned int ecc_size = chip->ecc.size; 4798c2ecf20Sopenharmony_ci unsigned int bitflips = 0; 4808c2ecf20Sopenharmony_ci unsigned int max_bitflips = 0; 4818c2ecf20Sopenharmony_ci u32 err_addr, err_cor_info; 4828c2ecf20Sopenharmony_ci unsigned int err_byte, err_sector, err_device; 4838c2ecf20Sopenharmony_ci u8 err_cor_value; 4848c2ecf20Sopenharmony_ci unsigned int prev_sector = 0; 4858c2ecf20Sopenharmony_ci u32 irq_status; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci denali_reset_irq(denali); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci do { 4908c2ecf20Sopenharmony_ci err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS); 4918c2ecf20Sopenharmony_ci err_sector = FIELD_GET(ECC_ERROR_ADDRESS__SECTOR, err_addr); 4928c2ecf20Sopenharmony_ci err_byte = FIELD_GET(ECC_ERROR_ADDRESS__OFFSET, err_addr); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO); 4958c2ecf20Sopenharmony_ci err_cor_value = FIELD_GET(ERR_CORRECTION_INFO__BYTE, 4968c2ecf20Sopenharmony_ci err_cor_info); 4978c2ecf20Sopenharmony_ci err_device = FIELD_GET(ERR_CORRECTION_INFO__DEVICE, 4988c2ecf20Sopenharmony_ci err_cor_info); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* reset the bitflip counter when crossing ECC sector */ 5018c2ecf20Sopenharmony_ci if (err_sector != prev_sector) 5028c2ecf20Sopenharmony_ci bitflips = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (err_cor_info & ERR_CORRECTION_INFO__UNCOR) { 5058c2ecf20Sopenharmony_ci /* 5068c2ecf20Sopenharmony_ci * Check later if this is a real ECC error, or 5078c2ecf20Sopenharmony_ci * an erased sector. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci *uncor_ecc_flags |= BIT(err_sector); 5108c2ecf20Sopenharmony_ci } else if (err_byte < ecc_size) { 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * If err_byte is larger than ecc_size, means error 5138c2ecf20Sopenharmony_ci * happened in OOB, so we ignore it. It's no need for 5148c2ecf20Sopenharmony_ci * us to correct it err_device is represented the NAND 5158c2ecf20Sopenharmony_ci * error bits are happened in if there are more than 5168c2ecf20Sopenharmony_ci * one NAND connected. 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ci int offset; 5198c2ecf20Sopenharmony_ci unsigned int flips_in_byte; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci offset = (err_sector * ecc_size + err_byte) * 5228c2ecf20Sopenharmony_ci denali->devs_per_cs + err_device; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* correct the ECC error */ 5258c2ecf20Sopenharmony_ci flips_in_byte = hweight8(buf[offset] ^ err_cor_value); 5268c2ecf20Sopenharmony_ci buf[offset] ^= err_cor_value; 5278c2ecf20Sopenharmony_ci ecc_stats->corrected += flips_in_byte; 5288c2ecf20Sopenharmony_ci bitflips += flips_in_byte; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci max_bitflips = max(max_bitflips, bitflips); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci prev_sector = err_sector; 5348c2ecf20Sopenharmony_ci } while (!(err_cor_info & ERR_CORRECTION_INFO__LAST_ERR)); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* 5378c2ecf20Sopenharmony_ci * Once handle all ECC errors, controller will trigger an 5388c2ecf20Sopenharmony_ci * ECC_TRANSACTION_DONE interrupt. 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE); 5418c2ecf20Sopenharmony_ci if (!(irq_status & INTR__ECC_TRANSACTION_DONE)) 5428c2ecf20Sopenharmony_ci return -EIO; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return max_bitflips; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void denali_setup_dma64(struct denali_controller *denali, 5488c2ecf20Sopenharmony_ci dma_addr_t dma_addr, int page, bool write) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci u32 mode; 5518c2ecf20Sopenharmony_ci const int page_count = 1; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci mode = DENALI_MAP10 | DENALI_BANK(denali) | page; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* DMA is a three step process */ 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* 5588c2ecf20Sopenharmony_ci * 1. setup transfer type, interrupt when complete, 5598c2ecf20Sopenharmony_ci * burst len = 64 bytes, the number of pages 5608c2ecf20Sopenharmony_ci */ 5618c2ecf20Sopenharmony_ci denali->host_write(denali, mode, 5628c2ecf20Sopenharmony_ci 0x01002000 | (64 << 16) | 5638c2ecf20Sopenharmony_ci (write ? BIT(8) : 0) | page_count); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* 2. set memory low address */ 5668c2ecf20Sopenharmony_ci denali->host_write(denali, mode, lower_32_bits(dma_addr)); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 3. set memory high address */ 5698c2ecf20Sopenharmony_ci denali->host_write(denali, mode, upper_32_bits(dma_addr)); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic void denali_setup_dma32(struct denali_controller *denali, 5738c2ecf20Sopenharmony_ci dma_addr_t dma_addr, int page, bool write) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci u32 mode; 5768c2ecf20Sopenharmony_ci const int page_count = 1; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci mode = DENALI_MAP10 | DENALI_BANK(denali); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* DMA is a four step process */ 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* 1. setup transfer type and # of pages */ 5838c2ecf20Sopenharmony_ci denali->host_write(denali, mode | page, 5848c2ecf20Sopenharmony_ci 0x2000 | (write ? BIT(8) : 0) | page_count); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* 2. set memory high address bits 23:8 */ 5878c2ecf20Sopenharmony_ci denali->host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* 3. set memory low address bits 23:8 */ 5908c2ecf20Sopenharmony_ci denali->host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* 4. interrupt when complete, burst len = 64 bytes */ 5938c2ecf20Sopenharmony_ci denali->host_write(denali, mode | 0x14000, 0x2400); 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int denali_pio_read(struct denali_controller *denali, u32 *buf, 5978c2ecf20Sopenharmony_ci size_t size, int page) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; 6008c2ecf20Sopenharmony_ci u32 irq_status, ecc_err_mask; 6018c2ecf20Sopenharmony_ci int i; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) 6048c2ecf20Sopenharmony_ci ecc_err_mask = INTR__ECC_UNCOR_ERR; 6058c2ecf20Sopenharmony_ci else 6068c2ecf20Sopenharmony_ci ecc_err_mask = INTR__ECC_ERR; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci denali_reset_irq(denali); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci for (i = 0; i < size / 4; i++) 6118c2ecf20Sopenharmony_ci buf[i] = denali->host_read(denali, addr); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC); 6148c2ecf20Sopenharmony_ci if (!(irq_status & INTR__PAGE_XFER_INC)) 6158c2ecf20Sopenharmony_ci return -EIO; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (irq_status & INTR__ERASED_PAGE) 6188c2ecf20Sopenharmony_ci memset(buf, 0xff, size); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci return irq_status & ecc_err_mask ? -EBADMSG : 0; 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int denali_pio_write(struct denali_controller *denali, const u32 *buf, 6248c2ecf20Sopenharmony_ci size_t size, int page) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci u32 addr = DENALI_MAP01 | DENALI_BANK(denali) | page; 6278c2ecf20Sopenharmony_ci u32 irq_status; 6288c2ecf20Sopenharmony_ci int i; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci denali_reset_irq(denali); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci for (i = 0; i < size / 4; i++) 6338c2ecf20Sopenharmony_ci denali->host_write(denali, addr, buf[i]); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci irq_status = denali_wait_for_irq(denali, 6368c2ecf20Sopenharmony_ci INTR__PROGRAM_COMP | 6378c2ecf20Sopenharmony_ci INTR__PROGRAM_FAIL); 6388c2ecf20Sopenharmony_ci if (!(irq_status & INTR__PROGRAM_COMP)) 6398c2ecf20Sopenharmony_ci return -EIO; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic int denali_pio_xfer(struct denali_controller *denali, void *buf, 6458c2ecf20Sopenharmony_ci size_t size, int page, bool write) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci if (write) 6488c2ecf20Sopenharmony_ci return denali_pio_write(denali, buf, size, page); 6498c2ecf20Sopenharmony_ci else 6508c2ecf20Sopenharmony_ci return denali_pio_read(denali, buf, size, page); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int denali_dma_xfer(struct denali_controller *denali, void *buf, 6548c2ecf20Sopenharmony_ci size_t size, int page, bool write) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 6578c2ecf20Sopenharmony_ci u32 irq_mask, irq_status, ecc_err_mask; 6588c2ecf20Sopenharmony_ci enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE; 6598c2ecf20Sopenharmony_ci int ret = 0; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci dma_addr = dma_map_single(denali->dev, buf, size, dir); 6628c2ecf20Sopenharmony_ci if (dma_mapping_error(denali->dev, dma_addr)) { 6638c2ecf20Sopenharmony_ci dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n"); 6648c2ecf20Sopenharmony_ci return denali_pio_xfer(denali, buf, size, page, write); 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (write) { 6688c2ecf20Sopenharmony_ci /* 6698c2ecf20Sopenharmony_ci * INTR__PROGRAM_COMP is never asserted for the DMA transfer. 6708c2ecf20Sopenharmony_ci * We can use INTR__DMA_CMD_COMP instead. This flag is asserted 6718c2ecf20Sopenharmony_ci * when the page program is completed. 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL; 6748c2ecf20Sopenharmony_ci ecc_err_mask = 0; 6758c2ecf20Sopenharmony_ci } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) { 6768c2ecf20Sopenharmony_ci irq_mask = INTR__DMA_CMD_COMP; 6778c2ecf20Sopenharmony_ci ecc_err_mask = INTR__ECC_UNCOR_ERR; 6788c2ecf20Sopenharmony_ci } else { 6798c2ecf20Sopenharmony_ci irq_mask = INTR__DMA_CMD_COMP; 6808c2ecf20Sopenharmony_ci ecc_err_mask = INTR__ECC_ERR; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci iowrite32(DMA_ENABLE__FLAG, denali->reg + DMA_ENABLE); 6848c2ecf20Sopenharmony_ci /* 6858c2ecf20Sopenharmony_ci * The ->setup_dma() hook kicks DMA by using the data/command 6868c2ecf20Sopenharmony_ci * interface, which belongs to a different AXI port from the 6878c2ecf20Sopenharmony_ci * register interface. Read back the register to avoid a race. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci ioread32(denali->reg + DMA_ENABLE); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci denali_reset_irq(denali); 6928c2ecf20Sopenharmony_ci denali->setup_dma(denali, dma_addr, page, write); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci irq_status = denali_wait_for_irq(denali, irq_mask); 6958c2ecf20Sopenharmony_ci if (!(irq_status & INTR__DMA_CMD_COMP)) 6968c2ecf20Sopenharmony_ci ret = -EIO; 6978c2ecf20Sopenharmony_ci else if (irq_status & ecc_err_mask) 6988c2ecf20Sopenharmony_ci ret = -EBADMSG; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci iowrite32(0, denali->reg + DMA_ENABLE); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci dma_unmap_single(denali->dev, dma_addr, size, dir); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (irq_status & INTR__ERASED_PAGE) 7058c2ecf20Sopenharmony_ci memset(buf, 0xff, size); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci return ret; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic int denali_page_xfer(struct nand_chip *chip, void *buf, size_t size, 7118c2ecf20Sopenharmony_ci int page, bool write) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci denali_select_target(chip, chip->cur_cs); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (denali->dma_avail) 7188c2ecf20Sopenharmony_ci return denali_dma_xfer(denali, buf, size, page, write); 7198c2ecf20Sopenharmony_ci else 7208c2ecf20Sopenharmony_ci return denali_pio_xfer(denali, buf, size, page, write); 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic int denali_read_page(struct nand_chip *chip, u8 *buf, 7248c2ecf20Sopenharmony_ci int oob_required, int page) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 7278c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7288c2ecf20Sopenharmony_ci unsigned long uncor_ecc_flags = 0; 7298c2ecf20Sopenharmony_ci int stat = 0; 7308c2ecf20Sopenharmony_ci int ret; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci ret = denali_page_xfer(chip, buf, mtd->writesize, page, false); 7338c2ecf20Sopenharmony_ci if (ret && ret != -EBADMSG) 7348c2ecf20Sopenharmony_ci return ret; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) 7378c2ecf20Sopenharmony_ci stat = denali_hw_ecc_fixup(chip, &uncor_ecc_flags); 7388c2ecf20Sopenharmony_ci else if (ret == -EBADMSG) 7398c2ecf20Sopenharmony_ci stat = denali_sw_ecc_fixup(chip, &uncor_ecc_flags, buf); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (stat < 0) 7428c2ecf20Sopenharmony_ci return stat; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (uncor_ecc_flags) { 7458c2ecf20Sopenharmony_ci ret = denali_read_oob(chip, page); 7468c2ecf20Sopenharmony_ci if (ret) 7478c2ecf20Sopenharmony_ci return ret; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci stat = denali_check_erased_page(chip, buf, 7508c2ecf20Sopenharmony_ci uncor_ecc_flags, stat); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return stat; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int denali_write_page(struct nand_chip *chip, const u8 *buf, 7578c2ecf20Sopenharmony_ci int oob_required, int page) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return denali_page_xfer(chip, (void *)buf, mtd->writesize, page, true); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int denali_setup_interface(struct nand_chip *chip, int chipnr, 7658c2ecf20Sopenharmony_ci const struct nand_interface_config *conf) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci static const unsigned int data_setup_on_host = 10000; 7688c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 7698c2ecf20Sopenharmony_ci struct denali_chip_sel *sel; 7708c2ecf20Sopenharmony_ci const struct nand_sdr_timings *timings; 7718c2ecf20Sopenharmony_ci unsigned long t_x, mult_x; 7728c2ecf20Sopenharmony_ci int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data; 7738c2ecf20Sopenharmony_ci int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup; 7748c2ecf20Sopenharmony_ci int addr_2_data_mask; 7758c2ecf20Sopenharmony_ci u32 tmp; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci timings = nand_get_sdr_timings(conf); 7788c2ecf20Sopenharmony_ci if (IS_ERR(timings)) 7798c2ecf20Sopenharmony_ci return PTR_ERR(timings); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* clk_x period in picoseconds */ 7828c2ecf20Sopenharmony_ci t_x = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate); 7838c2ecf20Sopenharmony_ci if (!t_x) 7848c2ecf20Sopenharmony_ci return -EINVAL; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* 7878c2ecf20Sopenharmony_ci * The bus interface clock, clk_x, is phase aligned with the core clock. 7888c2ecf20Sopenharmony_ci * The clk_x is an integral multiple N of the core clk. The value N is 7898c2ecf20Sopenharmony_ci * configured at IP delivery time, and its available value is 4, 5, 6. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci mult_x = DIV_ROUND_CLOSEST_ULL(denali->clk_x_rate, denali->clk_rate); 7928c2ecf20Sopenharmony_ci if (mult_x < 4 || mult_x > 6) 7938c2ecf20Sopenharmony_ci return -EINVAL; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci sel = &to_denali_chip(chip)->sels[chipnr]; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* tRWH -> RE_2_WE */ 8018c2ecf20Sopenharmony_ci re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_x); 8028c2ecf20Sopenharmony_ci re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + RE_2_WE); 8058c2ecf20Sopenharmony_ci tmp &= ~RE_2_WE__VALUE; 8068c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(RE_2_WE__VALUE, re_2_we); 8078c2ecf20Sopenharmony_ci sel->re_2_we = tmp; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* tRHZ -> RE_2_RE */ 8108c2ecf20Sopenharmony_ci re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_x); 8118c2ecf20Sopenharmony_ci re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + RE_2_RE); 8148c2ecf20Sopenharmony_ci tmp &= ~RE_2_RE__VALUE; 8158c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(RE_2_RE__VALUE, re_2_re); 8168c2ecf20Sopenharmony_ci sel->re_2_re = tmp; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* 8198c2ecf20Sopenharmony_ci * tCCS, tWHR -> WE_2_RE 8208c2ecf20Sopenharmony_ci * 8218c2ecf20Sopenharmony_ci * With WE_2_RE properly set, the Denali controller automatically takes 8228c2ecf20Sopenharmony_ci * care of the delay; the driver need not set NAND_WAIT_TCCS. 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci we_2_re = DIV_ROUND_UP(max(timings->tCCS_min, timings->tWHR_min), t_x); 8258c2ecf20Sopenharmony_ci we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE); 8288c2ecf20Sopenharmony_ci tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE; 8298c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(TWHR2_AND_WE_2_RE__WE_2_RE, we_2_re); 8308c2ecf20Sopenharmony_ci sel->hwhr2_and_we_2_re = tmp; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* tADL -> ADDR_2_DATA */ 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci /* for older versions, ADDR_2_DATA is only 6 bit wide */ 8358c2ecf20Sopenharmony_ci addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; 8368c2ecf20Sopenharmony_ci if (denali->revision < 0x0501) 8378c2ecf20Sopenharmony_ci addr_2_data_mask >>= 1; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_x); 8408c2ecf20Sopenharmony_ci addr_2_data = min_t(int, addr_2_data, addr_2_data_mask); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA); 8438c2ecf20Sopenharmony_ci tmp &= ~TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA; 8448c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA, addr_2_data); 8458c2ecf20Sopenharmony_ci sel->tcwaw_and_addr_2_data = tmp; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* tREH, tWH -> RDWR_EN_HI_CNT */ 8488c2ecf20Sopenharmony_ci rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min), 8498c2ecf20Sopenharmony_ci t_x); 8508c2ecf20Sopenharmony_ci rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + RDWR_EN_HI_CNT); 8538c2ecf20Sopenharmony_ci tmp &= ~RDWR_EN_HI_CNT__VALUE; 8548c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(RDWR_EN_HI_CNT__VALUE, rdwr_en_hi); 8558c2ecf20Sopenharmony_ci sel->rdwr_en_hi_cnt = tmp; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* 8588c2ecf20Sopenharmony_ci * tREA -> ACC_CLKS 8598c2ecf20Sopenharmony_ci * tRP, tWP, tRHOH, tRC, tWC -> RDWR_EN_LO_CNT 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* 8638c2ecf20Sopenharmony_ci * Determine the minimum of acc_clks to meet the setup timing when 8648c2ecf20Sopenharmony_ci * capturing the incoming data. 8658c2ecf20Sopenharmony_ci * 8668c2ecf20Sopenharmony_ci * The delay on the chip side is well-defined as tREA, but we need to 8678c2ecf20Sopenharmony_ci * take additional delay into account. This includes a certain degree 8688c2ecf20Sopenharmony_ci * of unknowledge, such as signal propagation delays on the PCB and 8698c2ecf20Sopenharmony_ci * in the SoC, load capacity of the I/O pins, etc. 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_ci acc_clks = DIV_ROUND_UP(timings->tREA_max + data_setup_on_host, t_x); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* Determine the minimum of rdwr_en_lo_cnt from RE#/WE# pulse width */ 8748c2ecf20Sopenharmony_ci rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min), t_x); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* Extend rdwr_en_lo to meet the data hold timing */ 8778c2ecf20Sopenharmony_ci rdwr_en_lo = max_t(int, rdwr_en_lo, 8788c2ecf20Sopenharmony_ci acc_clks - timings->tRHOH_min / t_x); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Extend rdwr_en_lo to meet the requirement for RE#/WE# cycle time */ 8818c2ecf20Sopenharmony_ci rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min), 8828c2ecf20Sopenharmony_ci t_x); 8838c2ecf20Sopenharmony_ci rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi); 8848c2ecf20Sopenharmony_ci rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* Center the data latch timing for extra safety */ 8878c2ecf20Sopenharmony_ci acc_clks = (acc_clks + rdwr_en_lo + 8888c2ecf20Sopenharmony_ci DIV_ROUND_UP(timings->tRHOH_min, t_x)) / 2; 8898c2ecf20Sopenharmony_ci acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + ACC_CLKS); 8928c2ecf20Sopenharmony_ci tmp &= ~ACC_CLKS__VALUE; 8938c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(ACC_CLKS__VALUE, acc_clks); 8948c2ecf20Sopenharmony_ci sel->acc_clks = tmp; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + RDWR_EN_LO_CNT); 8978c2ecf20Sopenharmony_ci tmp &= ~RDWR_EN_LO_CNT__VALUE; 8988c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(RDWR_EN_LO_CNT__VALUE, rdwr_en_lo); 8998c2ecf20Sopenharmony_ci sel->rdwr_en_lo_cnt = tmp; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* tCS, tCEA -> CS_SETUP_CNT */ 9028c2ecf20Sopenharmony_ci cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_x) - rdwr_en_lo, 9038c2ecf20Sopenharmony_ci (int)DIV_ROUND_UP(timings->tCEA_max, t_x) - acc_clks, 9048c2ecf20Sopenharmony_ci 0); 9058c2ecf20Sopenharmony_ci cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci tmp = ioread32(denali->reg + CS_SETUP_CNT); 9088c2ecf20Sopenharmony_ci tmp &= ~CS_SETUP_CNT__VALUE; 9098c2ecf20Sopenharmony_ci tmp |= FIELD_PREP(CS_SETUP_CNT__VALUE, cs_setup); 9108c2ecf20Sopenharmony_ci sel->cs_setup_cnt = tmp; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ciint denali_calc_ecc_bytes(int step_size, int strength) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci /* BCH code. Denali requires ecc.bytes to be multiple of 2 */ 9188c2ecf20Sopenharmony_ci return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2; 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(denali_calc_ecc_bytes); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int denali_ooblayout_ecc(struct mtd_info *mtd, int section, 9238c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 9268c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (section > 0) 9298c2ecf20Sopenharmony_ci return -ERANGE; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci oobregion->offset = denali->oob_skip_bytes; 9328c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.total; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci return 0; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int denali_ooblayout_free(struct mtd_info *mtd, int section, 9388c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 9418c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (section > 0) 9448c2ecf20Sopenharmony_ci return -ERANGE; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci oobregion->offset = chip->ecc.total + denali->oob_skip_bytes; 9478c2ecf20Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci return 0; 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops denali_ooblayout_ops = { 9538c2ecf20Sopenharmony_ci .ecc = denali_ooblayout_ecc, 9548c2ecf20Sopenharmony_ci .free = denali_ooblayout_free, 9558c2ecf20Sopenharmony_ci}; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic int denali_multidev_fixup(struct nand_chip *chip) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 9608c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 9618c2ecf20Sopenharmony_ci struct nand_memory_organization *memorg; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci memorg = nanddev_get_memorg(&chip->base); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* 9668c2ecf20Sopenharmony_ci * Support for multi device: 9678c2ecf20Sopenharmony_ci * When the IP configuration is x16 capable and two x8 chips are 9688c2ecf20Sopenharmony_ci * connected in parallel, DEVICES_CONNECTED should be set to 2. 9698c2ecf20Sopenharmony_ci * In this case, the core framework knows nothing about this fact, 9708c2ecf20Sopenharmony_ci * so we should tell it the _logical_ pagesize and anything necessary. 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_ci denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED); 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* 9758c2ecf20Sopenharmony_ci * On some SoCs, DEVICES_CONNECTED is not auto-detected. 9768c2ecf20Sopenharmony_ci * For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case. 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci if (denali->devs_per_cs == 0) { 9798c2ecf20Sopenharmony_ci denali->devs_per_cs = 1; 9808c2ecf20Sopenharmony_ci iowrite32(1, denali->reg + DEVICES_CONNECTED); 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (denali->devs_per_cs == 1) 9848c2ecf20Sopenharmony_ci return 0; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci if (denali->devs_per_cs != 2) { 9878c2ecf20Sopenharmony_ci dev_err(denali->dev, "unsupported number of devices %d\n", 9888c2ecf20Sopenharmony_ci denali->devs_per_cs); 9898c2ecf20Sopenharmony_ci return -EINVAL; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* 2 chips in parallel */ 9938c2ecf20Sopenharmony_ci memorg->pagesize <<= 1; 9948c2ecf20Sopenharmony_ci memorg->oobsize <<= 1; 9958c2ecf20Sopenharmony_ci mtd->size <<= 1; 9968c2ecf20Sopenharmony_ci mtd->erasesize <<= 1; 9978c2ecf20Sopenharmony_ci mtd->writesize <<= 1; 9988c2ecf20Sopenharmony_ci mtd->oobsize <<= 1; 9998c2ecf20Sopenharmony_ci chip->page_shift += 1; 10008c2ecf20Sopenharmony_ci chip->phys_erase_shift += 1; 10018c2ecf20Sopenharmony_ci chip->bbt_erase_shift += 1; 10028c2ecf20Sopenharmony_ci chip->chip_shift += 1; 10038c2ecf20Sopenharmony_ci chip->pagemask <<= 1; 10048c2ecf20Sopenharmony_ci chip->ecc.size <<= 1; 10058c2ecf20Sopenharmony_ci chip->ecc.bytes <<= 1; 10068c2ecf20Sopenharmony_ci chip->ecc.strength <<= 1; 10078c2ecf20Sopenharmony_ci denali->oob_skip_bytes <<= 1; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic int denali_attach_chip(struct nand_chip *chip) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 10158c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 10168c2ecf20Sopenharmony_ci int ret; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci ret = nand_ecc_choose_conf(chip, denali->ecc_caps, 10198c2ecf20Sopenharmony_ci mtd->oobsize - denali->oob_skip_bytes); 10208c2ecf20Sopenharmony_ci if (ret) { 10218c2ecf20Sopenharmony_ci dev_err(denali->dev, "Failed to setup ECC settings.\n"); 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci dev_dbg(denali->dev, 10268c2ecf20Sopenharmony_ci "chosen ECC settings: step=%d, strength=%d, bytes=%d\n", 10278c2ecf20Sopenharmony_ci chip->ecc.size, chip->ecc.strength, chip->ecc.bytes); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci ret = denali_multidev_fixup(chip); 10308c2ecf20Sopenharmony_ci if (ret) 10318c2ecf20Sopenharmony_ci return ret; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci return 0; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic void denali_exec_in8(struct denali_controller *denali, u32 type, 10378c2ecf20Sopenharmony_ci u8 *buf, unsigned int len) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci int i; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 10428c2ecf20Sopenharmony_ci buf[i] = denali->host_read(denali, type | DENALI_BANK(denali)); 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic void denali_exec_in16(struct denali_controller *denali, u32 type, 10468c2ecf20Sopenharmony_ci u8 *buf, unsigned int len) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci u32 data; 10498c2ecf20Sopenharmony_ci int i; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 2) { 10528c2ecf20Sopenharmony_ci data = denali->host_read(denali, type | DENALI_BANK(denali)); 10538c2ecf20Sopenharmony_ci /* bit 31:24 and 15:8 are used for DDR */ 10548c2ecf20Sopenharmony_ci buf[i] = data; 10558c2ecf20Sopenharmony_ci buf[i + 1] = data >> 16; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic void denali_exec_in(struct denali_controller *denali, u32 type, 10608c2ecf20Sopenharmony_ci u8 *buf, unsigned int len, bool width16) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci if (width16) 10638c2ecf20Sopenharmony_ci denali_exec_in16(denali, type, buf, len); 10648c2ecf20Sopenharmony_ci else 10658c2ecf20Sopenharmony_ci denali_exec_in8(denali, type, buf, len); 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic void denali_exec_out8(struct denali_controller *denali, u32 type, 10698c2ecf20Sopenharmony_ci const u8 *buf, unsigned int len) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci int i; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 10748c2ecf20Sopenharmony_ci denali->host_write(denali, type | DENALI_BANK(denali), buf[i]); 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic void denali_exec_out16(struct denali_controller *denali, u32 type, 10788c2ecf20Sopenharmony_ci const u8 *buf, unsigned int len) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci int i; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci for (i = 0; i < len; i += 2) 10838c2ecf20Sopenharmony_ci denali->host_write(denali, type | DENALI_BANK(denali), 10848c2ecf20Sopenharmony_ci buf[i + 1] << 16 | buf[i]); 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cistatic void denali_exec_out(struct denali_controller *denali, u32 type, 10888c2ecf20Sopenharmony_ci const u8 *buf, unsigned int len, bool width16) 10898c2ecf20Sopenharmony_ci{ 10908c2ecf20Sopenharmony_ci if (width16) 10918c2ecf20Sopenharmony_ci denali_exec_out16(denali, type, buf, len); 10928c2ecf20Sopenharmony_ci else 10938c2ecf20Sopenharmony_ci denali_exec_out8(denali, type, buf, len); 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic int denali_exec_waitrdy(struct denali_controller *denali) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci u32 irq_stat; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci /* R/B# pin transitioned from low to high? */ 11018c2ecf20Sopenharmony_ci irq_stat = denali_wait_for_irq(denali, INTR__INT_ACT); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* Just in case nand_operation has multiple NAND_OP_WAITRDY_INSTR. */ 11048c2ecf20Sopenharmony_ci denali_reset_irq(denali); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci return irq_stat & INTR__INT_ACT ? 0 : -EIO; 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic int denali_exec_instr(struct nand_chip *chip, 11108c2ecf20Sopenharmony_ci const struct nand_op_instr *instr) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct denali_controller *denali = to_denali_controller(chip); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci switch (instr->type) { 11158c2ecf20Sopenharmony_ci case NAND_OP_CMD_INSTR: 11168c2ecf20Sopenharmony_ci denali_exec_out8(denali, DENALI_MAP11_CMD, 11178c2ecf20Sopenharmony_ci &instr->ctx.cmd.opcode, 1); 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci case NAND_OP_ADDR_INSTR: 11208c2ecf20Sopenharmony_ci denali_exec_out8(denali, DENALI_MAP11_ADDR, 11218c2ecf20Sopenharmony_ci instr->ctx.addr.addrs, 11228c2ecf20Sopenharmony_ci instr->ctx.addr.naddrs); 11238c2ecf20Sopenharmony_ci return 0; 11248c2ecf20Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 11258c2ecf20Sopenharmony_ci denali_exec_in(denali, DENALI_MAP11_DATA, 11268c2ecf20Sopenharmony_ci instr->ctx.data.buf.in, 11278c2ecf20Sopenharmony_ci instr->ctx.data.len, 11288c2ecf20Sopenharmony_ci !instr->ctx.data.force_8bit && 11298c2ecf20Sopenharmony_ci chip->options & NAND_BUSWIDTH_16); 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 11328c2ecf20Sopenharmony_ci denali_exec_out(denali, DENALI_MAP11_DATA, 11338c2ecf20Sopenharmony_ci instr->ctx.data.buf.out, 11348c2ecf20Sopenharmony_ci instr->ctx.data.len, 11358c2ecf20Sopenharmony_ci !instr->ctx.data.force_8bit && 11368c2ecf20Sopenharmony_ci chip->options & NAND_BUSWIDTH_16); 11378c2ecf20Sopenharmony_ci return 0; 11388c2ecf20Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 11398c2ecf20Sopenharmony_ci return denali_exec_waitrdy(denali); 11408c2ecf20Sopenharmony_ci default: 11418c2ecf20Sopenharmony_ci WARN_ONCE(1, "unsupported NAND instruction type: %d\n", 11428c2ecf20Sopenharmony_ci instr->type); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return -EINVAL; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cistatic int denali_exec_op(struct nand_chip *chip, 11498c2ecf20Sopenharmony_ci const struct nand_operation *op, bool check_only) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci int i, ret; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (check_only) 11548c2ecf20Sopenharmony_ci return 0; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci denali_select_target(chip, op->cs); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * Some commands contain NAND_OP_WAITRDY_INSTR. 11608c2ecf20Sopenharmony_ci * irq must be cleared here to catch the R/B# interrupt there. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci denali_reset_irq(to_denali_controller(chip)); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) { 11658c2ecf20Sopenharmony_ci ret = denali_exec_instr(chip, &op->instrs[i]); 11668c2ecf20Sopenharmony_ci if (ret) 11678c2ecf20Sopenharmony_ci return ret; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic const struct nand_controller_ops denali_controller_ops = { 11748c2ecf20Sopenharmony_ci .attach_chip = denali_attach_chip, 11758c2ecf20Sopenharmony_ci .exec_op = denali_exec_op, 11768c2ecf20Sopenharmony_ci .setup_interface = denali_setup_interface, 11778c2ecf20Sopenharmony_ci}; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ciint denali_chip_init(struct denali_controller *denali, 11808c2ecf20Sopenharmony_ci struct denali_chip *dchip) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci struct nand_chip *chip = &dchip->chip; 11838c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 11848c2ecf20Sopenharmony_ci struct denali_chip *dchip2; 11858c2ecf20Sopenharmony_ci int i, j, ret; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci chip->controller = &denali->controller; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* sanity checks for bank numbers */ 11908c2ecf20Sopenharmony_ci for (i = 0; i < dchip->nsels; i++) { 11918c2ecf20Sopenharmony_ci unsigned int bank = dchip->sels[i].bank; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci if (bank >= denali->nbanks) { 11948c2ecf20Sopenharmony_ci dev_err(denali->dev, "unsupported bank %d\n", bank); 11958c2ecf20Sopenharmony_ci return -EINVAL; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 11998c2ecf20Sopenharmony_ci if (bank == dchip->sels[j].bank) { 12008c2ecf20Sopenharmony_ci dev_err(denali->dev, 12018c2ecf20Sopenharmony_ci "bank %d is assigned twice in the same chip\n", 12028c2ecf20Sopenharmony_ci bank); 12038c2ecf20Sopenharmony_ci return -EINVAL; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci list_for_each_entry(dchip2, &denali->chips, node) { 12088c2ecf20Sopenharmony_ci for (j = 0; j < dchip2->nsels; j++) { 12098c2ecf20Sopenharmony_ci if (bank == dchip2->sels[j].bank) { 12108c2ecf20Sopenharmony_ci dev_err(denali->dev, 12118c2ecf20Sopenharmony_ci "bank %d is already used\n", 12128c2ecf20Sopenharmony_ci bank); 12138c2ecf20Sopenharmony_ci return -EINVAL; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci mtd->dev.parent = denali->dev; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* 12228c2ecf20Sopenharmony_ci * Fallback to the default name if DT did not give "label" property. 12238c2ecf20Sopenharmony_ci * Use "label" property if multiple chips are connected. 12248c2ecf20Sopenharmony_ci */ 12258c2ecf20Sopenharmony_ci if (!mtd->name && list_empty(&denali->chips)) 12268c2ecf20Sopenharmony_ci mtd->name = "denali-nand"; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (denali->dma_avail) { 12298c2ecf20Sopenharmony_ci chip->options |= NAND_USES_DMA; 12308c2ecf20Sopenharmony_ci chip->buf_align = 16; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* clk rate info is needed for setup_interface */ 12348c2ecf20Sopenharmony_ci if (!denali->clk_rate || !denali->clk_x_rate) 12358c2ecf20Sopenharmony_ci chip->options |= NAND_KEEP_TIMINGS; 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci chip->bbt_options |= NAND_BBT_USE_FLASH; 12388c2ecf20Sopenharmony_ci chip->bbt_options |= NAND_BBT_NO_OOB; 12398c2ecf20Sopenharmony_ci chip->options |= NAND_NO_SUBPAGE_WRITE; 12408c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 12418c2ecf20Sopenharmony_ci chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; 12428c2ecf20Sopenharmony_ci chip->ecc.read_page = denali_read_page; 12438c2ecf20Sopenharmony_ci chip->ecc.write_page = denali_write_page; 12448c2ecf20Sopenharmony_ci chip->ecc.read_page_raw = denali_read_page_raw; 12458c2ecf20Sopenharmony_ci chip->ecc.write_page_raw = denali_write_page_raw; 12468c2ecf20Sopenharmony_ci chip->ecc.read_oob = denali_read_oob; 12478c2ecf20Sopenharmony_ci chip->ecc.write_oob = denali_write_oob; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &denali_ooblayout_ops); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci ret = nand_scan(chip, dchip->nsels); 12528c2ecf20Sopenharmony_ci if (ret) 12538c2ecf20Sopenharmony_ci return ret; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 12568c2ecf20Sopenharmony_ci if (ret) { 12578c2ecf20Sopenharmony_ci dev_err(denali->dev, "Failed to register MTD: %d\n", ret); 12588c2ecf20Sopenharmony_ci goto cleanup_nand; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci list_add_tail(&dchip->node, &denali->chips); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cicleanup_nand: 12668c2ecf20Sopenharmony_ci nand_cleanup(chip); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci return ret; 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(denali_chip_init); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ciint denali_init(struct denali_controller *denali) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci u32 features = ioread32(denali->reg + FEATURES); 12758c2ecf20Sopenharmony_ci int ret; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci nand_controller_init(&denali->controller); 12788c2ecf20Sopenharmony_ci denali->controller.ops = &denali_controller_ops; 12798c2ecf20Sopenharmony_ci init_completion(&denali->complete); 12808c2ecf20Sopenharmony_ci spin_lock_init(&denali->irq_lock); 12818c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&denali->chips); 12828c2ecf20Sopenharmony_ci denali->active_bank = DENALI_INVALID_BANK; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* 12858c2ecf20Sopenharmony_ci * The REVISION register may not be reliable. Platforms are allowed to 12868c2ecf20Sopenharmony_ci * override it. 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_ci if (!denali->revision) 12898c2ecf20Sopenharmony_ci denali->revision = swab16(ioread32(denali->reg + REVISION)); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci denali->nbanks = 1 << FIELD_GET(FEATURES__N_BANKS, features); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* the encoding changed from rev 5.0 to 5.1 */ 12948c2ecf20Sopenharmony_ci if (denali->revision < 0x0501) 12958c2ecf20Sopenharmony_ci denali->nbanks <<= 1; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if (features & FEATURES__DMA) 12988c2ecf20Sopenharmony_ci denali->dma_avail = true; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (denali->dma_avail) { 13018c2ecf20Sopenharmony_ci int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit)); 13048c2ecf20Sopenharmony_ci if (ret) { 13058c2ecf20Sopenharmony_ci dev_info(denali->dev, 13068c2ecf20Sopenharmony_ci "Failed to set DMA mask. Disabling DMA.\n"); 13078c2ecf20Sopenharmony_ci denali->dma_avail = false; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (denali->dma_avail) { 13128c2ecf20Sopenharmony_ci if (denali->caps & DENALI_CAP_DMA_64BIT) 13138c2ecf20Sopenharmony_ci denali->setup_dma = denali_setup_dma64; 13148c2ecf20Sopenharmony_ci else 13158c2ecf20Sopenharmony_ci denali->setup_dma = denali_setup_dma32; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci if (features & FEATURES__INDEX_ADDR) { 13198c2ecf20Sopenharmony_ci denali->host_read = denali_indexed_read; 13208c2ecf20Sopenharmony_ci denali->host_write = denali_indexed_write; 13218c2ecf20Sopenharmony_ci } else { 13228c2ecf20Sopenharmony_ci denali->host_read = denali_direct_read; 13238c2ecf20Sopenharmony_ci denali->host_write = denali_direct_write; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci /* 13278c2ecf20Sopenharmony_ci * Set how many bytes should be skipped before writing data in OOB. 13288c2ecf20Sopenharmony_ci * If a platform requests a non-zero value, set it to the register. 13298c2ecf20Sopenharmony_ci * Otherwise, read the value out, expecting it has already been set up 13308c2ecf20Sopenharmony_ci * by firmware. 13318c2ecf20Sopenharmony_ci */ 13328c2ecf20Sopenharmony_ci if (denali->oob_skip_bytes) 13338c2ecf20Sopenharmony_ci iowrite32(denali->oob_skip_bytes, 13348c2ecf20Sopenharmony_ci denali->reg + SPARE_AREA_SKIP_BYTES); 13358c2ecf20Sopenharmony_ci else 13368c2ecf20Sopenharmony_ci denali->oob_skip_bytes = ioread32(denali->reg + 13378c2ecf20Sopenharmony_ci SPARE_AREA_SKIP_BYTES); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci iowrite32(0, denali->reg + TRANSFER_SPARE_REG); 13408c2ecf20Sopenharmony_ci iowrite32(GENMASK(denali->nbanks - 1, 0), denali->reg + RB_PIN_ENABLED); 13418c2ecf20Sopenharmony_ci iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE); 13428c2ecf20Sopenharmony_ci iowrite32(ECC_ENABLE__FLAG, denali->reg + ECC_ENABLE); 13438c2ecf20Sopenharmony_ci iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER); 13448c2ecf20Sopenharmony_ci iowrite32(WRITE_PROTECT__FLAG, denali->reg + WRITE_PROTECT); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci denali_clear_irq_all(denali); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci ret = devm_request_irq(denali->dev, denali->irq, denali_isr, 13498c2ecf20Sopenharmony_ci IRQF_SHARED, DENALI_NAND_NAME, denali); 13508c2ecf20Sopenharmony_ci if (ret) { 13518c2ecf20Sopenharmony_ci dev_err(denali->dev, "Unable to request IRQ\n"); 13528c2ecf20Sopenharmony_ci return ret; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci denali_enable_irq(denali); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(denali_init); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_civoid denali_remove(struct denali_controller *denali) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct denali_chip *dchip, *tmp; 13648c2ecf20Sopenharmony_ci struct nand_chip *chip; 13658c2ecf20Sopenharmony_ci int ret; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci list_for_each_entry_safe(dchip, tmp, &denali->chips, node) { 13688c2ecf20Sopenharmony_ci chip = &dchip->chip; 13698c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 13708c2ecf20Sopenharmony_ci WARN_ON(ret); 13718c2ecf20Sopenharmony_ci nand_cleanup(chip); 13728c2ecf20Sopenharmony_ci list_del(&dchip->node); 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci denali_disable_irq(denali); 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ciEXPORT_SYMBOL(denali_remove); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver core for Denali NAND controller"); 13808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation and its suppliers"); 13818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1382