162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SuperH FLCTL nand controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2008 Renesas Solutions Corp. 662306a36Sopenharmony_ci * Copyright (c) 2008 Atom Create Engineering Co., Ltd. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/completion.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/dmaengine.h> 1662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2262306a36Sopenharmony_ci#include <linux/sh_dma.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2762306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 2862306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 2962306a36Sopenharmony_ci#include <linux/mtd/sh_flctl.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_sp_ecc(struct mtd_info *mtd, int section, 3262306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (section) 3762306a36Sopenharmony_ci return -ERANGE; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci oobregion->offset = 0; 4062306a36Sopenharmony_ci oobregion->length = chip->ecc.bytes; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return 0; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_sp_free(struct mtd_info *mtd, int section, 4662306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci if (section) 4962306a36Sopenharmony_ci return -ERANGE; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci oobregion->offset = 12; 5262306a36Sopenharmony_ci oobregion->length = 4; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops flctl_4secc_oob_smallpage_ops = { 5862306a36Sopenharmony_ci .ecc = flctl_4secc_ooblayout_sp_ecc, 5962306a36Sopenharmony_ci .free = flctl_4secc_ooblayout_sp_free, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_lp_ecc(struct mtd_info *mtd, int section, 6362306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (section >= chip->ecc.steps) 6862306a36Sopenharmony_ci return -ERANGE; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci oobregion->offset = (section * 16) + 6; 7162306a36Sopenharmony_ci oobregion->length = chip->ecc.bytes; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int flctl_4secc_ooblayout_lp_free(struct mtd_info *mtd, int section, 7762306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (section >= chip->ecc.steps) 8262306a36Sopenharmony_ci return -ERANGE; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci oobregion->offset = section * 16; 8562306a36Sopenharmony_ci oobregion->length = 6; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!section) { 8862306a36Sopenharmony_ci oobregion->offset += 2; 8962306a36Sopenharmony_ci oobregion->length -= 2; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops flctl_4secc_oob_largepage_ops = { 9662306a36Sopenharmony_ci .ecc = flctl_4secc_ooblayout_lp_ecc, 9762306a36Sopenharmony_ci .free = flctl_4secc_ooblayout_lp_free, 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic uint8_t scan_ff_pattern[] = { 0xff, 0xff }; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic struct nand_bbt_descr flctl_4secc_smallpage = { 10362306a36Sopenharmony_ci .offs = 11, 10462306a36Sopenharmony_ci .len = 1, 10562306a36Sopenharmony_ci .pattern = scan_ff_pattern, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct nand_bbt_descr flctl_4secc_largepage = { 10962306a36Sopenharmony_ci .offs = 0, 11062306a36Sopenharmony_ci .len = 2, 11162306a36Sopenharmony_ci .pattern = scan_ff_pattern, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void empty_fifo(struct sh_flctl *flctl) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci writel(flctl->flintdmacr_base | AC1CLR | AC0CLR, FLINTDMACR(flctl)); 11762306a36Sopenharmony_ci writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void start_translation(struct sh_flctl *flctl) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci writeb(TRSTRT, FLTRCR(flctl)); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void timeout_error(struct sh_flctl *flctl, const char *str) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci dev_err(&flctl->pdev->dev, "Timeout occurred in %s\n", str); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void wait_completion(struct sh_flctl *flctl) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci while (timeout--) { 13562306a36Sopenharmony_ci if (readb(FLTRCR(flctl)) & TREND) { 13662306a36Sopenharmony_ci writeb(0x0, FLTRCR(flctl)); 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci udelay(1); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci timeout_error(flctl, __func__); 14362306a36Sopenharmony_ci writeb(0x0, FLTRCR(flctl)); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void flctl_dma_complete(void *param) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct sh_flctl *flctl = param; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci complete(&flctl->dma_complete); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void flctl_release_dma(struct sh_flctl *flctl) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci if (flctl->chan_fifo0_rx) { 15662306a36Sopenharmony_ci dma_release_channel(flctl->chan_fifo0_rx); 15762306a36Sopenharmony_ci flctl->chan_fifo0_rx = NULL; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci if (flctl->chan_fifo0_tx) { 16062306a36Sopenharmony_ci dma_release_channel(flctl->chan_fifo0_tx); 16162306a36Sopenharmony_ci flctl->chan_fifo0_tx = NULL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void flctl_setup_dma(struct sh_flctl *flctl) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci dma_cap_mask_t mask; 16862306a36Sopenharmony_ci struct dma_slave_config cfg; 16962306a36Sopenharmony_ci struct platform_device *pdev = flctl->pdev; 17062306a36Sopenharmony_ci struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!pdata) 17462306a36Sopenharmony_ci return; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0) 17762306a36Sopenharmony_ci return; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* We can only either use DMA for both Tx and Rx or not use it at all */ 18062306a36Sopenharmony_ci dma_cap_zero(mask); 18162306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter, 18462306a36Sopenharmony_ci (void *)(uintptr_t)pdata->slave_id_fifo0_tx); 18562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__, 18662306a36Sopenharmony_ci flctl->chan_fifo0_tx); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!flctl->chan_fifo0_tx) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 19262306a36Sopenharmony_ci cfg.direction = DMA_MEM_TO_DEV; 19362306a36Sopenharmony_ci cfg.dst_addr = flctl->fifo; 19462306a36Sopenharmony_ci cfg.src_addr = 0; 19562306a36Sopenharmony_ci ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg); 19662306a36Sopenharmony_ci if (ret < 0) 19762306a36Sopenharmony_ci goto err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter, 20062306a36Sopenharmony_ci (void *)(uintptr_t)pdata->slave_id_fifo0_rx); 20162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__, 20262306a36Sopenharmony_ci flctl->chan_fifo0_rx); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!flctl->chan_fifo0_rx) 20562306a36Sopenharmony_ci goto err; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci cfg.direction = DMA_DEV_TO_MEM; 20862306a36Sopenharmony_ci cfg.dst_addr = 0; 20962306a36Sopenharmony_ci cfg.src_addr = flctl->fifo; 21062306a36Sopenharmony_ci ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg); 21162306a36Sopenharmony_ci if (ret < 0) 21262306a36Sopenharmony_ci goto err; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci init_completion(&flctl->dma_complete); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cierr: 21962306a36Sopenharmony_ci flctl_release_dma(flctl); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void set_addr(struct mtd_info *mtd, int column, int page_addr) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 22562306a36Sopenharmony_ci uint32_t addr = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (column == -1) { 22862306a36Sopenharmony_ci addr = page_addr; /* ERASE1 */ 22962306a36Sopenharmony_ci } else if (page_addr != -1) { 23062306a36Sopenharmony_ci /* SEQIN, READ0, etc.. */ 23162306a36Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 23262306a36Sopenharmony_ci column >>= 1; 23362306a36Sopenharmony_ci if (flctl->page_size) { 23462306a36Sopenharmony_ci addr = column & 0x0FFF; 23562306a36Sopenharmony_ci addr |= (page_addr & 0xff) << 16; 23662306a36Sopenharmony_ci addr |= ((page_addr >> 8) & 0xff) << 24; 23762306a36Sopenharmony_ci /* big than 128MB */ 23862306a36Sopenharmony_ci if (flctl->rw_ADRCNT == ADRCNT2_E) { 23962306a36Sopenharmony_ci uint32_t addr2; 24062306a36Sopenharmony_ci addr2 = (page_addr >> 16) & 0xff; 24162306a36Sopenharmony_ci writel(addr2, FLADR2(flctl)); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci addr = column; 24562306a36Sopenharmony_ci addr |= (page_addr & 0xff) << 8; 24662306a36Sopenharmony_ci addr |= ((page_addr >> 8) & 0xff) << 16; 24762306a36Sopenharmony_ci addr |= ((page_addr >> 16) & 0xff) << 24; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci writel(addr, FLADR(flctl)); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void wait_rfifo_ready(struct sh_flctl *flctl) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci while (timeout--) { 25862306a36Sopenharmony_ci uint32_t val; 25962306a36Sopenharmony_ci /* check FIFO */ 26062306a36Sopenharmony_ci val = readl(FLDTCNTR(flctl)) >> 16; 26162306a36Sopenharmony_ci if (val & 0xFF) 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci udelay(1); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci timeout_error(flctl, __func__); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void wait_wfifo_ready(struct sh_flctl *flctl) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci uint32_t len, timeout = LOOP_TIMEOUT_MAX; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci while (timeout--) { 27362306a36Sopenharmony_ci /* check FIFO */ 27462306a36Sopenharmony_ci len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; 27562306a36Sopenharmony_ci if (len >= 4) 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci udelay(1); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci timeout_error(flctl, __func__); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic enum flctl_ecc_res_t wait_recfifo_ready 28362306a36Sopenharmony_ci (struct sh_flctl *flctl, int sector_number) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 28662306a36Sopenharmony_ci void __iomem *ecc_reg[4]; 28762306a36Sopenharmony_ci int i; 28862306a36Sopenharmony_ci int state = FL_SUCCESS; 28962306a36Sopenharmony_ci uint32_t data, size; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * First this loops checks in FLDTCNTR if we are ready to read out the 29362306a36Sopenharmony_ci * oob data. This is the case if either all went fine without errors or 29462306a36Sopenharmony_ci * if the bottom part of the loop corrected the errors or marked them as 29562306a36Sopenharmony_ci * uncorrectable and the controller is given time to push the data into 29662306a36Sopenharmony_ci * the FIFO. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci while (timeout--) { 29962306a36Sopenharmony_ci /* check if all is ok and we can read out the OOB */ 30062306a36Sopenharmony_ci size = readl(FLDTCNTR(flctl)) >> 24; 30162306a36Sopenharmony_ci if ((size & 0xFF) == 4) 30262306a36Sopenharmony_ci return state; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* check if a correction code has been calculated */ 30562306a36Sopenharmony_ci if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) { 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * either we wait for the fifo to be filled or a 30862306a36Sopenharmony_ci * correction pattern is being generated 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci udelay(1); 31162306a36Sopenharmony_ci continue; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* check for an uncorrectable error */ 31562306a36Sopenharmony_ci if (readl(FL4ECCCR(flctl)) & _4ECCFA) { 31662306a36Sopenharmony_ci /* check if we face a non-empty page */ 31762306a36Sopenharmony_ci for (i = 0; i < 512; i++) { 31862306a36Sopenharmony_ci if (flctl->done_buff[i] != 0xff) { 31962306a36Sopenharmony_ci state = FL_ERROR; /* can't correct */ 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (state == FL_SUCCESS) 32562306a36Sopenharmony_ci dev_dbg(&flctl->pdev->dev, 32662306a36Sopenharmony_ci "reading empty sector %d, ecc error ignored\n", 32762306a36Sopenharmony_ci sector_number); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci writel(0, FL4ECCCR(flctl)); 33062306a36Sopenharmony_ci continue; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* start error correction */ 33462306a36Sopenharmony_ci ecc_reg[0] = FL4ECCRESULT0(flctl); 33562306a36Sopenharmony_ci ecc_reg[1] = FL4ECCRESULT1(flctl); 33662306a36Sopenharmony_ci ecc_reg[2] = FL4ECCRESULT2(flctl); 33762306a36Sopenharmony_ci ecc_reg[3] = FL4ECCRESULT3(flctl); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 34062306a36Sopenharmony_ci uint8_t org; 34162306a36Sopenharmony_ci unsigned int index; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci data = readl(ecc_reg[i]); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (flctl->page_size) 34662306a36Sopenharmony_ci index = (512 * sector_number) + 34762306a36Sopenharmony_ci (data >> 16); 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci index = data >> 16; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci org = flctl->done_buff[index]; 35262306a36Sopenharmony_ci flctl->done_buff[index] = org ^ (data & 0xFF); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci state = FL_REPAIRABLE; 35562306a36Sopenharmony_ci writel(0, FL4ECCCR(flctl)); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci timeout_error(flctl, __func__); 35962306a36Sopenharmony_ci return FL_TIMEOUT; /* timeout */ 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic void wait_wecfifo_ready(struct sh_flctl *flctl) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci uint32_t timeout = LOOP_TIMEOUT_MAX; 36562306a36Sopenharmony_ci uint32_t len; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci while (timeout--) { 36862306a36Sopenharmony_ci /* check FLECFIFO */ 36962306a36Sopenharmony_ci len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; 37062306a36Sopenharmony_ci if (len >= 4) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci udelay(1); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci timeout_error(flctl, __func__); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf, 37862306a36Sopenharmony_ci int len, enum dma_data_direction dir) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc = NULL; 38162306a36Sopenharmony_ci struct dma_chan *chan; 38262306a36Sopenharmony_ci enum dma_transfer_direction tr_dir; 38362306a36Sopenharmony_ci dma_addr_t dma_addr; 38462306a36Sopenharmony_ci dma_cookie_t cookie; 38562306a36Sopenharmony_ci uint32_t reg; 38662306a36Sopenharmony_ci int ret = 0; 38762306a36Sopenharmony_ci unsigned long time_left; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (dir == DMA_FROM_DEVICE) { 39062306a36Sopenharmony_ci chan = flctl->chan_fifo0_rx; 39162306a36Sopenharmony_ci tr_dir = DMA_DEV_TO_MEM; 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci chan = flctl->chan_fifo0_tx; 39462306a36Sopenharmony_ci tr_dir = DMA_MEM_TO_DEV; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci dma_addr = dma_map_single(chan->device->dev, buf, len, dir); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!dma_mapping_error(chan->device->dev, dma_addr)) 40062306a36Sopenharmony_ci desc = dmaengine_prep_slave_single(chan, dma_addr, len, 40162306a36Sopenharmony_ci tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (desc) { 40462306a36Sopenharmony_ci reg = readl(FLINTDMACR(flctl)); 40562306a36Sopenharmony_ci reg |= DREQ0EN; 40662306a36Sopenharmony_ci writel(reg, FLINTDMACR(flctl)); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci desc->callback = flctl_dma_complete; 40962306a36Sopenharmony_ci desc->callback_param = flctl; 41062306a36Sopenharmony_ci cookie = dmaengine_submit(desc); 41162306a36Sopenharmony_ci if (dma_submit_error(cookie)) { 41262306a36Sopenharmony_ci ret = dma_submit_error(cookie); 41362306a36Sopenharmony_ci dev_warn(&flctl->pdev->dev, 41462306a36Sopenharmony_ci "DMA submit failed, falling back to PIO\n"); 41562306a36Sopenharmony_ci goto out; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci dma_async_issue_pending(chan); 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci /* DMA failed, fall back to PIO */ 42162306a36Sopenharmony_ci flctl_release_dma(flctl); 42262306a36Sopenharmony_ci dev_warn(&flctl->pdev->dev, 42362306a36Sopenharmony_ci "DMA failed, falling back to PIO\n"); 42462306a36Sopenharmony_ci ret = -EIO; 42562306a36Sopenharmony_ci goto out; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci time_left = 42962306a36Sopenharmony_ci wait_for_completion_timeout(&flctl->dma_complete, 43062306a36Sopenharmony_ci msecs_to_jiffies(3000)); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (time_left == 0) { 43362306a36Sopenharmony_ci dmaengine_terminate_all(chan); 43462306a36Sopenharmony_ci dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n"); 43562306a36Sopenharmony_ci ret = -ETIMEDOUT; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciout: 43962306a36Sopenharmony_ci reg = readl(FLINTDMACR(flctl)); 44062306a36Sopenharmony_ci reg &= ~DREQ0EN; 44162306a36Sopenharmony_ci writel(reg, FLINTDMACR(flctl)); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci dma_unmap_single(chan->device->dev, dma_addr, len, dir); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* ret == 0 is success */ 44662306a36Sopenharmony_ci return ret; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic void read_datareg(struct sh_flctl *flctl, int offset) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci unsigned long data; 45262306a36Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci wait_completion(flctl); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci data = readl(FLDATAR(flctl)); 45762306a36Sopenharmony_ci *buf = le32_to_cpu(data); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int i, len_4align; 46362306a36Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci len_4align = (rlen + 3) / 4; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* initiate DMA transfer */ 46862306a36Sopenharmony_ci if (flctl->chan_fifo0_rx && rlen >= 32 && 46962306a36Sopenharmony_ci !flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_FROM_DEVICE)) 47062306a36Sopenharmony_ci goto convert; /* DMA success */ 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* do polling transfer */ 47362306a36Sopenharmony_ci for (i = 0; i < len_4align; i++) { 47462306a36Sopenharmony_ci wait_rfifo_ready(flctl); 47562306a36Sopenharmony_ci buf[i] = readl(FLDTFIFO(flctl)); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ciconvert: 47962306a36Sopenharmony_ci for (i = 0; i < len_4align; i++) 48062306a36Sopenharmony_ci buf[i] = be32_to_cpu(buf[i]); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic enum flctl_ecc_res_t read_ecfiforeg 48462306a36Sopenharmony_ci (struct sh_flctl *flctl, uint8_t *buff, int sector) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci int i; 48762306a36Sopenharmony_ci enum flctl_ecc_res_t res; 48862306a36Sopenharmony_ci unsigned long *ecc_buf = (unsigned long *)buff; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci res = wait_recfifo_ready(flctl , sector); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (res != FL_ERROR) { 49362306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 49462306a36Sopenharmony_ci ecc_buf[i] = readl(FLECFIFO(flctl)); 49562306a36Sopenharmony_ci ecc_buf[i] = be32_to_cpu(ecc_buf[i]); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return res; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void write_fiforeg(struct sh_flctl *flctl, int rlen, 50362306a36Sopenharmony_ci unsigned int offset) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci int i, len_4align; 50662306a36Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci len_4align = (rlen + 3) / 4; 50962306a36Sopenharmony_ci for (i = 0; i < len_4align; i++) { 51062306a36Sopenharmony_ci wait_wfifo_ready(flctl); 51162306a36Sopenharmony_ci writel(cpu_to_be32(buf[i]), FLDTFIFO(flctl)); 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic void write_ec_fiforeg(struct sh_flctl *flctl, int rlen, 51662306a36Sopenharmony_ci unsigned int offset) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci int i, len_4align; 51962306a36Sopenharmony_ci unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci len_4align = (rlen + 3) / 4; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci for (i = 0; i < len_4align; i++) 52462306a36Sopenharmony_ci buf[i] = cpu_to_be32(buf[i]); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* initiate DMA transfer */ 52762306a36Sopenharmony_ci if (flctl->chan_fifo0_tx && rlen >= 32 && 52862306a36Sopenharmony_ci !flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_TO_DEVICE)) 52962306a36Sopenharmony_ci return; /* DMA success */ 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* do polling transfer */ 53262306a36Sopenharmony_ci for (i = 0; i < len_4align; i++) { 53362306a36Sopenharmony_ci wait_wecfifo_ready(flctl); 53462306a36Sopenharmony_ci writel(buf[i], FLECFIFO(flctl)); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 54162306a36Sopenharmony_ci uint32_t flcmncr_val = flctl->flcmncr_base & ~SEL_16BIT; 54262306a36Sopenharmony_ci uint32_t flcmdcr_val, addr_len_bytes = 0; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* Set SNAND bit if page size is 2048byte */ 54562306a36Sopenharmony_ci if (flctl->page_size) 54662306a36Sopenharmony_ci flcmncr_val |= SNAND_E; 54762306a36Sopenharmony_ci else 54862306a36Sopenharmony_ci flcmncr_val &= ~SNAND_E; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* default FLCMDCR val */ 55162306a36Sopenharmony_ci flcmdcr_val = DOCMD1_E | DOADR_E; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Set for FLCMDCR */ 55462306a36Sopenharmony_ci switch (cmd) { 55562306a36Sopenharmony_ci case NAND_CMD_ERASE1: 55662306a36Sopenharmony_ci addr_len_bytes = flctl->erase_ADRCNT; 55762306a36Sopenharmony_ci flcmdcr_val |= DOCMD2_E; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case NAND_CMD_READ0: 56062306a36Sopenharmony_ci case NAND_CMD_READOOB: 56162306a36Sopenharmony_ci case NAND_CMD_RNDOUT: 56262306a36Sopenharmony_ci addr_len_bytes = flctl->rw_ADRCNT; 56362306a36Sopenharmony_ci flcmdcr_val |= CDSRC_E; 56462306a36Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 56562306a36Sopenharmony_ci flcmncr_val |= SEL_16BIT; 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci case NAND_CMD_SEQIN: 56862306a36Sopenharmony_ci /* This case is that cmd is READ0 or READ1 or READ00 */ 56962306a36Sopenharmony_ci flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci case NAND_CMD_PAGEPROG: 57262306a36Sopenharmony_ci addr_len_bytes = flctl->rw_ADRCNT; 57362306a36Sopenharmony_ci flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; 57462306a36Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 57562306a36Sopenharmony_ci flcmncr_val |= SEL_16BIT; 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case NAND_CMD_READID: 57862306a36Sopenharmony_ci flcmncr_val &= ~SNAND_E; 57962306a36Sopenharmony_ci flcmdcr_val |= CDSRC_E; 58062306a36Sopenharmony_ci addr_len_bytes = ADRCNT_1; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci case NAND_CMD_STATUS: 58362306a36Sopenharmony_ci case NAND_CMD_RESET: 58462306a36Sopenharmony_ci flcmncr_val &= ~SNAND_E; 58562306a36Sopenharmony_ci flcmdcr_val &= ~(DOADR_E | DOSR_E); 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci default: 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Set address bytes parameter */ 59262306a36Sopenharmony_ci flcmdcr_val |= addr_len_bytes; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Now actually write */ 59562306a36Sopenharmony_ci writel(flcmncr_val, FLCMNCR(flctl)); 59662306a36Sopenharmony_ci writel(flcmdcr_val, FLCMDCR(flctl)); 59762306a36Sopenharmony_ci writel(flcmcdr_val, FLCMCDR(flctl)); 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic int flctl_read_page_hwecc(struct nand_chip *chip, uint8_t *buf, 60162306a36Sopenharmony_ci int oob_required, int page) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci nand_read_page_op(chip, page, 0, buf, mtd->writesize); 60662306a36Sopenharmony_ci if (oob_required) 60762306a36Sopenharmony_ci chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize); 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int flctl_write_page_hwecc(struct nand_chip *chip, const uint8_t *buf, 61262306a36Sopenharmony_ci int oob_required, int page) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); 61762306a36Sopenharmony_ci chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize); 61862306a36Sopenharmony_ci return nand_prog_page_end_op(chip); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 62462306a36Sopenharmony_ci int sector, page_sectors; 62562306a36Sopenharmony_ci enum flctl_ecc_res_t ecc_result; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci page_sectors = flctl->page_size ? 4 : 1; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_READ0, 63062306a36Sopenharmony_ci (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, 63362306a36Sopenharmony_ci FLCMNCR(flctl)); 63462306a36Sopenharmony_ci writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); 63562306a36Sopenharmony_ci writel(page_addr << 2, FLADR(flctl)); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci empty_fifo(flctl); 63862306a36Sopenharmony_ci start_translation(flctl); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci for (sector = 0; sector < page_sectors; sector++) { 64162306a36Sopenharmony_ci read_fiforeg(flctl, 512, 512 * sector); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci ecc_result = read_ecfiforeg(flctl, 64462306a36Sopenharmony_ci &flctl->done_buff[mtd->writesize + 16 * sector], 64562306a36Sopenharmony_ci sector); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci switch (ecc_result) { 64862306a36Sopenharmony_ci case FL_REPAIRABLE: 64962306a36Sopenharmony_ci dev_info(&flctl->pdev->dev, 65062306a36Sopenharmony_ci "applied ecc on page 0x%x", page_addr); 65162306a36Sopenharmony_ci mtd->ecc_stats.corrected++; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case FL_ERROR: 65462306a36Sopenharmony_ci dev_warn(&flctl->pdev->dev, 65562306a36Sopenharmony_ci "page 0x%x contains corrupted data\n", 65662306a36Sopenharmony_ci page_addr); 65762306a36Sopenharmony_ci mtd->ecc_stats.failed++; 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci default: 66062306a36Sopenharmony_ci ; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci wait_completion(flctl); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), 66762306a36Sopenharmony_ci FLCMNCR(flctl)); 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic void execmd_read_oob(struct mtd_info *mtd, int page_addr) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 67362306a36Sopenharmony_ci int page_sectors = flctl->page_size ? 4 : 1; 67462306a36Sopenharmony_ci int i; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_READ0, 67762306a36Sopenharmony_ci (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci empty_fifo(flctl); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci for (i = 0; i < page_sectors; i++) { 68262306a36Sopenharmony_ci set_addr(mtd, (512 + 16) * i + 512 , page_addr); 68362306a36Sopenharmony_ci writel(16, FLDTCNTR(flctl)); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci start_translation(flctl); 68662306a36Sopenharmony_ci read_fiforeg(flctl, 16, 16 * i); 68762306a36Sopenharmony_ci wait_completion(flctl); 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic void execmd_write_page_sector(struct mtd_info *mtd) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 69462306a36Sopenharmony_ci int page_addr = flctl->seqin_page_addr; 69562306a36Sopenharmony_ci int sector, page_sectors; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci page_sectors = flctl->page_size ? 4 : 1; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_PAGEPROG, 70062306a36Sopenharmony_ci (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci empty_fifo(flctl); 70362306a36Sopenharmony_ci writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); 70462306a36Sopenharmony_ci writel(readl(FLCMDCR(flctl)) | page_sectors, FLCMDCR(flctl)); 70562306a36Sopenharmony_ci writel(page_addr << 2, FLADR(flctl)); 70662306a36Sopenharmony_ci start_translation(flctl); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci for (sector = 0; sector < page_sectors; sector++) { 70962306a36Sopenharmony_ci write_fiforeg(flctl, 512, 512 * sector); 71062306a36Sopenharmony_ci write_ec_fiforeg(flctl, 16, mtd->writesize + 16 * sector); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci wait_completion(flctl); 71462306a36Sopenharmony_ci writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic void execmd_write_oob(struct mtd_info *mtd) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 72062306a36Sopenharmony_ci int page_addr = flctl->seqin_page_addr; 72162306a36Sopenharmony_ci int sector, page_sectors; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci page_sectors = flctl->page_size ? 4 : 1; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_PAGEPROG, 72662306a36Sopenharmony_ci (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci for (sector = 0; sector < page_sectors; sector++) { 72962306a36Sopenharmony_ci empty_fifo(flctl); 73062306a36Sopenharmony_ci set_addr(mtd, sector * 528 + 512, page_addr); 73162306a36Sopenharmony_ci writel(16, FLDTCNTR(flctl)); /* set read size */ 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci start_translation(flctl); 73462306a36Sopenharmony_ci write_fiforeg(flctl, 16, 16 * sector); 73562306a36Sopenharmony_ci wait_completion(flctl); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic void flctl_cmdfunc(struct nand_chip *chip, unsigned int command, 74062306a36Sopenharmony_ci int column, int page_addr) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 74362306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 74462306a36Sopenharmony_ci uint32_t read_cmd = 0; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci pm_runtime_get_sync(&flctl->pdev->dev); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci flctl->read_bytes = 0; 74962306a36Sopenharmony_ci if (command != NAND_CMD_PAGEPROG) 75062306a36Sopenharmony_ci flctl->index = 0; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci switch (command) { 75362306a36Sopenharmony_ci case NAND_CMD_READ1: 75462306a36Sopenharmony_ci case NAND_CMD_READ0: 75562306a36Sopenharmony_ci if (flctl->hwecc) { 75662306a36Sopenharmony_ci /* read page with hwecc */ 75762306a36Sopenharmony_ci execmd_read_page_sector(mtd, page_addr); 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci if (flctl->page_size) 76162306a36Sopenharmony_ci set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) 76262306a36Sopenharmony_ci | command); 76362306a36Sopenharmony_ci else 76462306a36Sopenharmony_ci set_cmd_regs(mtd, command, command); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci set_addr(mtd, 0, page_addr); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci flctl->read_bytes = mtd->writesize + mtd->oobsize; 76962306a36Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 77062306a36Sopenharmony_ci column >>= 1; 77162306a36Sopenharmony_ci flctl->index += column; 77262306a36Sopenharmony_ci goto read_normal_exit; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci case NAND_CMD_READOOB: 77562306a36Sopenharmony_ci if (flctl->hwecc) { 77662306a36Sopenharmony_ci /* read page with hwecc */ 77762306a36Sopenharmony_ci execmd_read_oob(mtd, page_addr); 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (flctl->page_size) { 78262306a36Sopenharmony_ci set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) 78362306a36Sopenharmony_ci | NAND_CMD_READ0); 78462306a36Sopenharmony_ci set_addr(mtd, mtd->writesize, page_addr); 78562306a36Sopenharmony_ci } else { 78662306a36Sopenharmony_ci set_cmd_regs(mtd, command, command); 78762306a36Sopenharmony_ci set_addr(mtd, 0, page_addr); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci flctl->read_bytes = mtd->oobsize; 79062306a36Sopenharmony_ci goto read_normal_exit; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci case NAND_CMD_RNDOUT: 79362306a36Sopenharmony_ci if (flctl->hwecc) 79462306a36Sopenharmony_ci break; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (flctl->page_size) 79762306a36Sopenharmony_ci set_cmd_regs(mtd, command, (NAND_CMD_RNDOUTSTART << 8) 79862306a36Sopenharmony_ci | command); 79962306a36Sopenharmony_ci else 80062306a36Sopenharmony_ci set_cmd_regs(mtd, command, command); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci set_addr(mtd, column, 0); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci flctl->read_bytes = mtd->writesize + mtd->oobsize - column; 80562306a36Sopenharmony_ci goto read_normal_exit; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci case NAND_CMD_READID: 80862306a36Sopenharmony_ci set_cmd_regs(mtd, command, command); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* READID is always performed using an 8-bit bus */ 81162306a36Sopenharmony_ci if (flctl->chip.options & NAND_BUSWIDTH_16) 81262306a36Sopenharmony_ci column <<= 1; 81362306a36Sopenharmony_ci set_addr(mtd, column, 0); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci flctl->read_bytes = 8; 81662306a36Sopenharmony_ci writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ 81762306a36Sopenharmony_ci empty_fifo(flctl); 81862306a36Sopenharmony_ci start_translation(flctl); 81962306a36Sopenharmony_ci read_fiforeg(flctl, flctl->read_bytes, 0); 82062306a36Sopenharmony_ci wait_completion(flctl); 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci case NAND_CMD_ERASE1: 82462306a36Sopenharmony_ci flctl->erase1_page_addr = page_addr; 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci case NAND_CMD_ERASE2: 82862306a36Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_ERASE1, 82962306a36Sopenharmony_ci (command << 8) | NAND_CMD_ERASE1); 83062306a36Sopenharmony_ci set_addr(mtd, -1, flctl->erase1_page_addr); 83162306a36Sopenharmony_ci start_translation(flctl); 83262306a36Sopenharmony_ci wait_completion(flctl); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci case NAND_CMD_SEQIN: 83662306a36Sopenharmony_ci if (!flctl->page_size) { 83762306a36Sopenharmony_ci /* output read command */ 83862306a36Sopenharmony_ci if (column >= mtd->writesize) { 83962306a36Sopenharmony_ci column -= mtd->writesize; 84062306a36Sopenharmony_ci read_cmd = NAND_CMD_READOOB; 84162306a36Sopenharmony_ci } else if (column < 256) { 84262306a36Sopenharmony_ci read_cmd = NAND_CMD_READ0; 84362306a36Sopenharmony_ci } else { 84462306a36Sopenharmony_ci column -= 256; 84562306a36Sopenharmony_ci read_cmd = NAND_CMD_READ1; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci flctl->seqin_column = column; 84962306a36Sopenharmony_ci flctl->seqin_page_addr = page_addr; 85062306a36Sopenharmony_ci flctl->seqin_read_cmd = read_cmd; 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci case NAND_CMD_PAGEPROG: 85462306a36Sopenharmony_ci empty_fifo(flctl); 85562306a36Sopenharmony_ci if (!flctl->page_size) { 85662306a36Sopenharmony_ci set_cmd_regs(mtd, NAND_CMD_SEQIN, 85762306a36Sopenharmony_ci flctl->seqin_read_cmd); 85862306a36Sopenharmony_ci set_addr(mtd, -1, -1); 85962306a36Sopenharmony_ci writel(0, FLDTCNTR(flctl)); /* set 0 size */ 86062306a36Sopenharmony_ci start_translation(flctl); 86162306a36Sopenharmony_ci wait_completion(flctl); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci if (flctl->hwecc) { 86462306a36Sopenharmony_ci /* write page with hwecc */ 86562306a36Sopenharmony_ci if (flctl->seqin_column == mtd->writesize) 86662306a36Sopenharmony_ci execmd_write_oob(mtd); 86762306a36Sopenharmony_ci else if (!flctl->seqin_column) 86862306a36Sopenharmony_ci execmd_write_page_sector(mtd); 86962306a36Sopenharmony_ci else 87062306a36Sopenharmony_ci pr_err("Invalid address !?\n"); 87162306a36Sopenharmony_ci break; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); 87462306a36Sopenharmony_ci set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); 87562306a36Sopenharmony_ci writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ 87662306a36Sopenharmony_ci start_translation(flctl); 87762306a36Sopenharmony_ci write_fiforeg(flctl, flctl->index, 0); 87862306a36Sopenharmony_ci wait_completion(flctl); 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci case NAND_CMD_STATUS: 88262306a36Sopenharmony_ci set_cmd_regs(mtd, command, command); 88362306a36Sopenharmony_ci set_addr(mtd, -1, -1); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci flctl->read_bytes = 1; 88662306a36Sopenharmony_ci writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ 88762306a36Sopenharmony_ci start_translation(flctl); 88862306a36Sopenharmony_ci read_datareg(flctl, 0); /* read and end */ 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci case NAND_CMD_RESET: 89262306a36Sopenharmony_ci set_cmd_regs(mtd, command, command); 89362306a36Sopenharmony_ci set_addr(mtd, -1, -1); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci writel(0, FLDTCNTR(flctl)); /* set 0 size */ 89662306a36Sopenharmony_ci start_translation(flctl); 89762306a36Sopenharmony_ci wait_completion(flctl); 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci default: 90162306a36Sopenharmony_ci break; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci goto runtime_exit; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ciread_normal_exit: 90662306a36Sopenharmony_ci writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ 90762306a36Sopenharmony_ci empty_fifo(flctl); 90862306a36Sopenharmony_ci start_translation(flctl); 90962306a36Sopenharmony_ci read_fiforeg(flctl, flctl->read_bytes, 0); 91062306a36Sopenharmony_ci wait_completion(flctl); 91162306a36Sopenharmony_ciruntime_exit: 91262306a36Sopenharmony_ci pm_runtime_put_sync(&flctl->pdev->dev); 91362306a36Sopenharmony_ci return; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic void flctl_select_chip(struct nand_chip *chip, int chipnr) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 91962306a36Sopenharmony_ci int ret; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci switch (chipnr) { 92262306a36Sopenharmony_ci case -1: 92362306a36Sopenharmony_ci flctl->flcmncr_base &= ~CE0_ENABLE; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci pm_runtime_get_sync(&flctl->pdev->dev); 92662306a36Sopenharmony_ci writel(flctl->flcmncr_base, FLCMNCR(flctl)); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (flctl->qos_request) { 92962306a36Sopenharmony_ci dev_pm_qos_remove_request(&flctl->pm_qos); 93062306a36Sopenharmony_ci flctl->qos_request = 0; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci pm_runtime_put_sync(&flctl->pdev->dev); 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci case 0: 93662306a36Sopenharmony_ci flctl->flcmncr_base |= CE0_ENABLE; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci if (!flctl->qos_request) { 93962306a36Sopenharmony_ci ret = dev_pm_qos_add_request(&flctl->pdev->dev, 94062306a36Sopenharmony_ci &flctl->pm_qos, 94162306a36Sopenharmony_ci DEV_PM_QOS_RESUME_LATENCY, 94262306a36Sopenharmony_ci 100); 94362306a36Sopenharmony_ci if (ret < 0) 94462306a36Sopenharmony_ci dev_err(&flctl->pdev->dev, 94562306a36Sopenharmony_ci "PM QoS request failed: %d\n", ret); 94662306a36Sopenharmony_ci flctl->qos_request = 1; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (flctl->holden) { 95062306a36Sopenharmony_ci pm_runtime_get_sync(&flctl->pdev->dev); 95162306a36Sopenharmony_ci writel(HOLDEN, FLHOLDCR(flctl)); 95262306a36Sopenharmony_ci pm_runtime_put_sync(&flctl->pdev->dev); 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci default: 95662306a36Sopenharmony_ci BUG(); 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic void flctl_write_buf(struct nand_chip *chip, const uint8_t *buf, int len) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci memcpy(&flctl->done_buff[flctl->index], buf, len); 96562306a36Sopenharmony_ci flctl->index += len; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic uint8_t flctl_read_byte(struct nand_chip *chip) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 97162306a36Sopenharmony_ci uint8_t data; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci data = flctl->done_buff[flctl->index]; 97462306a36Sopenharmony_ci flctl->index++; 97562306a36Sopenharmony_ci return data; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic void flctl_read_buf(struct nand_chip *chip, uint8_t *buf, int len) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(nand_to_mtd(chip)); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci memcpy(buf, &flctl->done_buff[flctl->index], len); 98362306a36Sopenharmony_ci flctl->index += len; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_cistatic int flctl_chip_attach_chip(struct nand_chip *chip) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci u64 targetsize = nanddev_target_size(&chip->base); 98962306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 99062306a36Sopenharmony_ci struct sh_flctl *flctl = mtd_to_flctl(mtd); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* 99362306a36Sopenharmony_ci * NAND_BUSWIDTH_16 may have been set by nand_scan_ident(). 99462306a36Sopenharmony_ci * Add the SEL_16BIT flag in flctl->flcmncr_base. 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_ci if (chip->options & NAND_BUSWIDTH_16) 99762306a36Sopenharmony_ci flctl->flcmncr_base |= SEL_16BIT; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (mtd->writesize == 512) { 100062306a36Sopenharmony_ci flctl->page_size = 0; 100162306a36Sopenharmony_ci if (targetsize > (32 << 20)) { 100262306a36Sopenharmony_ci /* big than 32MB */ 100362306a36Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_4; 100462306a36Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_3; 100562306a36Sopenharmony_ci } else if (targetsize > (2 << 16)) { 100662306a36Sopenharmony_ci /* big than 128KB */ 100762306a36Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_3; 100862306a36Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_2; 100962306a36Sopenharmony_ci } else { 101062306a36Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_2; 101162306a36Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_1; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci } else { 101462306a36Sopenharmony_ci flctl->page_size = 1; 101562306a36Sopenharmony_ci if (targetsize > (128 << 20)) { 101662306a36Sopenharmony_ci /* big than 128MB */ 101762306a36Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT2_E; 101862306a36Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_3; 101962306a36Sopenharmony_ci } else if (targetsize > (8 << 16)) { 102062306a36Sopenharmony_ci /* big than 512KB */ 102162306a36Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_4; 102262306a36Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_2; 102362306a36Sopenharmony_ci } else { 102462306a36Sopenharmony_ci flctl->rw_ADRCNT = ADRCNT_3; 102562306a36Sopenharmony_ci flctl->erase_ADRCNT = ADRCNT_1; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (flctl->hwecc) { 103062306a36Sopenharmony_ci if (mtd->writesize == 512) { 103162306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &flctl_4secc_oob_smallpage_ops); 103262306a36Sopenharmony_ci chip->badblock_pattern = &flctl_4secc_smallpage; 103362306a36Sopenharmony_ci } else { 103462306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &flctl_4secc_oob_largepage_ops); 103562306a36Sopenharmony_ci chip->badblock_pattern = &flctl_4secc_largepage; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci chip->ecc.size = 512; 103962306a36Sopenharmony_ci chip->ecc.bytes = 10; 104062306a36Sopenharmony_ci chip->ecc.strength = 4; 104162306a36Sopenharmony_ci chip->ecc.read_page = flctl_read_page_hwecc; 104262306a36Sopenharmony_ci chip->ecc.write_page = flctl_write_page_hwecc; 104362306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* 4 symbols ECC enabled */ 104662306a36Sopenharmony_ci flctl->flcmncr_base |= _4ECCEN; 104762306a36Sopenharmony_ci } else { 104862306a36Sopenharmony_ci chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT; 104962306a36Sopenharmony_ci chip->ecc.algo = NAND_ECC_ALGO_HAMMING; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci} 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_cistatic const struct nand_controller_ops flctl_nand_controller_ops = { 105662306a36Sopenharmony_ci .attach_chip = flctl_chip_attach_chip, 105762306a36Sopenharmony_ci}; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic irqreturn_t flctl_handle_flste(int irq, void *dev_id) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct sh_flctl *flctl = dev_id; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci dev_err(&flctl->pdev->dev, "flste irq: %x\n", readl(FLINTDMACR(flctl))); 106462306a36Sopenharmony_ci writel(flctl->flintdmacr_base, FLINTDMACR(flctl)); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci return IRQ_HANDLED; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistruct flctl_soc_config { 107062306a36Sopenharmony_ci unsigned long flcmncr_val; 107162306a36Sopenharmony_ci unsigned has_hwecc:1; 107262306a36Sopenharmony_ci unsigned use_holden:1; 107362306a36Sopenharmony_ci}; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic struct flctl_soc_config flctl_sh7372_config = { 107662306a36Sopenharmony_ci .flcmncr_val = CLK_16B_12L_4H | TYPESEL_SET | SHBUSSEL, 107762306a36Sopenharmony_ci .has_hwecc = 1, 107862306a36Sopenharmony_ci .use_holden = 1, 107962306a36Sopenharmony_ci}; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic const struct of_device_id of_flctl_match[] = { 108262306a36Sopenharmony_ci { .compatible = "renesas,shmobile-flctl-sh7372", 108362306a36Sopenharmony_ci .data = &flctl_sh7372_config }, 108462306a36Sopenharmony_ci {}, 108562306a36Sopenharmony_ci}; 108662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_flctl_match); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci const struct flctl_soc_config *config; 109162306a36Sopenharmony_ci struct sh_flctl_platform_data *pdata; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci config = of_device_get_match_data(dev); 109462306a36Sopenharmony_ci if (!config) { 109562306a36Sopenharmony_ci dev_err(dev, "%s: no OF configuration attached\n", __func__); 109662306a36Sopenharmony_ci return NULL; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(struct sh_flctl_platform_data), 110062306a36Sopenharmony_ci GFP_KERNEL); 110162306a36Sopenharmony_ci if (!pdata) 110262306a36Sopenharmony_ci return NULL; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* set SoC specific options */ 110562306a36Sopenharmony_ci pdata->flcmncr_val = config->flcmncr_val; 110662306a36Sopenharmony_ci pdata->has_hwecc = config->has_hwecc; 110762306a36Sopenharmony_ci pdata->use_holden = config->use_holden; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return pdata; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic int flctl_probe(struct platform_device *pdev) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci struct resource *res; 111562306a36Sopenharmony_ci struct sh_flctl *flctl; 111662306a36Sopenharmony_ci struct mtd_info *flctl_mtd; 111762306a36Sopenharmony_ci struct nand_chip *nand; 111862306a36Sopenharmony_ci struct sh_flctl_platform_data *pdata; 111962306a36Sopenharmony_ci int ret; 112062306a36Sopenharmony_ci int irq; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci flctl = devm_kzalloc(&pdev->dev, sizeof(struct sh_flctl), GFP_KERNEL); 112362306a36Sopenharmony_ci if (!flctl) 112462306a36Sopenharmony_ci return -ENOMEM; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci flctl->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 112762306a36Sopenharmony_ci if (IS_ERR(flctl->reg)) 112862306a36Sopenharmony_ci return PTR_ERR(flctl->reg); 112962306a36Sopenharmony_ci flctl->fifo = res->start + 0x24; /* FLDTFIFO */ 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 113262306a36Sopenharmony_ci if (irq < 0) 113362306a36Sopenharmony_ci return irq; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, flctl_handle_flste, IRQF_SHARED, 113662306a36Sopenharmony_ci "flste", flctl); 113762306a36Sopenharmony_ci if (ret) { 113862306a36Sopenharmony_ci dev_err(&pdev->dev, "request interrupt failed.\n"); 113962306a36Sopenharmony_ci return ret; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci if (pdev->dev.of_node) 114362306a36Sopenharmony_ci pdata = flctl_parse_dt(&pdev->dev); 114462306a36Sopenharmony_ci else 114562306a36Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (!pdata) { 114862306a36Sopenharmony_ci dev_err(&pdev->dev, "no setup data defined\n"); 114962306a36Sopenharmony_ci return -EINVAL; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci platform_set_drvdata(pdev, flctl); 115362306a36Sopenharmony_ci nand = &flctl->chip; 115462306a36Sopenharmony_ci flctl_mtd = nand_to_mtd(nand); 115562306a36Sopenharmony_ci nand_set_flash_node(nand, pdev->dev.of_node); 115662306a36Sopenharmony_ci flctl_mtd->dev.parent = &pdev->dev; 115762306a36Sopenharmony_ci flctl->pdev = pdev; 115862306a36Sopenharmony_ci flctl->hwecc = pdata->has_hwecc; 115962306a36Sopenharmony_ci flctl->holden = pdata->use_holden; 116062306a36Sopenharmony_ci flctl->flcmncr_base = pdata->flcmncr_val; 116162306a36Sopenharmony_ci flctl->flintdmacr_base = flctl->hwecc ? (STERINTE | ECERB) : STERINTE; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* Set address of hardware control function */ 116462306a36Sopenharmony_ci /* 20 us command delay time */ 116562306a36Sopenharmony_ci nand->legacy.chip_delay = 20; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci nand->legacy.read_byte = flctl_read_byte; 116862306a36Sopenharmony_ci nand->legacy.write_buf = flctl_write_buf; 116962306a36Sopenharmony_ci nand->legacy.read_buf = flctl_read_buf; 117062306a36Sopenharmony_ci nand->legacy.select_chip = flctl_select_chip; 117162306a36Sopenharmony_ci nand->legacy.cmdfunc = flctl_cmdfunc; 117262306a36Sopenharmony_ci nand->legacy.set_features = nand_get_set_features_notsupp; 117362306a36Sopenharmony_ci nand->legacy.get_features = nand_get_set_features_notsupp; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if (pdata->flcmncr_val & SEL_16BIT) 117662306a36Sopenharmony_ci nand->options |= NAND_BUSWIDTH_16; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci nand->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 118162306a36Sopenharmony_ci pm_runtime_resume(&pdev->dev); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci flctl_setup_dma(flctl); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops; 118662306a36Sopenharmony_ci ret = nand_scan(nand, 1); 118762306a36Sopenharmony_ci if (ret) 118862306a36Sopenharmony_ci goto err_chip; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci ret = mtd_device_register(flctl_mtd, pdata->parts, pdata->nr_parts); 119162306a36Sopenharmony_ci if (ret) 119262306a36Sopenharmony_ci goto cleanup_nand; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci return 0; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cicleanup_nand: 119762306a36Sopenharmony_ci nand_cleanup(nand); 119862306a36Sopenharmony_cierr_chip: 119962306a36Sopenharmony_ci flctl_release_dma(flctl); 120062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 120162306a36Sopenharmony_ci return ret; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic void flctl_remove(struct platform_device *pdev) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct sh_flctl *flctl = platform_get_drvdata(pdev); 120762306a36Sopenharmony_ci struct nand_chip *chip = &flctl->chip; 120862306a36Sopenharmony_ci int ret; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci flctl_release_dma(flctl); 121162306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(chip)); 121262306a36Sopenharmony_ci WARN_ON(ret); 121362306a36Sopenharmony_ci nand_cleanup(chip); 121462306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistatic struct platform_driver flctl_driver = { 121862306a36Sopenharmony_ci .remove_new = flctl_remove, 121962306a36Sopenharmony_ci .driver = { 122062306a36Sopenharmony_ci .name = "sh_flctl", 122162306a36Sopenharmony_ci .of_match_table = of_flctl_match, 122262306a36Sopenharmony_ci }, 122362306a36Sopenharmony_ci}; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cimodule_platform_driver_probe(flctl_driver, flctl_probe); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 122862306a36Sopenharmony_ciMODULE_AUTHOR("Yoshihiro Shimoda"); 122962306a36Sopenharmony_ciMODULE_DESCRIPTION("SuperH FLCTL driver"); 123062306a36Sopenharmony_ciMODULE_ALIAS("platform:sh_flctl"); 1231