18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SuperH FLCTL nand controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Renesas Solutions Corp. 68c2ecf20Sopenharmony_ci * Copyright (c) 2008 Atom Create Engineering Co., Ltd. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/completion.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci#include <linux/sh_dma.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/string.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 288c2ecf20Sopenharmony_ci#include <linux/mtd/rawnand.h> 298c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 308c2ecf20Sopenharmony_ci#include <linux/mtd/sh_flctl.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, 338c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (section) 388c2ecf20Sopenharmony_ci return -ERANGE; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci oobregion->offset = 0; 418c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.bytes; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, 478c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci if (section) 508c2ecf20Sopenharmony_ci return -ERANGE; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci oobregion->offset = 12; 538c2ecf20Sopenharmony_ci oobregion->length = 4; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { 598c2ecf20Sopenharmony_ci .ecc = flctl_4secc_ooblayout_sp_ecc, 608c2ecf20Sopenharmony_ci .free = flctl_4secc_ooblayout_sp_free, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, 648c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (section >= chip->ecc.steps) 698c2ecf20Sopenharmony_ci return -ERANGE; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci oobregion->offset = (section * 16) + 6; 728c2ecf20Sopenharmony_ci oobregion->length = chip->ecc.bytes; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, 788c2ecf20Sopenharmony_ci struct mtd_oob_region *oobregion) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (section >= chip->ecc.steps) 838c2ecf20Sopenharmony_ci return -ERANGE; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci oobregion->offset = section * 16; 868c2ecf20Sopenharmony_ci oobregion->length = 6; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!section) { 898c2ecf20Sopenharmony_ci oobregion->offset += 2; 908c2ecf20Sopenharmony_ci oobregion->length -= 2; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { 978c2ecf20Sopenharmony_ci .ecc = flctl_4secc_ooblayout_lp_ecc, 988c2ecf20Sopenharmony_ci .free = flctl_4secc_ooblayout_lp_free, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff, 0xff }; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic struct nand_bbt_descr flctl_4secc_smallpage = { 1048c2ecf20Sopenharmony_ci .offs = 11, 1058c2ecf20Sopenharmony_ci .len = 1, 1068c2ecf20Sopenharmony_ci .pattern = scan_ff_pattern, 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct nand_bbt_descr flctl_4secc_largepage = { 1108c2ecf20Sopenharmony_ci .offs = 0, 1118c2ecf20Sopenharmony_ci .len = 2, 1128c2ecf20Sopenharmony_ci .pattern = scan_ff_pattern, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void empty_fifo(struct sh_flctl *flctl) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); 1188c2ecf20Sopenharmony_ci writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void start_translation(struct sh_flctl *flctl) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci writeb(TRSTRT, FLTRCR(flctl)); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void timeout_error(struct sh_flctl *flctl, const char *str) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void wait_completion(struct sh_flctl *flctl) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci while (timeout--) { 1368c2ecf20Sopenharmony_ci if (readb(FLTRCR(flctl)) & TREND) { 1378c2ecf20Sopenharmony_ci writeb(0x0, FLTRCR(flctl)); 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci udelay(1); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci timeout_error(flctl, __func__); 1448c2ecf20Sopenharmony_ci writeb(0x0, FLTRCR(flctl)); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void flctl_dma_complete(void *param) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct sh_flctl *flctl = param; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci complete(&flctl->dma_complete); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void flctl_release_dma(struct sh_flctl *flctl) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci if (flctl->chan_fifo0_rx) { 1578c2ecf20Sopenharmony_ci dma_release_channel(flctl->chan_fifo0_rx); 1588c2ecf20Sopenharmony_ci flctl->chan_fifo0_rx = NULL; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci if (flctl->chan_fifo0_tx) { 1618c2ecf20Sopenharmony_ci dma_release_channel(flctl->chan_fifo0_tx); 1628c2ecf20Sopenharmony_ci flctl->chan_fifo0_tx = NULL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void flctl_setup_dma(struct sh_flctl *flctl) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci dma_cap_mask_t mask; 1698c2ecf20Sopenharmony_ci struct dma_slave_config cfg; 1708c2ecf20Sopenharmony_ci struct platform_device *pdev = flctl->pdev; 1718c2ecf20Sopenharmony_ci struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); 1728c2ecf20Sopenharmony_ci int ret; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!pdata) 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0) 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* We can only either use DMA for both Tx and Rx or not use it at all */ 1818c2ecf20Sopenharmony_ci dma_cap_zero(mask); 1828c2ecf20Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter, 1858c2ecf20Sopenharmony_ci (void *)(uintptr_t)pdata->slave_id_fifo0_tx); 1868c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__, 1878c2ecf20Sopenharmony_ci flctl->chan_fifo0_tx); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!flctl->chan_fifo0_tx) 1908c2ecf20Sopenharmony_ci return; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 1938c2ecf20Sopenharmony_ci cfg.direction = DMA_MEM_TO_DEV; 1948c2ecf20Sopenharmony_ci cfg.dst_addr = flctl->fifo; 1958c2ecf20Sopenharmony_ci cfg.src_addr = 0; 1968c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg); 1978c2ecf20Sopenharmony_ci if (ret < 0) 1988c2ecf20Sopenharmony_ci goto err; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter, 2018c2ecf20Sopenharmony_ci (void *)(uintptr_t)pdata->slave_id_fifo0_rx); 2028c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__, 2038c2ecf20Sopenharmony_ci flctl->chan_fifo0_rx); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!flctl->chan_fifo0_rx) 2068c2ecf20Sopenharmony_ci goto err; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci cfg.direction = DMA_DEV_TO_MEM; 2098c2ecf20Sopenharmony_ci cfg.dst_addr = 0; 2108c2ecf20Sopenharmony_ci cfg.src_addr = flctl->fifo; 2118c2ecf20Sopenharmony_ci ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg); 2128c2ecf20Sopenharmony_ci if (ret < 0) 2138c2ecf20Sopenharmony_ci goto err; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci init_completion(&flctl->dma_complete); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cierr: 2208c2ecf20Sopenharmony_ci flctl_release_dma(flctl); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 2268c2ecf20Sopenharmony_ci uint32_t addr = 0; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (column == -1) { 2298c2ecf20Sopenharmony_ci addr = page_addr; /* ERASE1 */ 2308c2ecf20Sopenharmony_ci } else if (page_addr != -1) { 2318c2ecf20Sopenharmony_ci /* SEQIN, READ0, etc.. */ 2328c2ecf20Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 2338c2ecf20Sopenharmony_ci column >>= 1; 2348c2ecf20Sopenharmony_ci if (flctl->page_size) { 2358c2ecf20Sopenharmony_ci addr = column & 0x0FFF; 2368c2ecf20Sopenharmony_ci addr |= (page_addr & 0xff) << 16; 2378c2ecf20Sopenharmony_ci addr |= ((page_addr >> 8) & 0xff) << 24; 2388c2ecf20Sopenharmony_ci /* big than 128MB */ 2398c2ecf20Sopenharmony_ci if (flctl->rw_ADRCNT == ADRCNT2_E) { 2408c2ecf20Sopenharmony_ci uint32_t addr2; 2418c2ecf20Sopenharmony_ci addr2 = (page_addr >> 16) & 0xff; 2428c2ecf20Sopenharmony_ci writel(addr2, FLADR2(flctl)); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci } else { 2458c2ecf20Sopenharmony_ci addr = column; 2468c2ecf20Sopenharmony_ci addr |= (page_addr & 0xff) << 8; 2478c2ecf20Sopenharmony_ci addr |= ((page_addr >> 8) & 0xff) << 16; 2488c2ecf20Sopenharmony_ci addr |= ((page_addr >> 16) & 0xff) << 24; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci writel(addr, FLADR(flctl)); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void wait_rfifo_ready(struct sh_flctl *flctl) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci while (timeout--) { 2598c2ecf20Sopenharmony_ci uint32_t val; 2608c2ecf20Sopenharmony_ci /* check FIFO */ 2618c2ecf20Sopenharmony_ci val = readl(FLDTCNTR(flctl)) >> 16; 2628c2ecf20Sopenharmony_ci if (val & 0xFF) 2638c2ecf20Sopenharmony_ci return; 2648c2ecf20Sopenharmony_ci udelay(1); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci timeout_error(flctl, __func__); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void wait_wfifo_ready(struct sh_flctl *flctl) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci uint32_t len, timeout = LOOP_TIMEOUT_MAX; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci while (timeout--) { 2748c2ecf20Sopenharmony_ci /* check FIFO */ 2758c2ecf20Sopenharmony_ci len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; 2768c2ecf20Sopenharmony_ci if (len >= 4) 2778c2ecf20Sopenharmony_ci return; 2788c2ecf20Sopenharmony_ci udelay(1); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci timeout_error(flctl, __func__); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic enum flctl_ecc_res_t wait_recfifo_ready 2848c2ecf20Sopenharmony_ci (struct sh_flctl *flctl, int sector_number) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 2878c2ecf20Sopenharmony_ci void __iomem *ecc_reg[4]; 2888c2ecf20Sopenharmony_ci int i; 2898c2ecf20Sopenharmony_ci int state = FL_SUCCESS; 2908c2ecf20Sopenharmony_ci uint32_t data, size; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * First this loops checks in FLDTCNTR if we are ready to read out the 2948c2ecf20Sopenharmony_ci * oob data. This is the case if either all went fine without errors or 2958c2ecf20Sopenharmony_ci * if the bottom part of the loop corrected the errors or marked them as 2968c2ecf20Sopenharmony_ci * uncorrectable and the controller is given time to push the data into 2978c2ecf20Sopenharmony_ci * the FIFO. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci while (timeout--) { 3008c2ecf20Sopenharmony_ci /* check if all is ok and we can read out the OOB */ 3018c2ecf20Sopenharmony_ci size = readl(FLDTCNTR(flctl)) >> 24; 3028c2ecf20Sopenharmony_ci if ((size & 0xFF) == 4) 3038c2ecf20Sopenharmony_ci return state; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* check if a correction code has been calculated */ 3068c2ecf20Sopenharmony_ci if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) { 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * either we wait for the fifo to be filled or a 3098c2ecf20Sopenharmony_ci * correction pattern is being generated 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci udelay(1); 3128c2ecf20Sopenharmony_ci continue; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* check for an uncorrectable error */ 3168c2ecf20Sopenharmony_ci if (readl(FL4ECCCR(flctl)) & _4ECCFA) { 3178c2ecf20Sopenharmony_ci /* check if we face a non-empty page */ 3188c2ecf20Sopenharmony_ci for (i = 0; i < 512; i++) { 3198c2ecf20Sopenharmony_ci if (flctl->done_buff[i] != 0xff) { 3208c2ecf20Sopenharmony_ci state = FL_ERROR; /* can't correct */ 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (state == FL_SUCCESS) 3268c2ecf20Sopenharmony_ci dev_dbg(&flctl->pdev->dev, 3278c2ecf20Sopenharmony_ci "reading empty sector %d, ecc error ignored\n", 3288c2ecf20Sopenharmony_ci sector_number); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci writel(0, FL4ECCCR(flctl)); 3318c2ecf20Sopenharmony_ci continue; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* start error correction */ 3358c2ecf20Sopenharmony_ci ecc_reg[0] = FL4ECCRESULT0(flctl); 3368c2ecf20Sopenharmony_ci ecc_reg[1] = FL4ECCRESULT1(flctl); 3378c2ecf20Sopenharmony_ci ecc_reg[2] = FL4ECCRESULT2(flctl); 3388c2ecf20Sopenharmony_ci ecc_reg[3] = FL4ECCRESULT3(flctl); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 3418c2ecf20Sopenharmony_ci uint8_t org; 3428c2ecf20Sopenharmony_ci unsigned int index; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci data = readl(ecc_reg[i]); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (flctl->page_size) 3478c2ecf20Sopenharmony_ci index = (512 * sector_number) + 3488c2ecf20Sopenharmony_ci (data >> 16); 3498c2ecf20Sopenharmony_ci else 3508c2ecf20Sopenharmony_ci index = data >> 16; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci org = flctl->done_buff[index]; 3538c2ecf20Sopenharmony_ci flctl->done_buff[index] = org ^ (data & 0xFF); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci state = FL_REPAIRABLE; 3568c2ecf20Sopenharmony_ci writel(0, FL4ECCCR(flctl)); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci timeout_error(flctl, __func__); 3608c2ecf20Sopenharmony_ci return FL_TIMEOUT; /* timeout */ 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void wait_wecfifo_ready(struct sh_flctl *flctl) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 3668c2ecf20Sopenharmony_ci uint32_t len; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci while (timeout--) { 3698c2ecf20Sopenharmony_ci /* check FLECFIFO */ 3708c2ecf20Sopenharmony_ci len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; 3718c2ecf20Sopenharmony_ci if (len >= 4) 3728c2ecf20Sopenharmony_ci return; 3738c2ecf20Sopenharmony_ci udelay(1); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci timeout_error(flctl, __func__); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, 3798c2ecf20Sopenharmony_ci int len, enum dma_data_direction dir) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc = NULL; 3828c2ecf20Sopenharmony_ci struct dma_chan *chan; 3838c2ecf20Sopenharmony_ci enum dma_transfer_direction tr_dir; 3848c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 3858c2ecf20Sopenharmony_ci dma_cookie_t cookie; 3868c2ecf20Sopenharmony_ci uint32_t reg; 3878c2ecf20Sopenharmony_ci int ret = 0; 3888c2ecf20Sopenharmony_ci unsigned long time_left; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (dir == DMA_FROM_DEVICE) { 3918c2ecf20Sopenharmony_ci chan = flctl->chan_fifo0_rx; 3928c2ecf20Sopenharmony_ci tr_dir = DMA_DEV_TO_MEM; 3938c2ecf20Sopenharmony_ci } else { 3948c2ecf20Sopenharmony_ci chan = flctl->chan_fifo0_tx; 3958c2ecf20Sopenharmony_ci tr_dir = DMA_MEM_TO_DEV; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci dma_addr = dma_map_single(chan->device->dev, buf, len, dir); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!dma_mapping_error(chan->device->dev, dma_addr)) 4018c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_single(chan, dma_addr, len, 4028c2ecf20Sopenharmony_ci tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (desc) { 4058c2ecf20Sopenharmony_ci reg = readl(FLINTDMACR(flctl)); 4068c2ecf20Sopenharmony_ci reg |= DREQ0EN; 4078c2ecf20Sopenharmony_ci writel(reg, FLINTDMACR(flctl)); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci desc->callback = flctl_dma_complete; 4108c2ecf20Sopenharmony_ci desc->callback_param = flctl; 4118c2ecf20Sopenharmony_ci cookie = dmaengine_submit(desc); 4128c2ecf20Sopenharmony_ci if (dma_submit_error(cookie)) { 4138c2ecf20Sopenharmony_ci ret = dma_submit_error(cookie); 4148c2ecf20Sopenharmony_ci dev_warn(&flctl->pdev->dev, 4158c2ecf20Sopenharmony_ci "DMA submit failed, falling back to PIO\n"); 4168c2ecf20Sopenharmony_ci goto out; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci dma_async_issue_pending(chan); 4208c2ecf20Sopenharmony_ci } else { 4218c2ecf20Sopenharmony_ci /* DMA failed, fall back to PIO */ 4228c2ecf20Sopenharmony_ci flctl_release_dma(flctl); 4238c2ecf20Sopenharmony_ci dev_warn(&flctl->pdev->dev, 4248c2ecf20Sopenharmony_ci "DMA failed, falling back to PIO\n"); 4258c2ecf20Sopenharmony_ci ret = -EIO; 4268c2ecf20Sopenharmony_ci goto out; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci time_left = 4308c2ecf20Sopenharmony_ci wait_for_completion_timeout(&flctl->dma_complete, 4318c2ecf20Sopenharmony_ci msecs_to_jiffies(3000)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (time_left == 0) { 4348c2ecf20Sopenharmony_ci dmaengine_terminate_all(chan); 4358c2ecf20Sopenharmony_ci dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n"); 4368c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ciout: 4408c2ecf20Sopenharmony_ci reg = readl(FLINTDMACR(flctl)); 4418c2ecf20Sopenharmony_ci reg &= ~DREQ0EN; 4428c2ecf20Sopenharmony_ci writel(reg, FLINTDMACR(flctl)); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci dma_unmap_single(chan->device->dev, dma_addr, len, dir); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* ret == 0 is success */ 4478c2ecf20Sopenharmony_ci return ret; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void read_datareg(struct sh_flctl *flctl, int offset) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci unsigned long data; 4538c2ecf20Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci wait_completion(flctl); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci data = readl(FLDATAR(flctl)); 4588c2ecf20Sopenharmony_ci *buf = le32_to_cpu(data); 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci int i, len_4align; 4648c2ecf20Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci len_4align = (rlen + 3) / 4; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* initiate DMA transfer */ 4698c2ecf20Sopenharmony_ci if (flctl->chan_fifo0_rx && rlen >= 32 && 4708c2ecf20Sopenharmony_ci !flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE)) 4718c2ecf20Sopenharmony_ci goto convert; /* DMA success */ 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* do polling transfer */ 4748c2ecf20Sopenharmony_ci for (i = 0; i < len_4align; i++) { 4758c2ecf20Sopenharmony_ci wait_rfifo_ready(flctl); 4768c2ecf20Sopenharmony_ci buf[i] = readl(FLDTFIFO(flctl)); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ciconvert: 4808c2ecf20Sopenharmony_ci for (i = 0; i < len_4align; i++) 4818c2ecf20Sopenharmony_ci buf[i] = be32_to_cpu(buf[i]); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic enum flctl_ecc_res_t read_ecfiforeg 4858c2ecf20Sopenharmony_ci (struct sh_flctl *flctl, uint8_t *buff, int sector) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci int i; 4888c2ecf20Sopenharmony_ci enum flctl_ecc_res_t res; 4898c2ecf20Sopenharmony_ci unsigned long *ecc_buf = (unsigned long *)buff; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci res = wait_recfifo_ready(flctl , sector); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (res != FL_ERROR) { 4948c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 4958c2ecf20Sopenharmony_ci ecc_buf[i] = readl(FLECFIFO(flctl)); 4968c2ecf20Sopenharmony_ci ecc_buf[i] = be32_to_cpu(ecc_buf[i]); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return res; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic void write_fiforeg(struct sh_flctl *flctl, int rlen, 5048c2ecf20Sopenharmony_ci unsigned int offset) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci int i, len_4align; 5078c2ecf20Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci len_4align = (rlen + 3) / 4; 5108c2ecf20Sopenharmony_ci for (i = 0; i < len_4align; i++) { 5118c2ecf20Sopenharmony_ci wait_wfifo_ready(flctl); 5128c2ecf20Sopenharmony_ci writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, 5178c2ecf20Sopenharmony_ci unsigned int offset) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci int i, len_4align; 5208c2ecf20Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci len_4align = (rlen + 3) / 4; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci for (i = 0; i < len_4align; i++) 5258c2ecf20Sopenharmony_ci buf[i] = cpu_to_be32(buf[i]); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* initiate DMA transfer */ 5288c2ecf20Sopenharmony_ci if (flctl->chan_fifo0_tx && rlen >= 32 && 5298c2ecf20Sopenharmony_ci !flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE)) 5308c2ecf20Sopenharmony_ci return; /* DMA success */ 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* do polling transfer */ 5338c2ecf20Sopenharmony_ci for (i = 0; i < len_4align; i++) { 5348c2ecf20Sopenharmony_ci wait_wecfifo_ready(flctl); 5358c2ecf20Sopenharmony_ci writel(buf[i], FLECFIFO(flctl)); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 5428c2ecf20Sopenharmony_ci uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; 5438c2ecf20Sopenharmony_ci uint32_t flcmdcr_val, addr_len_bytes = 0; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Set SNAND bit if page size is 2048byte */ 5468c2ecf20Sopenharmony_ci if (flctl->page_size) 5478c2ecf20Sopenharmony_ci flcmncr_val |= SNAND_E; 5488c2ecf20Sopenharmony_ci else 5498c2ecf20Sopenharmony_ci flcmncr_val &= ~SNAND_E; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* default FLCMDCR val */ 5528c2ecf20Sopenharmony_ci flcmdcr_val = DOCMD1_E | DOADR_E; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Set for FLCMDCR */ 5558c2ecf20Sopenharmony_ci switch (cmd) { 5568c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 5578c2ecf20Sopenharmony_ci addr_len_bytes = flctl->erase_ADRCNT; 5588c2ecf20Sopenharmony_ci flcmdcr_val |= DOCMD2_E; 5598c2ecf20Sopenharmony_ci break; 5608c2ecf20Sopenharmony_ci case NAND_CMD_READ0: 5618c2ecf20Sopenharmony_ci case NAND_CMD_READOOB: 5628c2ecf20Sopenharmony_ci case NAND_CMD_RNDOUT: 5638c2ecf20Sopenharmony_ci addr_len_bytes = flctl->rw_ADRCNT; 5648c2ecf20Sopenharmony_ci flcmdcr_val |= CDSRC_E; 5658c2ecf20Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 5668c2ecf20Sopenharmony_ci flcmncr_val |= SEL_16BIT; 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci case NAND_CMD_SEQIN: 5698c2ecf20Sopenharmony_ci /* This case is that cmd is READ0 or READ1 or READ00 */ 5708c2ecf20Sopenharmony_ci flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci case NAND_CMD_PAGEPROG: 5738c2ecf20Sopenharmony_ci addr_len_bytes = flctl->rw_ADRCNT; 5748c2ecf20Sopenharmony_ci flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; 5758c2ecf20Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 5768c2ecf20Sopenharmony_ci flcmncr_val |= SEL_16BIT; 5778c2ecf20Sopenharmony_ci break; 5788c2ecf20Sopenharmony_ci case NAND_CMD_READID: 5798c2ecf20Sopenharmony_ci flcmncr_val &= ~SNAND_E; 5808c2ecf20Sopenharmony_ci flcmdcr_val |= CDSRC_E; 5818c2ecf20Sopenharmony_ci addr_len_bytes = ADRCNT_1; 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: 5848c2ecf20Sopenharmony_ci case NAND_CMD_RESET: 5858c2ecf20Sopenharmony_ci flcmncr_val &= ~SNAND_E; 5868c2ecf20Sopenharmony_ci flcmdcr_val &= ~(DOADR_E | DOSR_E); 5878c2ecf20Sopenharmony_ci break; 5888c2ecf20Sopenharmony_ci default: 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* Set address bytes parameter */ 5938c2ecf20Sopenharmony_ci flcmdcr_val |= addr_len_bytes; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Now actually write */ 5968c2ecf20Sopenharmony_ci writel(flcmncr_val, FLCMNCR(flctl)); 5978c2ecf20Sopenharmony_ci writel(flcmdcr_val, FLCMDCR(flctl)); 5988c2ecf20Sopenharmony_ci writel(flcmcdr_val, FLCMCDR(flctl)); 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic int flctl_read_page_hwecc(struct nand_chip *chip, uint8_t *buf, 6028c2ecf20Sopenharmony_ci int oob_required, int page) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci nand_read_page_op(chip, page, 0, buf, mtd->writesize); 6078c2ecf20Sopenharmony_ci if (oob_required) 6088c2ecf20Sopenharmony_ci chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize); 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int flctl_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf, 6138c2ecf20Sopenharmony_ci int oob_required, int page) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); 6188c2ecf20Sopenharmony_ci chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); 6198c2ecf20Sopenharmony_ci return nand_prog_page_end_op(chip); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 6258c2ecf20Sopenharmony_ci int sector, page_sectors; 6268c2ecf20Sopenharmony_ci enum flctl_ecc_res_t ecc_result; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci page_sectors = flctl->page_size ? 4 : 1; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_READ0, 6318c2ecf20Sopenharmony_ci (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, 6348c2ecf20Sopenharmony_ci FLCMNCR(flctl)); 6358c2ecf20Sopenharmony_ci writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); 6368c2ecf20Sopenharmony_ci writel(page_addr << 2, FLADR(flctl)); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci empty_fifo(flctl); 6398c2ecf20Sopenharmony_ci start_translation(flctl); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci for (sector = 0; sector < page_sectors; sector++) { 6428c2ecf20Sopenharmony_ci read_fiforeg(flctl, 512, 512 * sector); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ecc_result = read_ecfiforeg(flctl, 6458c2ecf20Sopenharmony_ci &flctl->done_buff[mtd->writesize + 16 * sector], 6468c2ecf20Sopenharmony_ci sector); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci switch (ecc_result) { 6498c2ecf20Sopenharmony_ci case FL_REPAIRABLE: 6508c2ecf20Sopenharmony_ci dev_info(&flctl->pdev->dev, 6518c2ecf20Sopenharmony_ci "applied ecc on page 0x%x", page_addr); 6528c2ecf20Sopenharmony_ci mtd->ecc_stats.corrected++; 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci case FL_ERROR: 6558c2ecf20Sopenharmony_ci dev_warn(&flctl->pdev->dev, 6568c2ecf20Sopenharmony_ci "page 0x%x contains corrupted data\n", 6578c2ecf20Sopenharmony_ci page_addr); 6588c2ecf20Sopenharmony_ci mtd->ecc_stats.failed++; 6598c2ecf20Sopenharmony_ci break; 6608c2ecf20Sopenharmony_ci default: 6618c2ecf20Sopenharmony_ci ; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci wait_completion(flctl); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), 6688c2ecf20Sopenharmony_ci FLCMNCR(flctl)); 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic void execmd_read_oob(struct mtd_info *mtd, int page_addr) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 6748c2ecf20Sopenharmony_ci int page_sectors = flctl->page_size ? 4 : 1; 6758c2ecf20Sopenharmony_ci int i; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_READ0, 6788c2ecf20Sopenharmony_ci (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci empty_fifo(flctl); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci for (i = 0; i < page_sectors; i++) { 6838c2ecf20Sopenharmony_ci set_addr(mtd, (512 + 16) * i + 512 , page_addr); 6848c2ecf20Sopenharmony_ci writel(16, FLDTCNTR(flctl)); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci start_translation(flctl); 6878c2ecf20Sopenharmony_ci read_fiforeg(flctl, 16, 16 * i); 6888c2ecf20Sopenharmony_ci wait_completion(flctl); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic void execmd_write_page_sector(struct mtd_info *mtd) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 6958c2ecf20Sopenharmony_ci int page_addr = flctl->seqin_page_addr; 6968c2ecf20Sopenharmony_ci int sector, page_sectors; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci page_sectors = flctl->page_size ? 4 : 1; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_PAGEPROG, 7018c2ecf20Sopenharmony_ci (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci empty_fifo(flctl); 7048c2ecf20Sopenharmony_ci writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); 7058c2ecf20Sopenharmony_ci writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); 7068c2ecf20Sopenharmony_ci writel(page_addr << 2, FLADR(flctl)); 7078c2ecf20Sopenharmony_ci start_translation(flctl); 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci for (sector = 0; sector < page_sectors; sector++) { 7108c2ecf20Sopenharmony_ci write_fiforeg(flctl, 512, 512 * sector); 7118c2ecf20Sopenharmony_ci write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector); 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci wait_completion(flctl); 7158c2ecf20Sopenharmony_ci writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic void execmd_write_oob(struct mtd_info *mtd) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 7218c2ecf20Sopenharmony_ci int page_addr = flctl->seqin_page_addr; 7228c2ecf20Sopenharmony_ci int sector, page_sectors; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci page_sectors = flctl->page_size ? 4 : 1; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_PAGEPROG, 7278c2ecf20Sopenharmony_ci (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci for (sector = 0; sector < page_sectors; sector++) { 7308c2ecf20Sopenharmony_ci empty_fifo(flctl); 7318c2ecf20Sopenharmony_ci set_addr(mtd, sector * 528 + 512, page_addr); 7328c2ecf20Sopenharmony_ci writel(16, FLDTCNTR(flctl)); /* set read size */ 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci start_translation(flctl); 7358c2ecf20Sopenharmony_ci write_fiforeg(flctl, 16, 16 * sector); 7368c2ecf20Sopenharmony_ci wait_completion(flctl); 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic void flctl_cmdfunc(struct nand_chip *chip, unsigned int command, 7418c2ecf20Sopenharmony_ci int column, int page_addr) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 7448c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 7458c2ecf20Sopenharmony_ci uint32_t read_cmd = 0; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci pm_runtime_get_sync(&flctl->pdev->dev); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci flctl->read_bytes = 0; 7508c2ecf20Sopenharmony_ci if (command != NAND_CMD_PAGEPROG) 7518c2ecf20Sopenharmony_ci flctl->index = 0; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci switch (command) { 7548c2ecf20Sopenharmony_ci case NAND_CMD_READ1: 7558c2ecf20Sopenharmony_ci case NAND_CMD_READ0: 7568c2ecf20Sopenharmony_ci if (flctl->hwecc) { 7578c2ecf20Sopenharmony_ci /* read page with hwecc */ 7588c2ecf20Sopenharmony_ci execmd_read_page_sector(mtd, page_addr); 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci if (flctl->page_size) 7628c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) 7638c2ecf20Sopenharmony_ci | command); 7648c2ecf20Sopenharmony_ci else 7658c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, command); 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci set_addr(mtd, 0, page_addr); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci flctl->read_bytes = mtd->writesize + mtd->oobsize; 7708c2ecf20Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 7718c2ecf20Sopenharmony_ci column >>= 1; 7728c2ecf20Sopenharmony_ci flctl->index += column; 7738c2ecf20Sopenharmony_ci goto read_normal_exit; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci case NAND_CMD_READOOB: 7768c2ecf20Sopenharmony_ci if (flctl->hwecc) { 7778c2ecf20Sopenharmony_ci /* read page with hwecc */ 7788c2ecf20Sopenharmony_ci execmd_read_oob(mtd, page_addr); 7798c2ecf20Sopenharmony_ci break; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (flctl->page_size) { 7838c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) 7848c2ecf20Sopenharmony_ci | NAND_CMD_READ0); 7858c2ecf20Sopenharmony_ci set_addr(mtd, mtd->writesize, page_addr); 7868c2ecf20Sopenharmony_ci } else { 7878c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, command); 7888c2ecf20Sopenharmony_ci set_addr(mtd, 0, page_addr); 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci flctl->read_bytes = mtd->oobsize; 7918c2ecf20Sopenharmony_ci goto read_normal_exit; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci case NAND_CMD_RNDOUT: 7948c2ecf20Sopenharmony_ci if (flctl->hwecc) 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (flctl->page_size) 7988c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) 7998c2ecf20Sopenharmony_ci | command); 8008c2ecf20Sopenharmony_ci else 8018c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, command); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci set_addr(mtd, column, 0); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci flctl->read_bytes = mtd->writesize + mtd->oobsize - column; 8068c2ecf20Sopenharmony_ci goto read_normal_exit; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci case NAND_CMD_READID: 8098c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, command); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* READID is always performed using an 8-bit bus */ 8128c2ecf20Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 8138c2ecf20Sopenharmony_ci column <<= 1; 8148c2ecf20Sopenharmony_ci set_addr(mtd, column, 0); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci flctl->read_bytes = 8; 8178c2ecf20Sopenharmony_ci writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ 8188c2ecf20Sopenharmony_ci empty_fifo(flctl); 8198c2ecf20Sopenharmony_ci start_translation(flctl); 8208c2ecf20Sopenharmony_ci read_fiforeg(flctl, flctl->read_bytes, 0); 8218c2ecf20Sopenharmony_ci wait_completion(flctl); 8228c2ecf20Sopenharmony_ci break; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci case NAND_CMD_ERASE1: 8258c2ecf20Sopenharmony_ci flctl->erase1_page_addr = page_addr; 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci case NAND_CMD_ERASE2: 8298c2ecf20Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_ERASE1, 8308c2ecf20Sopenharmony_ci (command << 8) | NAND_CMD_ERASE1); 8318c2ecf20Sopenharmony_ci set_addr(mtd, -1, flctl->erase1_page_addr); 8328c2ecf20Sopenharmony_ci start_translation(flctl); 8338c2ecf20Sopenharmony_ci wait_completion(flctl); 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci case NAND_CMD_SEQIN: 8378c2ecf20Sopenharmony_ci if (!flctl->page_size) { 8388c2ecf20Sopenharmony_ci /* output read command */ 8398c2ecf20Sopenharmony_ci if (column >= mtd->writesize) { 8408c2ecf20Sopenharmony_ci column -= mtd->writesize; 8418c2ecf20Sopenharmony_ci read_cmd = NAND_CMD_READOOB; 8428c2ecf20Sopenharmony_ci } else if (column < 256) { 8438c2ecf20Sopenharmony_ci read_cmd = NAND_CMD_READ0; 8448c2ecf20Sopenharmony_ci } else { 8458c2ecf20Sopenharmony_ci column -= 256; 8468c2ecf20Sopenharmony_ci read_cmd = NAND_CMD_READ1; 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci flctl->seqin_column = column; 8508c2ecf20Sopenharmony_ci flctl->seqin_page_addr = page_addr; 8518c2ecf20Sopenharmony_ci flctl->seqin_read_cmd = read_cmd; 8528c2ecf20Sopenharmony_ci break; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci case NAND_CMD_PAGEPROG: 8558c2ecf20Sopenharmony_ci empty_fifo(flctl); 8568c2ecf20Sopenharmony_ci if (!flctl->page_size) { 8578c2ecf20Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_SEQIN, 8588c2ecf20Sopenharmony_ci flctl->seqin_read_cmd); 8598c2ecf20Sopenharmony_ci set_addr(mtd, -1, -1); 8608c2ecf20Sopenharmony_ci writel(0, FLDTCNTR(flctl)); /* set 0 size */ 8618c2ecf20Sopenharmony_ci start_translation(flctl); 8628c2ecf20Sopenharmony_ci wait_completion(flctl); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci if (flctl->hwecc) { 8658c2ecf20Sopenharmony_ci /* write page with hwecc */ 8668c2ecf20Sopenharmony_ci if (flctl->seqin_column == mtd->writesize) 8678c2ecf20Sopenharmony_ci execmd_write_oob(mtd); 8688c2ecf20Sopenharmony_ci else if (!flctl->seqin_column) 8698c2ecf20Sopenharmony_ci execmd_write_page_sector(mtd); 8708c2ecf20Sopenharmony_ci else 8718c2ecf20Sopenharmony_ci pr_err("Invalid address !?\n"); 8728c2ecf20Sopenharmony_ci break; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); 8758c2ecf20Sopenharmony_ci set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); 8768c2ecf20Sopenharmony_ci writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ 8778c2ecf20Sopenharmony_ci start_translation(flctl); 8788c2ecf20Sopenharmony_ci write_fiforeg(flctl, flctl->index, 0); 8798c2ecf20Sopenharmony_ci wait_completion(flctl); 8808c2ecf20Sopenharmony_ci break; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci case NAND_CMD_STATUS: 8838c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, command); 8848c2ecf20Sopenharmony_ci set_addr(mtd, -1, -1); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci flctl->read_bytes = 1; 8878c2ecf20Sopenharmony_ci writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ 8888c2ecf20Sopenharmony_ci start_translation(flctl); 8898c2ecf20Sopenharmony_ci read_datareg(flctl, 0); /* read and end */ 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci case NAND_CMD_RESET: 8938c2ecf20Sopenharmony_ci set_cmd_regs(mtd, command, command); 8948c2ecf20Sopenharmony_ci set_addr(mtd, -1, -1); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci writel(0, FLDTCNTR(flctl)); /* set 0 size */ 8978c2ecf20Sopenharmony_ci start_translation(flctl); 8988c2ecf20Sopenharmony_ci wait_completion(flctl); 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci default: 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci goto runtime_exit; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ciread_normal_exit: 9078c2ecf20Sopenharmony_ci writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ 9088c2ecf20Sopenharmony_ci empty_fifo(flctl); 9098c2ecf20Sopenharmony_ci start_translation(flctl); 9108c2ecf20Sopenharmony_ci read_fiforeg(flctl, flctl->read_bytes, 0); 9118c2ecf20Sopenharmony_ci wait_completion(flctl); 9128c2ecf20Sopenharmony_ciruntime_exit: 9138c2ecf20Sopenharmony_ci pm_runtime_put_sync(&flctl->pdev->dev); 9148c2ecf20Sopenharmony_ci return; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic void flctl_select_chip(struct nand_chip *chip, int chipnr) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 9208c2ecf20Sopenharmony_ci int ret; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci switch (chipnr) { 9238c2ecf20Sopenharmony_ci case -1: 9248c2ecf20Sopenharmony_ci flctl->flcmncr_base &= ~CE0_ENABLE; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci pm_runtime_get_sync(&flctl->pdev->dev); 9278c2ecf20Sopenharmony_ci writel(flctl->flcmncr_base, FLCMNCR(flctl)); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (flctl->qos_request) { 9308c2ecf20Sopenharmony_ci dev_pm_qos_remove_request(&flctl->pm_qos); 9318c2ecf20Sopenharmony_ci flctl->qos_request = 0; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci pm_runtime_put_sync(&flctl->pdev->dev); 9358c2ecf20Sopenharmony_ci break; 9368c2ecf20Sopenharmony_ci case 0: 9378c2ecf20Sopenharmony_ci flctl->flcmncr_base |= CE0_ENABLE; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (!flctl->qos_request) { 9408c2ecf20Sopenharmony_ci ret = dev_pm_qos_add_request(&flctl->pdev->dev, 9418c2ecf20Sopenharmony_ci &flctl->pm_qos, 9428c2ecf20Sopenharmony_ci DEV_PM_QOS_RESUME_LATENCY, 9438c2ecf20Sopenharmony_ci 100); 9448c2ecf20Sopenharmony_ci if (ret < 0) 9458c2ecf20Sopenharmony_ci dev_err(&flctl->pdev->dev, 9468c2ecf20Sopenharmony_ci "PM QoS request failed: %d\n", ret); 9478c2ecf20Sopenharmony_ci flctl->qos_request = 1; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (flctl->holden) { 9518c2ecf20Sopenharmony_ci pm_runtime_get_sync(&flctl->pdev->dev); 9528c2ecf20Sopenharmony_ci writel(HOLDEN, FLHOLDCR(flctl)); 9538c2ecf20Sopenharmony_ci pm_runtime_put_sync(&flctl->pdev->dev); 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci default: 9578c2ecf20Sopenharmony_ci BUG(); 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_cistatic void flctl_write_buf(struct nand_chip *chip, const uint8_t *buf, int len) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci memcpy(&flctl->done_buff[flctl->index], buf, len); 9668c2ecf20Sopenharmony_ci flctl->index += len; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic uint8_t flctl_read_byte(struct nand_chip *chip) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 9728c2ecf20Sopenharmony_ci uint8_t data; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci data = flctl->done_buff[flctl->index]; 9758c2ecf20Sopenharmony_ci flctl->index++; 9768c2ecf20Sopenharmony_ci return data; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic void flctl_read_buf(struct nand_chip *chip, uint8_t *buf, int len) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci memcpy(buf, &flctl->done_buff[flctl->index], len); 9848c2ecf20Sopenharmony_ci flctl->index += len; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic int flctl_chip_attach_chip(struct nand_chip *chip) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci u64 targetsize = nanddev_target_size(&chip->base); 9908c2ecf20Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 9918c2ecf20Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* 9948c2ecf20Sopenharmony_ci * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). 9958c2ecf20Sopenharmony_ci * Add the SEL_16BIT flag in flctl->flcmncr_base. 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 9988c2ecf20Sopenharmony_ci flctl->flcmncr_base |= SEL_16BIT; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (mtd->writesize == 512) { 10018c2ecf20Sopenharmony_ci flctl->page_size = 0; 10028c2ecf20Sopenharmony_ci if (targetsize > (32 << 20)) { 10038c2ecf20Sopenharmony_ci /* big than 32MB */ 10048c2ecf20Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_4; 10058c2ecf20Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_3; 10068c2ecf20Sopenharmony_ci } else if (targetsize > (2 << 16)) { 10078c2ecf20Sopenharmony_ci /* big than 128KB */ 10088c2ecf20Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_3; 10098c2ecf20Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_2; 10108c2ecf20Sopenharmony_ci } else { 10118c2ecf20Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_2; 10128c2ecf20Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_1; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci } else { 10158c2ecf20Sopenharmony_ci flctl->page_size = 1; 10168c2ecf20Sopenharmony_ci if (targetsize > (128 << 20)) { 10178c2ecf20Sopenharmony_ci /* big than 128MB */ 10188c2ecf20Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT2_E; 10198c2ecf20Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_3; 10208c2ecf20Sopenharmony_ci } else if (targetsize > (8 << 16)) { 10218c2ecf20Sopenharmony_ci /* big than 512KB */ 10228c2ecf20Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_4; 10238c2ecf20Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_2; 10248c2ecf20Sopenharmony_ci } else { 10258c2ecf20Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_3; 10268c2ecf20Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_1; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (flctl->hwecc) { 10318c2ecf20Sopenharmony_ci if (mtd->writesize == 512) { 10328c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); 10338c2ecf20Sopenharmony_ci chip->badblock_pattern = &flctl_4secc_smallpage; 10348c2ecf20Sopenharmony_ci } else { 10358c2ecf20Sopenharmony_ci mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); 10368c2ecf20Sopenharmony_ci chip->badblock_pattern = &flctl_4secc_largepage; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci chip->ecc.size = 512; 10408c2ecf20Sopenharmony_ci chip->ecc.bytes = 10; 10418c2ecf20Sopenharmony_ci chip->ecc.strength = 4; 10428c2ecf20Sopenharmony_ci chip->ecc.read_page = flctl_read_page_hwecc; 10438c2ecf20Sopenharmony_ci chip->ecc.write_page = flctl_write_page_hwecc; 10448c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* 4 symbols ECC enabled */ 10478c2ecf20Sopenharmony_ci flctl->flcmncr_base |= _4ECCEN; 10488c2ecf20Sopenharmony_ci } else { 10498c2ecf20Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 10508c2ecf20Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic const struct nand_controller_ops flctl_nand_controller_ops = { 10578c2ecf20Sopenharmony_ci .attach_chip = flctl_chip_attach_chip, 10588c2ecf20Sopenharmony_ci}; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic irqreturn_t flctl_handle_flste(int irq, void *dev_id) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci struct sh_flctl *flctl = dev_id; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); 10658c2ecf20Sopenharmony_ci writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistruct flctl_soc_config { 10718c2ecf20Sopenharmony_ci unsigned long flcmncr_val; 10728c2ecf20Sopenharmony_ci unsigned has_hwecc:1; 10738c2ecf20Sopenharmony_ci unsigned use_holden:1; 10748c2ecf20Sopenharmony_ci}; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic struct flctl_soc_config flctl_sh7372_config = { 10778c2ecf20Sopenharmony_ci .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, 10788c2ecf20Sopenharmony_ci .has_hwecc = 1, 10798c2ecf20Sopenharmony_ci .use_holden = 1, 10808c2ecf20Sopenharmony_ci}; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic const struct of_device_id of_flctl_match[] = { 10838c2ecf20Sopenharmony_ci { .compatible = "renesas,shmobile-flctl-sh7372", 10848c2ecf20Sopenharmony_ci .data = &flctl_sh7372_config }, 10858c2ecf20Sopenharmony_ci {}, 10868c2ecf20Sopenharmony_ci}; 10878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_flctl_match); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci const struct flctl_soc_config *config; 10928c2ecf20Sopenharmony_ci struct sh_flctl_platform_data *pdata; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci config = of_device_get_match_data(dev); 10958c2ecf20Sopenharmony_ci if (!config) { 10968c2ecf20Sopenharmony_ci dev_err(dev, "%s: no OF configuration attached\n", __func__); 10978c2ecf20Sopenharmony_ci return NULL; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), 11018c2ecf20Sopenharmony_ci GFP_KERNEL); 11028c2ecf20Sopenharmony_ci if (!pdata) 11038c2ecf20Sopenharmony_ci return NULL; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* set SoC specific options */ 11068c2ecf20Sopenharmony_ci pdata->flcmncr_val = config->flcmncr_val; 11078c2ecf20Sopenharmony_ci pdata->has_hwecc = config->has_hwecc; 11088c2ecf20Sopenharmony_ci pdata->use_holden = config->use_holden; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci return pdata; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int flctl_probe(struct platform_device *pdev) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct resource *res; 11168c2ecf20Sopenharmony_ci struct sh_flctl *flctl; 11178c2ecf20Sopenharmony_ci struct mtd_info *flctl_mtd; 11188c2ecf20Sopenharmony_ci struct nand_chip *nand; 11198c2ecf20Sopenharmony_ci struct sh_flctl_platform_data *pdata; 11208c2ecf20Sopenharmony_ci int ret; 11218c2ecf20Sopenharmony_ci int irq; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL); 11248c2ecf20Sopenharmony_ci if (!flctl) 11258c2ecf20Sopenharmony_ci return -ENOMEM; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11288c2ecf20Sopenharmony_ci flctl->reg = devm_ioremap_resource(&pdev->dev, res); 11298c2ecf20Sopenharmony_ci if (IS_ERR(flctl->reg)) 11308c2ecf20Sopenharmony_ci return PTR_ERR(flctl->reg); 11318c2ecf20Sopenharmony_ci flctl->fifo = res->start + 0x24; /* FLDTFIFO */ 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 11348c2ecf20Sopenharmony_ci if (irq < 0) 11358c2ecf20Sopenharmony_ci return irq; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, 11388c2ecf20Sopenharmony_ci "flste", flctl); 11398c2ecf20Sopenharmony_ci if (ret) { 11408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request interrupt failed.\n"); 11418c2ecf20Sopenharmony_ci return ret; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (pdev->dev.of_node) 11458c2ecf20Sopenharmony_ci pdata = flctl_parse_dt(&pdev->dev); 11468c2ecf20Sopenharmony_ci else 11478c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (!pdata) { 11508c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no setup data defined\n"); 11518c2ecf20Sopenharmony_ci return -EINVAL; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, flctl); 11558c2ecf20Sopenharmony_ci nand = &flctl->chip; 11568c2ecf20Sopenharmony_ci flctl_mtd = nand_to_mtd(nand); 11578c2ecf20Sopenharmony_ci nand_set_flash_node(nand, pdev->dev.of_node); 11588c2ecf20Sopenharmony_ci flctl_mtd->dev.parent = &pdev->dev; 11598c2ecf20Sopenharmony_ci flctl->pdev = pdev; 11608c2ecf20Sopenharmony_ci flctl->hwecc = pdata->has_hwecc; 11618c2ecf20Sopenharmony_ci flctl->holden = pdata->use_holden; 11628c2ecf20Sopenharmony_ci flctl->flcmncr_base = pdata->flcmncr_val; 11638c2ecf20Sopenharmony_ci flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* Set address of hardware control function */ 11668c2ecf20Sopenharmony_ci /* 20 us command delay time */ 11678c2ecf20Sopenharmony_ci nand->legacy.chip_delay = 20; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci nand->legacy.read_byte = flctl_read_byte; 11708c2ecf20Sopenharmony_ci nand->legacy.write_buf = flctl_write_buf; 11718c2ecf20Sopenharmony_ci nand->legacy.read_buf = flctl_read_buf; 11728c2ecf20Sopenharmony_ci nand->legacy.select_chip = flctl_select_chip; 11738c2ecf20Sopenharmony_ci nand->legacy.cmdfunc = flctl_cmdfunc; 11748c2ecf20Sopenharmony_ci nand->legacy.set_features = nand_get_set_features_notsupp; 11758c2ecf20Sopenharmony_ci nand->legacy.get_features = nand_get_set_features_notsupp; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci if (pdata->flcmncr_val & SEL_16BIT) 11788c2ecf20Sopenharmony_ci nand->options |= NAND_BUSWIDTH_16; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci nand->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 11838c2ecf20Sopenharmony_ci pm_runtime_resume(&pdev->dev); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci flctl_setup_dma(flctl); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops; 11888c2ecf20Sopenharmony_ci ret = nand_scan(nand, 1); 11898c2ecf20Sopenharmony_ci if (ret) 11908c2ecf20Sopenharmony_ci goto err_chip; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); 11938c2ecf20Sopenharmony_ci if (ret) 11948c2ecf20Sopenharmony_ci goto cleanup_nand; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return 0; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cicleanup_nand: 11998c2ecf20Sopenharmony_ci nand_cleanup(nand); 12008c2ecf20Sopenharmony_cierr_chip: 12018c2ecf20Sopenharmony_ci flctl_release_dma(flctl); 12028c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 12038c2ecf20Sopenharmony_ci return ret; 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic int flctl_remove(struct platform_device *pdev) 12078c2ecf20Sopenharmony_ci{ 12088c2ecf20Sopenharmony_ci struct sh_flctl *flctl = platform_get_drvdata(pdev); 12098c2ecf20Sopenharmony_ci struct nand_chip *chip = &flctl->chip; 12108c2ecf20Sopenharmony_ci int ret; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci flctl_release_dma(flctl); 12138c2ecf20Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 12148c2ecf20Sopenharmony_ci WARN_ON(ret); 12158c2ecf20Sopenharmony_ci nand_cleanup(chip); 12168c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci return 0; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic struct platform_driver flctl_driver = { 12228c2ecf20Sopenharmony_ci .remove = flctl_remove, 12238c2ecf20Sopenharmony_ci .driver = { 12248c2ecf20Sopenharmony_ci .name = "sh_flctl", 12258c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(of_flctl_match), 12268c2ecf20Sopenharmony_ci }, 12278c2ecf20Sopenharmony_ci}; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_cimodule_platform_driver_probe(flctl_driver, flctl_probe); 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 12328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda"); 12338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SuperH FLCTL driver"); 12348c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:sh_flctl"); 1235