162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Copyright (c) 2020 Intel Corporation. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/clk.h> 562306a36Sopenharmony_ci#include <linux/completion.h> 662306a36Sopenharmony_ci#include <linux/dmaengine.h> 762306a36Sopenharmony_ci#include <linux/dma-direction.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1662306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 1762306a36Sopenharmony_ci#include <linux/mtd/nand.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/units.h> 2562306a36Sopenharmony_ci#include <asm/unaligned.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define EBU_CLC 0x000 2862306a36Sopenharmony_ci#define EBU_CLC_RST 0x00000000u 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define EBU_ADDR_SEL(n) (0x020 + (n) * 4) 3162306a36Sopenharmony_ci/* 5 bits 26:22 included for comparison in the ADDR_SELx */ 3262306a36Sopenharmony_ci#define EBU_ADDR_MASK(x) ((x) << 4) 3362306a36Sopenharmony_ci#define EBU_ADDR_SEL_REGEN 0x1 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define EBU_BUSCON(n) (0x060 + (n) * 4) 3662306a36Sopenharmony_ci#define EBU_BUSCON_CMULT_V4 0x1 3762306a36Sopenharmony_ci#define EBU_BUSCON_RECOVC(n) ((n) << 2) 3862306a36Sopenharmony_ci#define EBU_BUSCON_HOLDC(n) ((n) << 4) 3962306a36Sopenharmony_ci#define EBU_BUSCON_WAITRDC(n) ((n) << 6) 4062306a36Sopenharmony_ci#define EBU_BUSCON_WAITWRC(n) ((n) << 8) 4162306a36Sopenharmony_ci#define EBU_BUSCON_BCGEN_CS 0x0 4262306a36Sopenharmony_ci#define EBU_BUSCON_SETUP_EN BIT(22) 4362306a36Sopenharmony_ci#define EBU_BUSCON_ALEC 0xC000 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define EBU_CON 0x0B0 4662306a36Sopenharmony_ci#define EBU_CON_NANDM_EN BIT(0) 4762306a36Sopenharmony_ci#define EBU_CON_NANDM_DIS 0x0 4862306a36Sopenharmony_ci#define EBU_CON_CSMUX_E_EN BIT(1) 4962306a36Sopenharmony_ci#define EBU_CON_ALE_P_LOW BIT(2) 5062306a36Sopenharmony_ci#define EBU_CON_CLE_P_LOW BIT(3) 5162306a36Sopenharmony_ci#define EBU_CON_CS_P_LOW BIT(4) 5262306a36Sopenharmony_ci#define EBU_CON_SE_P_LOW BIT(5) 5362306a36Sopenharmony_ci#define EBU_CON_WP_P_LOW BIT(6) 5462306a36Sopenharmony_ci#define EBU_CON_PRE_P_LOW BIT(7) 5562306a36Sopenharmony_ci#define EBU_CON_IN_CS_S(n) ((n) << 8) 5662306a36Sopenharmony_ci#define EBU_CON_OUT_CS_S(n) ((n) << 10) 5762306a36Sopenharmony_ci#define EBU_CON_LAT_EN_CS_P ((0x3D) << 18) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define EBU_WAIT 0x0B4 6062306a36Sopenharmony_ci#define EBU_WAIT_RDBY BIT(0) 6162306a36Sopenharmony_ci#define EBU_WAIT_WR_C BIT(3) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define HSNAND_CTL1 0x110 6462306a36Sopenharmony_ci#define HSNAND_CTL1_ADDR_SHIFT 24 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define HSNAND_CTL2 0x114 6762306a36Sopenharmony_ci#define HSNAND_CTL2_ADDR_SHIFT 8 6862306a36Sopenharmony_ci#define HSNAND_CTL2_CYC_N_V5 (0x2 << 16) 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define HSNAND_INT_MSK_CTL 0x124 7162306a36Sopenharmony_ci#define HSNAND_INT_MSK_CTL_WR_C BIT(4) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define HSNAND_INT_STA 0x128 7462306a36Sopenharmony_ci#define HSNAND_INT_STA_WR_C BIT(4) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define HSNAND_CTL 0x130 7762306a36Sopenharmony_ci#define HSNAND_CTL_ENABLE_ECC BIT(0) 7862306a36Sopenharmony_ci#define HSNAND_CTL_GO BIT(2) 7962306a36Sopenharmony_ci#define HSNAND_CTL_CE_SEL_CS(n) BIT(3 + (n)) 8062306a36Sopenharmony_ci#define HSNAND_CTL_RW_READ 0x0 8162306a36Sopenharmony_ci#define HSNAND_CTL_RW_WRITE BIT(10) 8262306a36Sopenharmony_ci#define HSNAND_CTL_ECC_OFF_V8TH BIT(11) 8362306a36Sopenharmony_ci#define HSNAND_CTL_CKFF_EN 0x0 8462306a36Sopenharmony_ci#define HSNAND_CTL_MSG_EN BIT(17) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define HSNAND_PARA0 0x13c 8762306a36Sopenharmony_ci#define HSNAND_PARA0_PAGE_V8192 0x3 8862306a36Sopenharmony_ci#define HSNAND_PARA0_PIB_V256 (0x3 << 4) 8962306a36Sopenharmony_ci#define HSNAND_PARA0_BYP_EN_NP 0x0 9062306a36Sopenharmony_ci#define HSNAND_PARA0_BYP_DEC_NP 0x0 9162306a36Sopenharmony_ci#define HSNAND_PARA0_TYPE_ONFI BIT(18) 9262306a36Sopenharmony_ci#define HSNAND_PARA0_ADEP_EN BIT(21) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define HSNAND_CMSG_0 0x150 9562306a36Sopenharmony_ci#define HSNAND_CMSG_1 0x154 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define HSNAND_ALE_OFFS BIT(2) 9862306a36Sopenharmony_ci#define HSNAND_CLE_OFFS BIT(3) 9962306a36Sopenharmony_ci#define HSNAND_CS_OFFS BIT(4) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define HSNAND_ECC_OFFSET 0x008 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define MAX_CS 2 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define USEC_PER_SEC 1000000L 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct ebu_nand_cs { 10862306a36Sopenharmony_ci void __iomem *chipaddr; 10962306a36Sopenharmony_ci u32 addr_sel; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistruct ebu_nand_controller { 11362306a36Sopenharmony_ci struct nand_controller controller; 11462306a36Sopenharmony_ci struct nand_chip chip; 11562306a36Sopenharmony_ci struct device *dev; 11662306a36Sopenharmony_ci void __iomem *ebu; 11762306a36Sopenharmony_ci void __iomem *hsnand; 11862306a36Sopenharmony_ci struct dma_chan *dma_tx; 11962306a36Sopenharmony_ci struct dma_chan *dma_rx; 12062306a36Sopenharmony_ci struct completion dma_access_complete; 12162306a36Sopenharmony_ci struct clk *clk; 12262306a36Sopenharmony_ci u32 nd_para0; 12362306a36Sopenharmony_ci u8 cs_num; 12462306a36Sopenharmony_ci struct ebu_nand_cs cs[MAX_CS]; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic inline struct ebu_nand_controller *nand_to_ebu(struct nand_chip *chip) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return container_of(chip, struct ebu_nand_controller, chip); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int ebu_nand_waitrdy(struct nand_chip *chip, int timeout_ms) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct ebu_nand_controller *ctrl = nand_to_ebu(chip); 13562306a36Sopenharmony_ci u32 status; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return readl_poll_timeout(ctrl->ebu + EBU_WAIT, status, 13862306a36Sopenharmony_ci (status & EBU_WAIT_RDBY) || 13962306a36Sopenharmony_ci (status & EBU_WAIT_WR_C), 20, timeout_ms); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic u8 ebu_nand_readb(struct nand_chip *chip) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 14562306a36Sopenharmony_ci u8 cs_num = ebu_host->cs_num; 14662306a36Sopenharmony_ci u8 val; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci val = readb(ebu_host->cs[cs_num].chipaddr + HSNAND_CS_OFFS); 14962306a36Sopenharmony_ci ebu_nand_waitrdy(chip, 1000); 15062306a36Sopenharmony_ci return val; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void ebu_nand_writeb(struct nand_chip *chip, u32 offset, u8 value) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 15662306a36Sopenharmony_ci u8 cs_num = ebu_host->cs_num; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci writeb(value, ebu_host->cs[cs_num].chipaddr + offset); 15962306a36Sopenharmony_ci ebu_nand_waitrdy(chip, 1000); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void ebu_read_buf(struct nand_chip *chip, u_char *buf, unsigned int len) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (i = 0; i < len; i++) 16762306a36Sopenharmony_ci buf[i] = ebu_nand_readb(chip); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void ebu_write_buf(struct nand_chip *chip, const u_char *buf, int len) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci int i; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (i = 0; i < len; i++) 17562306a36Sopenharmony_ci ebu_nand_writeb(chip, HSNAND_CS_OFFS, buf[i]); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void ebu_nand_disable(struct nand_chip *chip) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci writel(0, ebu_host->ebu + EBU_CON); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void ebu_select_chip(struct nand_chip *chip) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 18862306a36Sopenharmony_ci void __iomem *nand_con = ebu_host->ebu + EBU_CON; 18962306a36Sopenharmony_ci u32 cs = ebu_host->cs_num; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci writel(EBU_CON_NANDM_EN | EBU_CON_CSMUX_E_EN | EBU_CON_CS_P_LOW | 19262306a36Sopenharmony_ci EBU_CON_SE_P_LOW | EBU_CON_WP_P_LOW | EBU_CON_PRE_P_LOW | 19362306a36Sopenharmony_ci EBU_CON_IN_CS_S(cs) | EBU_CON_OUT_CS_S(cs) | 19462306a36Sopenharmony_ci EBU_CON_LAT_EN_CS_P, nand_con); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int ebu_nand_set_timings(struct nand_chip *chip, int csline, 19862306a36Sopenharmony_ci const struct nand_interface_config *conf) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct ebu_nand_controller *ctrl = nand_to_ebu(chip); 20162306a36Sopenharmony_ci unsigned int rate = clk_get_rate(ctrl->clk) / HZ_PER_MHZ; 20262306a36Sopenharmony_ci unsigned int period = DIV_ROUND_UP(USEC_PER_SEC, rate); 20362306a36Sopenharmony_ci const struct nand_sdr_timings *timings; 20462306a36Sopenharmony_ci u32 trecov, thold, twrwait, trdwait; 20562306a36Sopenharmony_ci u32 reg = 0; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci timings = nand_get_sdr_timings(conf); 20862306a36Sopenharmony_ci if (IS_ERR(timings)) 20962306a36Sopenharmony_ci return PTR_ERR(timings); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (csline == NAND_DATA_IFACE_CHECK_ONLY) 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci trecov = DIV_ROUND_UP(max(timings->tREA_max, timings->tREH_min), 21562306a36Sopenharmony_ci period); 21662306a36Sopenharmony_ci reg |= EBU_BUSCON_RECOVC(trecov); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci thold = DIV_ROUND_UP(max(timings->tDH_min, timings->tDS_min), period); 21962306a36Sopenharmony_ci reg |= EBU_BUSCON_HOLDC(thold); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci trdwait = DIV_ROUND_UP(max(timings->tRC_min, timings->tREH_min), 22262306a36Sopenharmony_ci period); 22362306a36Sopenharmony_ci reg |= EBU_BUSCON_WAITRDC(trdwait); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci twrwait = DIV_ROUND_UP(max(timings->tWC_min, timings->tWH_min), period); 22662306a36Sopenharmony_ci reg |= EBU_BUSCON_WAITWRC(twrwait); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci reg |= EBU_BUSCON_CMULT_V4 | EBU_BUSCON_BCGEN_CS | EBU_BUSCON_ALEC | 22962306a36Sopenharmony_ci EBU_BUSCON_SETUP_EN; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci writel(reg, ctrl->ebu + EBU_BUSCON(ctrl->cs_num)); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int ebu_nand_ooblayout_ecc(struct mtd_info *mtd, int section, 23762306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (section) 24262306a36Sopenharmony_ci return -ERANGE; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci oobregion->offset = HSNAND_ECC_OFFSET; 24562306a36Sopenharmony_ci oobregion->length = chip->ecc.total; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int ebu_nand_ooblayout_free(struct mtd_info *mtd, int section, 25162306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct nand_chip *chip = mtd_to_nand(mtd); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (section) 25662306a36Sopenharmony_ci return -ERANGE; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci oobregion->offset = chip->ecc.total + HSNAND_ECC_OFFSET; 25962306a36Sopenharmony_ci oobregion->length = mtd->oobsize - oobregion->offset; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops ebu_nand_ooblayout_ops = { 26562306a36Sopenharmony_ci .ecc = ebu_nand_ooblayout_ecc, 26662306a36Sopenharmony_ci .free = ebu_nand_ooblayout_free, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void ebu_dma_rx_callback(void *cookie) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = cookie; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci dmaengine_terminate_async(ebu_host->dma_rx); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci complete(&ebu_host->dma_access_complete); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic void ebu_dma_tx_callback(void *cookie) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = cookie; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci dmaengine_terminate_async(ebu_host->dma_tx); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci complete(&ebu_host->dma_access_complete); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir, 28862306a36Sopenharmony_ci const u8 *buf, u32 len) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 29162306a36Sopenharmony_ci struct completion *dma_completion; 29262306a36Sopenharmony_ci dma_async_tx_callback callback; 29362306a36Sopenharmony_ci struct dma_chan *chan; 29462306a36Sopenharmony_ci dma_cookie_t cookie; 29562306a36Sopenharmony_ci unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; 29662306a36Sopenharmony_ci dma_addr_t buf_dma; 29762306a36Sopenharmony_ci int ret; 29862306a36Sopenharmony_ci u32 timeout; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 30162306a36Sopenharmony_ci chan = ebu_host->dma_rx; 30262306a36Sopenharmony_ci dma_completion = &ebu_host->dma_access_complete; 30362306a36Sopenharmony_ci callback = ebu_dma_rx_callback; 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci chan = ebu_host->dma_tx; 30662306a36Sopenharmony_ci dma_completion = &ebu_host->dma_access_complete; 30762306a36Sopenharmony_ci callback = ebu_dma_tx_callback; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir); 31162306a36Sopenharmony_ci if (dma_mapping_error(chan->device->dev, buf_dma)) { 31262306a36Sopenharmony_ci dev_err(ebu_host->dev, "Failed to map DMA buffer\n"); 31362306a36Sopenharmony_ci ret = -EIO; 31462306a36Sopenharmony_ci goto err_unmap; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci tx = dmaengine_prep_slave_single(chan, buf_dma, len, dir, flags); 31862306a36Sopenharmony_ci if (!tx) { 31962306a36Sopenharmony_ci ret = -ENXIO; 32062306a36Sopenharmony_ci goto err_unmap; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci tx->callback = callback; 32462306a36Sopenharmony_ci tx->callback_param = ebu_host; 32562306a36Sopenharmony_ci cookie = tx->tx_submit(tx); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = dma_submit_error(cookie); 32862306a36Sopenharmony_ci if (ret) { 32962306a36Sopenharmony_ci dev_err(ebu_host->dev, "dma_submit_error %d\n", cookie); 33062306a36Sopenharmony_ci ret = -EIO; 33162306a36Sopenharmony_ci goto err_unmap; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci init_completion(dma_completion); 33562306a36Sopenharmony_ci dma_async_issue_pending(chan); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Wait DMA to finish the data transfer.*/ 33862306a36Sopenharmony_ci timeout = wait_for_completion_timeout(dma_completion, msecs_to_jiffies(1000)); 33962306a36Sopenharmony_ci if (!timeout) { 34062306a36Sopenharmony_ci dev_err(ebu_host->dev, "I/O Error in DMA RX (status %d)\n", 34162306a36Sopenharmony_ci dmaengine_tx_status(chan, cookie, NULL)); 34262306a36Sopenharmony_ci dmaengine_terminate_sync(chan); 34362306a36Sopenharmony_ci ret = -ETIMEDOUT; 34462306a36Sopenharmony_ci goto err_unmap; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cierr_unmap: 35062306a36Sopenharmony_ci dma_unmap_single(ebu_host->dev, buf_dma, len, dir); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return ret; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void ebu_nand_trigger(struct ebu_nand_controller *ebu_host, 35662306a36Sopenharmony_ci int page, u32 cmd) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned int val; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci val = cmd | (page & 0xFF) << HSNAND_CTL1_ADDR_SHIFT; 36162306a36Sopenharmony_ci writel(val, ebu_host->hsnand + HSNAND_CTL1); 36262306a36Sopenharmony_ci val = (page & 0xFFFF00) >> 8 | HSNAND_CTL2_CYC_N_V5; 36362306a36Sopenharmony_ci writel(val, ebu_host->hsnand + HSNAND_CTL2); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci writel(ebu_host->nd_para0, ebu_host->hsnand + HSNAND_PARA0); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* clear first, will update later */ 36862306a36Sopenharmony_ci writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_0); 36962306a36Sopenharmony_ci writel(0xFFFFFFFF, ebu_host->hsnand + HSNAND_CMSG_1); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci writel(HSNAND_INT_MSK_CTL_WR_C, 37262306a36Sopenharmony_ci ebu_host->hsnand + HSNAND_INT_MSK_CTL); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!cmd) 37562306a36Sopenharmony_ci val = HSNAND_CTL_RW_READ; 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci val = HSNAND_CTL_RW_WRITE; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci writel(HSNAND_CTL_MSG_EN | HSNAND_CTL_CKFF_EN | 38062306a36Sopenharmony_ci HSNAND_CTL_ECC_OFF_V8TH | HSNAND_CTL_CE_SEL_CS(ebu_host->cs_num) | 38162306a36Sopenharmony_ci HSNAND_CTL_ENABLE_ECC | HSNAND_CTL_GO | val, 38262306a36Sopenharmony_ci ebu_host->hsnand + HSNAND_CTL); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic int ebu_nand_read_page_hwecc(struct nand_chip *chip, u8 *buf, 38662306a36Sopenharmony_ci int oob_required, int page) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 38962306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 39062306a36Sopenharmony_ci int ret, reg_data; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci ebu_nand_trigger(ebu_host, page, NAND_CMD_READ0); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ret = ebu_dma_start(ebu_host, DMA_DEV_TO_MEM, buf, mtd->writesize); 39562306a36Sopenharmony_ci if (ret) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (oob_required) 39962306a36Sopenharmony_ci chip->ecc.read_oob(chip, page); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci reg_data = readl(ebu_host->hsnand + HSNAND_CTL); 40262306a36Sopenharmony_ci reg_data &= ~HSNAND_CTL_GO; 40362306a36Sopenharmony_ci writel(reg_data, ebu_host->hsnand + HSNAND_CTL); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int ebu_nand_write_page_hwecc(struct nand_chip *chip, const u8 *buf, 40962306a36Sopenharmony_ci int oob_required, int page) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 41262306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 41362306a36Sopenharmony_ci void __iomem *int_sta = ebu_host->hsnand + HSNAND_INT_STA; 41462306a36Sopenharmony_ci int reg_data, ret, val; 41562306a36Sopenharmony_ci u32 reg; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ebu_nand_trigger(ebu_host, page, NAND_CMD_SEQIN); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret = ebu_dma_start(ebu_host, DMA_MEM_TO_DEV, buf, mtd->writesize); 42062306a36Sopenharmony_ci if (ret) 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (oob_required) { 42462306a36Sopenharmony_ci reg = get_unaligned_le32(chip->oob_poi); 42562306a36Sopenharmony_ci writel(reg, ebu_host->hsnand + HSNAND_CMSG_0); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci reg = get_unaligned_le32(chip->oob_poi + 4); 42862306a36Sopenharmony_ci writel(reg, ebu_host->hsnand + HSNAND_CMSG_1); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(int_sta, val, !(val & HSNAND_INT_STA_WR_C), 43262306a36Sopenharmony_ci 10, 1000); 43362306a36Sopenharmony_ci if (ret) 43462306a36Sopenharmony_ci return ret; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci reg_data = readl(ebu_host->hsnand + HSNAND_CTL); 43762306a36Sopenharmony_ci reg_data &= ~HSNAND_CTL_GO; 43862306a36Sopenharmony_ci writel(reg_data, ebu_host->hsnand + HSNAND_CTL); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic const u8 ecc_strength[] = { 1, 1, 4, 8, 24, 32, 40, 60, }; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic int ebu_nand_attach_chip(struct nand_chip *chip) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct mtd_info *mtd = nand_to_mtd(chip); 44862306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = nand_get_controller_data(chip); 44962306a36Sopenharmony_ci u32 ecc_steps, ecc_bytes, ecc_total, pagesize, pg_per_blk; 45062306a36Sopenharmony_ci u32 ecc_strength_ds = chip->ecc.strength; 45162306a36Sopenharmony_ci u32 ecc_size = chip->ecc.size; 45262306a36Sopenharmony_ci u32 writesize = mtd->writesize; 45362306a36Sopenharmony_ci u32 blocksize = mtd->erasesize; 45462306a36Sopenharmony_ci int bch_algo, start, val; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Default to an ECC size of 512 */ 45762306a36Sopenharmony_ci if (!chip->ecc.size) 45862306a36Sopenharmony_ci chip->ecc.size = 512; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci switch (ecc_size) { 46162306a36Sopenharmony_ci case 512: 46262306a36Sopenharmony_ci start = 1; 46362306a36Sopenharmony_ci if (!ecc_strength_ds) 46462306a36Sopenharmony_ci ecc_strength_ds = 4; 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci case 1024: 46762306a36Sopenharmony_ci start = 4; 46862306a36Sopenharmony_ci if (!ecc_strength_ds) 46962306a36Sopenharmony_ci ecc_strength_ds = 32; 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* BCH ECC algorithm Settings for number of bits per 512B/1024B */ 47662306a36Sopenharmony_ci bch_algo = round_up(start + 1, 4); 47762306a36Sopenharmony_ci for (val = start; val < bch_algo; val++) { 47862306a36Sopenharmony_ci if (ecc_strength_ds == ecc_strength[val]) 47962306a36Sopenharmony_ci break; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci if (val == bch_algo) 48262306a36Sopenharmony_ci return -EINVAL; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (ecc_strength_ds == 8) 48562306a36Sopenharmony_ci ecc_bytes = 14; 48662306a36Sopenharmony_ci else 48762306a36Sopenharmony_ci ecc_bytes = DIV_ROUND_UP(ecc_strength_ds * fls(8 * ecc_size), 8); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ecc_steps = writesize / ecc_size; 49062306a36Sopenharmony_ci ecc_total = ecc_steps * ecc_bytes; 49162306a36Sopenharmony_ci if ((ecc_total + 8) > mtd->oobsize) 49262306a36Sopenharmony_ci return -ERANGE; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci chip->ecc.total = ecc_total; 49562306a36Sopenharmony_ci pagesize = fls(writesize >> 11); 49662306a36Sopenharmony_ci if (pagesize > HSNAND_PARA0_PAGE_V8192) 49762306a36Sopenharmony_ci return -ERANGE; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci pg_per_blk = fls((blocksize / writesize) >> 6) / 8; 50062306a36Sopenharmony_ci if (pg_per_blk > HSNAND_PARA0_PIB_V256) 50162306a36Sopenharmony_ci return -ERANGE; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ebu_host->nd_para0 = pagesize | pg_per_blk | HSNAND_PARA0_BYP_EN_NP | 50462306a36Sopenharmony_ci HSNAND_PARA0_BYP_DEC_NP | HSNAND_PARA0_ADEP_EN | 50562306a36Sopenharmony_ci HSNAND_PARA0_TYPE_ONFI | (val << 29); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &ebu_nand_ooblayout_ops); 50862306a36Sopenharmony_ci chip->ecc.read_page = ebu_nand_read_page_hwecc; 50962306a36Sopenharmony_ci chip->ecc.write_page = ebu_nand_write_page_hwecc; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int ebu_nand_exec_op(struct nand_chip *chip, 51562306a36Sopenharmony_ci const struct nand_operation *op, bool check_only) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci const struct nand_op_instr *instr = NULL; 51862306a36Sopenharmony_ci unsigned int op_id; 51962306a36Sopenharmony_ci int i, timeout_ms, ret = 0; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (check_only) 52262306a36Sopenharmony_ci return 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci ebu_select_chip(chip); 52562306a36Sopenharmony_ci for (op_id = 0; op_id < op->ninstrs; op_id++) { 52662306a36Sopenharmony_ci instr = &op->instrs[op_id]; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci switch (instr->type) { 52962306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 53062306a36Sopenharmony_ci ebu_nand_writeb(chip, HSNAND_CLE_OFFS | HSNAND_CS_OFFS, 53162306a36Sopenharmony_ci instr->ctx.cmd.opcode); 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 53562306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) 53662306a36Sopenharmony_ci ebu_nand_writeb(chip, 53762306a36Sopenharmony_ci HSNAND_ALE_OFFS | HSNAND_CS_OFFS, 53862306a36Sopenharmony_ci instr->ctx.addr.addrs[i]); 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 54262306a36Sopenharmony_ci ebu_read_buf(chip, instr->ctx.data.buf.in, 54362306a36Sopenharmony_ci instr->ctx.data.len); 54462306a36Sopenharmony_ci break; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 54762306a36Sopenharmony_ci ebu_write_buf(chip, instr->ctx.data.buf.out, 54862306a36Sopenharmony_ci instr->ctx.data.len); 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 55262306a36Sopenharmony_ci timeout_ms = instr->ctx.waitrdy.timeout_ms * 1000; 55362306a36Sopenharmony_ci ret = ebu_nand_waitrdy(chip, timeout_ms); 55462306a36Sopenharmony_ci break; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci return ret; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic const struct nand_controller_ops ebu_nand_controller_ops = { 56262306a36Sopenharmony_ci .attach_chip = ebu_nand_attach_chip, 56362306a36Sopenharmony_ci .setup_interface = ebu_nand_set_timings, 56462306a36Sopenharmony_ci .exec_op = ebu_nand_exec_op, 56562306a36Sopenharmony_ci}; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic void ebu_dma_cleanup(struct ebu_nand_controller *ebu_host) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci if (ebu_host->dma_rx) 57062306a36Sopenharmony_ci dma_release_channel(ebu_host->dma_rx); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (ebu_host->dma_tx) 57362306a36Sopenharmony_ci dma_release_channel(ebu_host->dma_tx); 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic int ebu_nand_probe(struct platform_device *pdev) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 57962306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host; 58062306a36Sopenharmony_ci struct device_node *chip_np; 58162306a36Sopenharmony_ci struct nand_chip *nand; 58262306a36Sopenharmony_ci struct mtd_info *mtd; 58362306a36Sopenharmony_ci struct resource *res; 58462306a36Sopenharmony_ci char *resname; 58562306a36Sopenharmony_ci int ret; 58662306a36Sopenharmony_ci u32 cs; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ebu_host = devm_kzalloc(dev, sizeof(*ebu_host), GFP_KERNEL); 58962306a36Sopenharmony_ci if (!ebu_host) 59062306a36Sopenharmony_ci return -ENOMEM; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ebu_host->dev = dev; 59362306a36Sopenharmony_ci nand_controller_init(&ebu_host->controller); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci ebu_host->ebu = devm_platform_ioremap_resource_byname(pdev, "ebunand"); 59662306a36Sopenharmony_ci if (IS_ERR(ebu_host->ebu)) 59762306a36Sopenharmony_ci return PTR_ERR(ebu_host->ebu); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci ebu_host->hsnand = devm_platform_ioremap_resource_byname(pdev, "hsnand"); 60062306a36Sopenharmony_ci if (IS_ERR(ebu_host->hsnand)) 60162306a36Sopenharmony_ci return PTR_ERR(ebu_host->hsnand); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci chip_np = of_get_next_child(dev->of_node, NULL); 60462306a36Sopenharmony_ci if (!chip_np) 60562306a36Sopenharmony_ci return dev_err_probe(dev, -EINVAL, 60662306a36Sopenharmony_ci "Could not find child node for the NAND chip\n"); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci ret = of_property_read_u32(chip_np, "reg", &cs); 60962306a36Sopenharmony_ci if (ret) { 61062306a36Sopenharmony_ci dev_err(dev, "failed to get chip select: %d\n", ret); 61162306a36Sopenharmony_ci goto err_of_node_put; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci if (cs >= MAX_CS) { 61462306a36Sopenharmony_ci dev_err(dev, "got invalid chip select: %d\n", cs); 61562306a36Sopenharmony_ci ret = -EINVAL; 61662306a36Sopenharmony_ci goto err_of_node_put; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci ebu_host->cs_num = cs; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci resname = devm_kasprintf(dev, GFP_KERNEL, "nand_cs%d", cs); 62262306a36Sopenharmony_ci if (!resname) { 62362306a36Sopenharmony_ci ret = -ENOMEM; 62462306a36Sopenharmony_ci goto err_of_node_put; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ebu_host->cs[cs].chipaddr = devm_platform_ioremap_resource_byname(pdev, 62862306a36Sopenharmony_ci resname); 62962306a36Sopenharmony_ci if (IS_ERR(ebu_host->cs[cs].chipaddr)) { 63062306a36Sopenharmony_ci ret = PTR_ERR(ebu_host->cs[cs].chipaddr); 63162306a36Sopenharmony_ci goto err_of_node_put; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ebu_host->clk = devm_clk_get_enabled(dev, NULL); 63562306a36Sopenharmony_ci if (IS_ERR(ebu_host->clk)) { 63662306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(ebu_host->clk), 63762306a36Sopenharmony_ci "failed to get and enable clock\n"); 63862306a36Sopenharmony_ci goto err_of_node_put; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci ebu_host->dma_tx = dma_request_chan(dev, "tx"); 64262306a36Sopenharmony_ci if (IS_ERR(ebu_host->dma_tx)) { 64362306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(ebu_host->dma_tx), 64462306a36Sopenharmony_ci "failed to request DMA tx chan!.\n"); 64562306a36Sopenharmony_ci goto err_of_node_put; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ebu_host->dma_rx = dma_request_chan(dev, "rx"); 64962306a36Sopenharmony_ci if (IS_ERR(ebu_host->dma_rx)) { 65062306a36Sopenharmony_ci ret = dev_err_probe(dev, PTR_ERR(ebu_host->dma_rx), 65162306a36Sopenharmony_ci "failed to request DMA rx chan!.\n"); 65262306a36Sopenharmony_ci ebu_host->dma_rx = NULL; 65362306a36Sopenharmony_ci goto err_cleanup_dma; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci resname = devm_kasprintf(dev, GFP_KERNEL, "addr_sel%d", cs); 65762306a36Sopenharmony_ci if (!resname) { 65862306a36Sopenharmony_ci ret = -ENOMEM; 65962306a36Sopenharmony_ci goto err_cleanup_dma; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, resname); 66362306a36Sopenharmony_ci if (!res) { 66462306a36Sopenharmony_ci ret = -EINVAL; 66562306a36Sopenharmony_ci goto err_cleanup_dma; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci ebu_host->cs[cs].addr_sel = res->start; 66862306a36Sopenharmony_ci writel(ebu_host->cs[cs].addr_sel | EBU_ADDR_MASK(5) | EBU_ADDR_SEL_REGEN, 66962306a36Sopenharmony_ci ebu_host->ebu + EBU_ADDR_SEL(cs)); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci nand_set_flash_node(&ebu_host->chip, chip_np); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci mtd = nand_to_mtd(&ebu_host->chip); 67462306a36Sopenharmony_ci if (!mtd->name) { 67562306a36Sopenharmony_ci dev_err(ebu_host->dev, "NAND label property is mandatory\n"); 67662306a36Sopenharmony_ci ret = -EINVAL; 67762306a36Sopenharmony_ci goto err_cleanup_dma; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci mtd->dev.parent = dev; 68162306a36Sopenharmony_ci ebu_host->dev = dev; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci platform_set_drvdata(pdev, ebu_host); 68462306a36Sopenharmony_ci nand_set_controller_data(&ebu_host->chip, ebu_host); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci nand = &ebu_host->chip; 68762306a36Sopenharmony_ci nand->controller = &ebu_host->controller; 68862306a36Sopenharmony_ci nand->controller->ops = &ebu_nand_controller_ops; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* Scan to find existence of the device */ 69162306a36Sopenharmony_ci ret = nand_scan(&ebu_host->chip, 1); 69262306a36Sopenharmony_ci if (ret) 69362306a36Sopenharmony_ci goto err_cleanup_dma; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci ret = mtd_device_register(mtd, NULL, 0); 69662306a36Sopenharmony_ci if (ret) 69762306a36Sopenharmony_ci goto err_clean_nand; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return 0; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cierr_clean_nand: 70262306a36Sopenharmony_ci nand_cleanup(&ebu_host->chip); 70362306a36Sopenharmony_cierr_cleanup_dma: 70462306a36Sopenharmony_ci ebu_dma_cleanup(ebu_host); 70562306a36Sopenharmony_cierr_of_node_put: 70662306a36Sopenharmony_ci of_node_put(chip_np); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return ret; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void ebu_nand_remove(struct platform_device *pdev) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct ebu_nand_controller *ebu_host = platform_get_drvdata(pdev); 71462306a36Sopenharmony_ci int ret; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ret = mtd_device_unregister(nand_to_mtd(&ebu_host->chip)); 71762306a36Sopenharmony_ci WARN_ON(ret); 71862306a36Sopenharmony_ci nand_cleanup(&ebu_host->chip); 71962306a36Sopenharmony_ci ebu_nand_disable(&ebu_host->chip); 72062306a36Sopenharmony_ci ebu_dma_cleanup(ebu_host); 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic const struct of_device_id ebu_nand_match[] = { 72462306a36Sopenharmony_ci { .compatible = "intel,lgm-ebunand" }, 72562306a36Sopenharmony_ci {} 72662306a36Sopenharmony_ci}; 72762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ebu_nand_match); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic struct platform_driver ebu_nand_driver = { 73062306a36Sopenharmony_ci .probe = ebu_nand_probe, 73162306a36Sopenharmony_ci .remove_new = ebu_nand_remove, 73262306a36Sopenharmony_ci .driver = { 73362306a36Sopenharmony_ci .name = "intel-nand-controller", 73462306a36Sopenharmony_ci .of_match_table = ebu_nand_match, 73562306a36Sopenharmony_ci }, 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci}; 73862306a36Sopenharmony_cimodule_platform_driver(ebu_nand_driver); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 74162306a36Sopenharmony_ciMODULE_AUTHOR("Vadivel Murugan R <vadivel.muruganx.ramuthevar@intel.com>"); 74262306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel's LGM External Bus NAND Controller driver"); 743