aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch
diff options
context:
space:
mode:
authorDaniel Golle <daniel@makrotopia.org>2021-10-01 23:24:06 +0100
committerDaniel Golle <daniel@makrotopia.org>2021-10-01 23:28:24 +0100
commit6b434d3c44aced4c210b042a8791f8d14c407a49 (patch)
tree9a188b5090f3aac6d1fda3695c5e4f69508b9dc3 /target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch
parent87623958b2796f69dcb7677e30ba5911b208f59a (diff)
downloadupstream-6b434d3c44aced4c210b042a8791f8d14c407a49.tar.gz
upstream-6b434d3c44aced4c210b042a8791f8d14c407a49.tar.bz2
upstream-6b434d3c44aced4c210b042a8791f8d14c407a49.zip
mediatek: remove files and patches for Linux 5.4
All subtargets only provide files and patches for Linux 5.10 by now so there is little use for the old Linux 5.4 stuff. Remove it. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Diffstat (limited to 'target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch')
-rw-r--r--target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch1246
1 files changed, 0 insertions, 1246 deletions
diff --git a/target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch b/target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch
deleted file mode 100644
index b3672e5b15..0000000000
--- a/target/linux/mediatek/patches-5.4/0306-spi-spi-mem-MediaTek-Add-SPI-NAND-Flash-interface-dr.patch
+++ /dev/null
@@ -1,1246 +0,0 @@
-From 1ecb38eabd90efe93957d0a822a167560c39308a Mon Sep 17 00:00:00 2001
-From: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
-Date: Wed, 20 Mar 2019 16:19:51 +0800
-Subject: [PATCH 6/6] spi: spi-mem: MediaTek: Add SPI NAND Flash interface
- driver for MediaTek MT7622
-
-Change-Id: I3e78406bb9b46b0049d3988a5c71c7069e4f809c
-Signed-off-by: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
----
- drivers/spi/Kconfig | 9 +
- drivers/spi/Makefile | 1 +
- drivers/spi/spi-mtk-snfi.c | 1183 ++++++++++++++++++++++++++++++++++++
- 3 files changed, 1193 insertions(+)
- create mode 100644 drivers/spi/spi-mtk-snfi.c
-
---- a/drivers/spi/Makefile
-+++ b/drivers/spi/Makefile
-@@ -60,6 +60,7 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mp
- obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
- obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
- obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o
-+obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o
- obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o
- obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o
- obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
---- a/drivers/spi/Kconfig
-+++ b/drivers/spi/Kconfig
-@@ -427,6 +427,15 @@ config SPI_MT65XX
- say Y or M here.If you are not sure, say N.
- SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs.
-
-+config SPI_MTK_SNFI
-+ tristate "MediaTek SPI NAND interface"
-+ select MTD_SPI_NAND
-+ help
-+ This selects the SPI NAND FLASH interface(SNFI),
-+ which could be found on MediaTek Soc.
-+ Say Y or M here.If you are not sure, say N.
-+ Note Parallel Nand and SPI NAND is alternative on MediaTek SoCs.
-+
- config SPI_MT7621
- tristate "MediaTek MT7621 SPI Controller"
- depends on RALINK || COMPILE_TEST
---- /dev/null
-+++ b/drivers/spi/spi-mtk-snfi.c
-@@ -0,0 +1,1200 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Driver for MediaTek SPI Nand interface
-+ *
-+ * Copyright (C) 2018 MediaTek Inc.
-+ * Authors: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
-+ *
-+ */
-+
-+#include <linux/clk.h>
-+#include <linux/delay.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/interrupt.h>
-+#include <linux/iopoll.h>
-+#include <linux/mtd/mtd.h>
-+#include <linux/mtd/mtk_ecc.h>
-+#include <linux/mtd/spinand.h>
-+#include <linux/module.h>
-+#include <linux/of.h>
-+#include <linux/of_device.h>
-+#include <linux/platform_device.h>
-+#include <linux/spi/spi.h>
-+#include <linux/spi/spi-mem.h>
-+
-+/* NAND controller register definition */
-+/* NFI control */
-+#define NFI_CNFG 0x00
-+#define CNFG_DMA BIT(0)
-+#define CNFG_READ_EN BIT(1)
-+#define CNFG_DMA_BURST_EN BIT(2)
-+#define CNFG_BYTE_RW BIT(6)
-+#define CNFG_HW_ECC_EN BIT(8)
-+#define CNFG_AUTO_FMT_EN BIT(9)
-+#define CNFG_OP_PROGRAM (3UL << 12)
-+#define CNFG_OP_CUST (6UL << 12)
-+#define NFI_PAGEFMT 0x04
-+#define PAGEFMT_512 0
-+#define PAGEFMT_2K 1
-+#define PAGEFMT_4K 2
-+#define PAGEFMT_FDM_SHIFT 8
-+#define PAGEFMT_FDM_ECC_SHIFT 12
-+#define NFI_CON 0x08
-+#define CON_FIFO_FLUSH BIT(0)
-+#define CON_NFI_RST BIT(1)
-+#define CON_BRD BIT(8)
-+#define CON_BWR BIT(9)
-+#define CON_SEC_SHIFT 12
-+#define NFI_INTR_EN 0x10
-+#define INTR_AHB_DONE_EN BIT(6)
-+#define NFI_INTR_STA 0x14
-+#define NFI_CMD 0x20
-+#define NFI_STA 0x60
-+#define STA_EMP_PAGE BIT(12)
-+#define NAND_FSM_MASK (0x1f << 24)
-+#define NFI_FSM_MASK (0xf << 16)
-+#define NFI_ADDRCNTR 0x70
-+#define CNTR_MASK GENMASK(16, 12)
-+#define ADDRCNTR_SEC_SHIFT 12
-+#define ADDRCNTR_SEC(val) \
-+ (((val) & CNTR_MASK) >> ADDRCNTR_SEC_SHIFT)
-+#define NFI_STRADDR 0x80
-+#define NFI_BYTELEN 0x84
-+#define NFI_CSEL 0x90
-+#define NFI_FDML(x) (0xa0 + (x) * sizeof(u32) * 2)
-+#define NFI_FDMM(x) (0xa4 + (x) * sizeof(u32) * 2)
-+#define NFI_MASTER_STA 0x224
-+#define MASTER_STA_MASK 0x0fff
-+/* NFI_SPI control */
-+#define SNFI_MAC_OUTL 0x504
-+#define SNFI_MAC_INL 0x508
-+#define SNFI_RD_CTL2 0x510
-+#define RD_CMD_MASK 0x00ff
-+#define RD_DUMMY_SHIFT 8
-+#define SNFI_RD_CTL3 0x514
-+#define RD_ADDR_MASK 0xffff
-+#define SNFI_MISC_CTL 0x538
-+#define RD_MODE_X2 BIT(16)
-+#define RD_MODE_X4 (2UL << 16)
-+#define RD_QDUAL_IO (4UL << 16)
-+#define RD_MODE_MASK (7UL << 16)
-+#define RD_CUSTOM_EN BIT(6)
-+#define WR_CUSTOM_EN BIT(7)
-+#define WR_X4_EN BIT(20)
-+#define SW_RST BIT(28)
-+#define SNFI_MISC_CTL2 0x53c
-+#define WR_LEN_SHIFT 16
-+#define SNFI_PG_CTL1 0x524
-+#define WR_LOAD_CMD_SHIFT 8
-+#define SNFI_PG_CTL2 0x528
-+#define WR_LOAD_ADDR_MASK 0xffff
-+#define SNFI_MAC_CTL 0x500
-+#define MAC_WIP BIT(0)
-+#define MAC_WIP_READY BIT(1)
-+#define MAC_TRIG BIT(2)
-+#define MAC_EN BIT(3)
-+#define MAC_SIO_SEL BIT(4)
-+#define SNFI_STA_CTL1 0x550
-+#define SPI_STATE_IDLE 0xf
-+#define SNFI_CNFG 0x55c
-+#define SNFI_MODE_EN BIT(0)
-+#define SNFI_GPRAM_DATA 0x800
-+#define SNFI_GPRAM_MAX_LEN 16
-+
-+/* Dummy command trigger NFI to spi mode */
-+#define NAND_CMD_DUMMYREAD 0x00
-+#define NAND_CMD_DUMMYPROG 0x80
-+
-+#define MTK_TIMEOUT 500000
-+#define MTK_RESET_TIMEOUT 1000000
-+#define MTK_SNFC_MIN_SPARE 16
-+#define KB(x) ((x) * 1024UL)
-+
-+/*
-+ * supported spare size of each IP.
-+ * order should be the same with the spare size bitfiled defination of
-+ * register NFI_PAGEFMT.
-+ */
-+static const u8 spare_size_mt7622[] = {
-+ 16, 26, 27, 28
-+};
-+
-+struct mtk_snfi_caps {
-+ const u8 *spare_size;
-+ u8 num_spare_size;
-+ u32 nand_sec_size;
-+ u8 nand_fdm_size;
-+ u8 nand_fdm_ecc_size;
-+ u8 ecc_parity_bits;
-+ u8 pageformat_spare_shift;
-+ u8 bad_mark_swap;
-+};
-+
-+struct mtk_snfi_bad_mark_ctl {
-+ void (*bm_swap)(struct spi_mem *mem, u8 *buf, int raw);
-+ u32 sec;
-+ u32 pos;
-+};
-+
-+struct mtk_snfi_nand_chip {
-+ struct mtk_snfi_bad_mark_ctl bad_mark;
-+ u32 spare_per_sector;
-+};
-+
-+struct mtk_snfi_clk {
-+ struct clk *nfi_clk;
-+ struct clk *spi_clk;
-+};
-+
-+struct mtk_snfi {
-+ const struct mtk_snfi_caps *caps;
-+ struct mtk_snfi_nand_chip snfi_nand;
-+ struct mtk_snfi_clk clk;
-+ struct mtk_ecc_config ecc_cfg;
-+ struct mtk_ecc *ecc;
-+ struct completion done;
-+ struct device *dev;
-+
-+ void __iomem *regs;
-+
-+ u8 *buffer;
-+};
-+
-+static inline u8 *oob_ptr(struct spi_mem *mem, int i)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u8 *poi;
-+
-+ /* map the sector's FDM data to free oob:
-+ * the beginning of the oob area stores the FDM data of bad mark
-+ */
-+
-+ if (i < snfi_nand->bad_mark.sec)
-+ poi = spinand->oobbuf + (i + 1) * snfi->caps->nand_fdm_size;
-+ else if (i == snfi_nand->bad_mark.sec)
-+ poi = spinand->oobbuf;
-+ else
-+ poi = spinand->oobbuf + i * snfi->caps->nand_fdm_size;
-+
-+ return poi;
-+}
-+
-+static inline int mtk_data_len(struct spi_mem *mem)
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+
-+ return snfi->caps->nand_sec_size + snfi_nand->spare_per_sector;
-+}
-+
-+static inline u8 *mtk_oob_ptr(struct spi_mem *mem,
-+ const u8 *p, int i)
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+
-+ return (u8 *)p + i * mtk_data_len(mem) + snfi->caps->nand_sec_size;
-+}
-+
-+static void mtk_snfi_bad_mark_swap(struct spi_mem *mem,
-+ u8 *buf, int raw)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u32 bad_pos = snfi_nand->bad_mark.pos;
-+
-+ if (raw)
-+ bad_pos += snfi_nand->bad_mark.sec * mtk_data_len(mem);
-+ else
-+ bad_pos += snfi_nand->bad_mark.sec * snfi->caps->nand_sec_size;
-+
-+ swap(spinand->oobbuf[0], buf[bad_pos]);
-+}
-+
-+static void mtk_snfi_set_bad_mark_ctl(struct mtk_snfi_bad_mark_ctl *bm_ctl,
-+ struct spi_mem *mem)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+
-+ bm_ctl->bm_swap = mtk_snfi_bad_mark_swap;
-+ bm_ctl->sec = mtd->writesize / mtk_data_len(mem);
-+ bm_ctl->pos = mtd->writesize % mtk_data_len(mem);
-+}
-+
-+static void mtk_snfi_mac_enable(struct mtk_snfi *snfi)
-+{
-+ u32 mac;
-+
-+ mac = readl(snfi->regs + SNFI_MAC_CTL);
-+ mac &= ~MAC_SIO_SEL;
-+ mac |= MAC_EN;
-+
-+ writel(mac, snfi->regs + SNFI_MAC_CTL);
-+}
-+
-+static int mtk_snfi_mac_trigger(struct mtk_snfi *snfi)
-+{
-+ u32 mac, reg;
-+ int ret = 0;
-+
-+ mac = readl(snfi->regs + SNFI_MAC_CTL);
-+ mac |= MAC_TRIG;
-+ writel(mac, snfi->regs + SNFI_MAC_CTL);
-+
-+ ret = readl_poll_timeout_atomic(snfi->regs + SNFI_MAC_CTL, reg,
-+ reg & MAC_WIP_READY, 10,
-+ MTK_TIMEOUT);
-+ if (ret < 0) {
-+ dev_err(snfi->dev, "polling wip ready for read timeout\n");
-+ return -EIO;
-+ }
-+
-+ ret = readl_poll_timeout_atomic(snfi->regs + SNFI_MAC_CTL, reg,
-+ !(reg & MAC_WIP), 10,
-+ MTK_TIMEOUT);
-+ if (ret < 0) {
-+ dev_err(snfi->dev, "polling flash update timeout\n");
-+ return -EIO;
-+ }
-+
-+ return ret;
-+}
-+
-+static void mtk_snfi_mac_leave(struct mtk_snfi *snfi)
-+{
-+ u32 mac;
-+
-+ mac = readl(snfi->regs + SNFI_MAC_CTL);
-+ mac &= ~(MAC_TRIG | MAC_EN | MAC_SIO_SEL);
-+ writel(mac, snfi->regs + SNFI_MAC_CTL);
-+}
-+
-+static int mtk_snfi_mac_op(struct mtk_snfi *snfi)
-+{
-+ int ret = 0;
-+
-+ mtk_snfi_mac_enable(snfi);
-+
-+ ret = mtk_snfi_mac_trigger(snfi);
-+ if (ret)
-+ return ret;
-+
-+ mtk_snfi_mac_leave(snfi);
-+
-+ return ret;
-+}
-+
-+static irqreturn_t mtk_snfi_irq(int irq, void *id)
-+{
-+ struct mtk_snfi *snfi = id;
-+ u16 sta, ien;
-+
-+ sta = readw(snfi->regs + NFI_INTR_STA);
-+ ien = readw(snfi->regs + NFI_INTR_EN);
-+
-+ if (!(sta & ien))
-+ return IRQ_NONE;
-+
-+ writew(~sta & ien, snfi->regs + NFI_INTR_EN);
-+ complete(&snfi->done);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static int mtk_snfi_enable_clk(struct device *dev, struct mtk_snfi_clk *clk)
-+{
-+ int ret;
-+
-+ ret = clk_prepare_enable(clk->nfi_clk);
-+ if (ret) {
-+ dev_err(dev, "failed to enable nfi clk\n");
-+ return ret;
-+ }
-+
-+ ret = clk_prepare_enable(clk->spi_clk);
-+ if (ret) {
-+ dev_err(dev, "failed to enable spi clk\n");
-+ clk_disable_unprepare(clk->nfi_clk);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static void mtk_snfi_disable_clk(struct mtk_snfi_clk *clk)
-+{
-+ clk_disable_unprepare(clk->nfi_clk);
-+ clk_disable_unprepare(clk->spi_clk);
-+}
-+
-+static int mtk_snfi_reset(struct mtk_snfi *snfi)
-+{
-+ u32 val;
-+ int ret;
-+
-+ /* SW reset controller */
-+ val = readl(snfi->regs + SNFI_MISC_CTL) | SW_RST;
-+ writel(val, snfi->regs + SNFI_MISC_CTL);
-+
-+ ret = readw_poll_timeout(snfi->regs + SNFI_STA_CTL1, val,
-+ !(val & SPI_STATE_IDLE), 50,
-+ MTK_RESET_TIMEOUT);
-+ if (ret) {
-+ dev_warn(snfi->dev, "spi state active in reset [0x%x] = 0x%x\n",
-+ SNFI_STA_CTL1, val);
-+ return ret;
-+ }
-+
-+ val = readl(snfi->regs + SNFI_MISC_CTL);
-+ val &= ~SW_RST;
-+ writel(val, snfi->regs + SNFI_MISC_CTL);
-+
-+ /* reset all registers and force the NFI master to terminate */
-+ writew(CON_FIFO_FLUSH | CON_NFI_RST, snfi->regs + NFI_CON);
-+ ret = readw_poll_timeout(snfi->regs + NFI_STA, val,
-+ !(val & (NFI_FSM_MASK | NAND_FSM_MASK)), 50,
-+ MTK_RESET_TIMEOUT);
-+ if (ret) {
-+ dev_warn(snfi->dev, "nfi active in reset [0x%x] = 0x%x\n",
-+ NFI_STA, val);
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static int mtk_snfi_set_spare_per_sector(struct spinand_device *spinand,
-+ const struct mtk_snfi_caps *caps,
-+ u32 *sps)
-+{
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ const u8 *spare = caps->spare_size;
-+ u32 sectors, i, closest_spare = 0;
-+
-+ sectors = mtd->writesize / caps->nand_sec_size;
-+ *sps = mtd->oobsize / sectors;
-+
-+ if (*sps < MTK_SNFC_MIN_SPARE)
-+ return -EINVAL;
-+
-+ for (i = 0; i < caps->num_spare_size; i++) {
-+ if (*sps >= spare[i] && spare[i] >= spare[closest_spare]) {
-+ closest_spare = i;
-+ if (*sps == spare[i])
-+ break;
-+ }
-+ }
-+
-+ *sps = spare[closest_spare];
-+
-+ return 0;
-+}
-+
-+static void mtk_snfi_read_fdm_data(struct spi_mem *mem,
-+ u32 sectors)
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ const struct mtk_snfi_caps *caps = snfi->caps;
-+ u32 vall, valm;
-+ int i, j;
-+ u8 *oobptr;
-+
-+ for (i = 0; i < sectors; i++) {
-+ oobptr = oob_ptr(mem, i);
-+ vall = readl(snfi->regs + NFI_FDML(i));
-+ valm = readl(snfi->regs + NFI_FDMM(i));
-+
-+ for (j = 0; j < caps->nand_fdm_size; j++)
-+ oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8);
-+ }
-+}
-+
-+static void mtk_snfi_write_fdm_data(struct spi_mem *mem,
-+ u32 sectors)
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ const struct mtk_snfi_caps *caps = snfi->caps;
-+ u32 vall, valm;
-+ int i, j;
-+ u8 *oobptr;
-+
-+ for (i = 0; i < sectors; i++) {
-+ oobptr = oob_ptr(mem, i);
-+ vall = 0;
-+ valm = 0;
-+ for (j = 0; j < 8; j++) {
-+ if (j < 4)
-+ vall |= (j < caps->nand_fdm_size ? oobptr[j] :
-+ 0xff) << (j * 8);
-+ else
-+ valm |= (j < caps->nand_fdm_size ? oobptr[j] :
-+ 0xff) << ((j - 4) * 8);
-+ }
-+ writel(vall, snfi->regs + NFI_FDML(i));
-+ writel(valm, snfi->regs + NFI_FDMM(i));
-+ }
-+}
-+
-+static int mtk_snfi_update_ecc_stats(struct spi_mem *mem,
-+ u8 *buf, u32 sectors)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct mtk_ecc_stats stats;
-+ int rc, i;
-+
-+ rc = readl(snfi->regs + NFI_STA) & STA_EMP_PAGE;
-+ if (rc) {
-+ memset(buf, 0xff, sectors * snfi->caps->nand_sec_size);
-+ for (i = 0; i < sectors; i++)
-+ memset(spinand->oobbuf, 0xff,
-+ snfi->caps->nand_fdm_size);
-+ return 0;
-+ }
-+
-+ mtk_ecc_get_stats(snfi->ecc, &stats, sectors);
-+ mtd->ecc_stats.corrected += stats.corrected;
-+ mtd->ecc_stats.failed += stats.failed;
-+
-+ return 0;
-+}
-+
-+static int mtk_snfi_hw_runtime_config(struct spi_mem *mem)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ struct nand_device *nand = mtd_to_nanddev(mtd);
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ const struct mtk_snfi_caps *caps = snfi->caps;
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u32 fmt, spare, i = 0;
-+ int ret;
-+
-+ ret = mtk_snfi_set_spare_per_sector(spinand, caps, &spare);
-+ if (ret)
-+ return ret;
-+
-+ /* calculate usable oob bytes for ecc parity data */
-+ snfi_nand->spare_per_sector = spare;
-+ spare -= caps->nand_fdm_size;
-+
-+ nand->memorg.oobsize = snfi_nand->spare_per_sector
-+ * (mtd->writesize / caps->nand_sec_size);
-+ mtd->oobsize = nanddev_per_page_oobsize(nand);
-+
-+ snfi->ecc_cfg.strength = (spare << 3) / caps->ecc_parity_bits;
-+ mtk_ecc_adjust_strength(snfi->ecc, &snfi->ecc_cfg.strength);
-+
-+ switch (mtd->writesize) {
-+ case 512:
-+ fmt = PAGEFMT_512;
-+ break;
-+ case KB(2):
-+ fmt = PAGEFMT_2K;
-+ break;
-+ case KB(4):
-+ fmt = PAGEFMT_4K;
-+ break;
-+ default:
-+ dev_err(snfi->dev, "invalid page len: %d\n", mtd->writesize);
-+ return -EINVAL;
-+ }
-+
-+ /* Setup PageFormat */
-+ while (caps->spare_size[i] != snfi_nand->spare_per_sector) {
-+ i++;
-+ if (i == (caps->num_spare_size - 1)) {
-+ dev_err(snfi->dev, "invalid spare size %d\n",
-+ snfi_nand->spare_per_sector);
-+ return -EINVAL;
-+ }
-+ }
-+
-+ fmt |= i << caps->pageformat_spare_shift;
-+ fmt |= caps->nand_fdm_size << PAGEFMT_FDM_SHIFT;
-+ fmt |= caps->nand_fdm_ecc_size << PAGEFMT_FDM_ECC_SHIFT;
-+ writel(fmt, snfi->regs + NFI_PAGEFMT);
-+
-+ snfi->ecc_cfg.len = caps->nand_sec_size + caps->nand_fdm_ecc_size;
-+
-+ mtk_snfi_set_bad_mark_ctl(&snfi_nand->bad_mark, mem);
-+
-+ return 0;
-+}
-+
-+static int mtk_snfi_read_from_cache(struct spi_mem *mem,
-+ const struct spi_mem_op *op, int oob_on)
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ u32 sectors = mtd->writesize / snfi->caps->nand_sec_size;
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u32 reg, len, col_addr = 0;
-+ int dummy_cycle, ret;
-+ dma_addr_t dma_addr;
-+
-+ len = sectors * (snfi->caps->nand_sec_size
-+ + snfi_nand->spare_per_sector);
-+
-+ dma_addr = dma_map_single(snfi->dev, snfi->buffer,
-+ len, DMA_FROM_DEVICE);
-+ ret = dma_mapping_error(snfi->dev, dma_addr);
-+ if (ret) {
-+ dev_err(snfi->dev, "dma mapping error\n");
-+ return -EINVAL;
-+ }
-+
-+ /* set Read cache command and dummy cycle */
-+ dummy_cycle = (op->dummy.nbytes << 3) >> (ffs(op->dummy.buswidth) - 1);
-+ reg = ((op->cmd.opcode & RD_CMD_MASK) |
-+ (dummy_cycle << RD_DUMMY_SHIFT));
-+ writel(reg, snfi->regs + SNFI_RD_CTL2);
-+
-+ writel((col_addr & RD_ADDR_MASK), snfi->regs + SNFI_RD_CTL3);
-+
-+ reg = readl(snfi->regs + SNFI_MISC_CTL);
-+ reg |= RD_CUSTOM_EN;
-+ reg &= ~(RD_MODE_MASK | WR_X4_EN);
-+
-+ /* set data and addr buswidth */
-+ if (op->data.buswidth == 4)
-+ reg |= RD_MODE_X4;
-+ else if (op->data.buswidth == 2)
-+ reg |= RD_MODE_X2;
-+
-+ if (op->addr.buswidth == 4 || op->addr.buswidth == 2)
-+ reg |= RD_QDUAL_IO;
-+ writel(reg, snfi->regs + SNFI_MISC_CTL);
-+
-+ writel(len, snfi->regs + SNFI_MISC_CTL2);
-+ writew(sectors << CON_SEC_SHIFT, snfi->regs + NFI_CON);
-+ reg = readw(snfi->regs + NFI_CNFG);
-+ reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_DMA | CNFG_OP_CUST;
-+
-+ if (!oob_on) {
-+ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
-+ writew(reg, snfi->regs + NFI_CNFG);
-+
-+ snfi->ecc_cfg.mode = ECC_NFI_MODE;
-+ snfi->ecc_cfg.sectors = sectors;
-+ snfi->ecc_cfg.op = ECC_DECODE;
-+ ret = mtk_ecc_enable(snfi->ecc, &snfi->ecc_cfg);
-+ if (ret) {
-+ dev_err(snfi->dev, "ecc enable failed\n");
-+ /* clear NFI_CNFG */
-+ reg &= ~(CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_DMA |
-+ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
-+ writew(reg, snfi->regs + NFI_CNFG);
-+ goto out;
-+ }
-+ } else {
-+ writew(reg, snfi->regs + NFI_CNFG);
-+ }
-+
-+ writel(lower_32_bits(dma_addr), snfi->regs + NFI_STRADDR);
-+ readw(snfi->regs + NFI_INTR_STA);
-+ writew(INTR_AHB_DONE_EN, snfi->regs + NFI_INTR_EN);
-+
-+ init_completion(&snfi->done);
-+
-+ /* set dummy command to trigger NFI enter SPI mode */
-+ writew(NAND_CMD_DUMMYREAD, snfi->regs + NFI_CMD);
-+ reg = readl(snfi->regs + NFI_CON) | CON_BRD;
-+ writew(reg, snfi->regs + NFI_CON);
-+
-+ ret = wait_for_completion_timeout(&snfi->done, msecs_to_jiffies(500));
-+ if (!ret) {
-+ dev_err(snfi->dev, "read ahb done timeout\n");
-+ writew(0, snfi->regs + NFI_INTR_EN);
-+ ret = -ETIMEDOUT;
-+ goto out;
-+ }
-+
-+ ret = readl_poll_timeout_atomic(snfi->regs + NFI_BYTELEN, reg,
-+ ADDRCNTR_SEC(reg) >= sectors, 10,
-+ MTK_TIMEOUT);
-+ if (ret < 0) {
-+ dev_err(snfi->dev, "polling read byte len timeout\n");
-+ ret = -EIO;
-+ } else {
-+ if (!oob_on) {
-+ ret = mtk_ecc_wait_done(snfi->ecc, ECC_DECODE);
-+ if (ret) {
-+ dev_warn(snfi->dev, "wait ecc done timeout\n");
-+ } else {
-+ mtk_snfi_update_ecc_stats(mem, snfi->buffer,
-+ sectors);
-+ mtk_snfi_read_fdm_data(mem, sectors);
-+ }
-+ }
-+ }
-+
-+ if (oob_on)
-+ goto out;
-+
-+ mtk_ecc_disable(snfi->ecc);
-+out:
-+ dma_unmap_single(snfi->dev, dma_addr, len, DMA_FROM_DEVICE);
-+ writel(0, snfi->regs + NFI_CON);
-+ writel(0, snfi->regs + NFI_CNFG);
-+ reg = readl(snfi->regs + SNFI_MISC_CTL);
-+ reg &= ~RD_CUSTOM_EN;
-+ writel(reg, snfi->regs + SNFI_MISC_CTL);
-+
-+ return ret;
-+}
-+
-+static int mtk_snfi_write_to_cache(struct spi_mem *mem,
-+ const struct spi_mem_op *op,
-+ int oob_on)
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ u32 sectors = mtd->writesize / snfi->caps->nand_sec_size;
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u32 reg, len, col_addr = 0;
-+ dma_addr_t dma_addr;
-+ int ret;
-+
-+ len = sectors * (snfi->caps->nand_sec_size
-+ + snfi_nand->spare_per_sector);
-+
-+ dma_addr = dma_map_single(snfi->dev, snfi->buffer, len,
-+ DMA_TO_DEVICE);
-+ ret = dma_mapping_error(snfi->dev, dma_addr);
-+ if (ret) {
-+ dev_err(snfi->dev, "dma mapping error\n");
-+ return -EINVAL;
-+ }
-+
-+ /* set program load cmd and address */
-+ reg = (op->cmd.opcode << WR_LOAD_CMD_SHIFT);
-+ writel(reg, snfi->regs + SNFI_PG_CTL1);
-+ writel(col_addr & WR_LOAD_ADDR_MASK, snfi->regs + SNFI_PG_CTL2);
-+
-+ reg = readl(snfi->regs + SNFI_MISC_CTL);
-+ reg |= WR_CUSTOM_EN;
-+ reg &= ~(RD_MODE_MASK | WR_X4_EN);
-+
-+ if (op->data.buswidth == 4)
-+ reg |= WR_X4_EN;
-+ writel(reg, snfi->regs + SNFI_MISC_CTL);
-+
-+ writel(len << WR_LEN_SHIFT, snfi->regs + SNFI_MISC_CTL2);
-+ writew(sectors << CON_SEC_SHIFT, snfi->regs + NFI_CON);
-+
-+ reg = readw(snfi->regs + NFI_CNFG);
-+ reg &= ~(CNFG_READ_EN | CNFG_BYTE_RW);
-+ reg |= CNFG_DMA | CNFG_DMA_BURST_EN | CNFG_OP_PROGRAM;
-+
-+ if (!oob_on) {
-+ reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
-+ writew(reg, snfi->regs + NFI_CNFG);
-+
-+ snfi->ecc_cfg.mode = ECC_NFI_MODE;
-+ snfi->ecc_cfg.op = ECC_ENCODE;
-+ ret = mtk_ecc_enable(snfi->ecc, &snfi->ecc_cfg);
-+ if (ret) {
-+ dev_err(snfi->dev, "ecc enable failed\n");
-+ /* clear NFI_CNFG */
-+ reg &= ~(CNFG_DMA_BURST_EN | CNFG_DMA |
-+ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
-+ writew(reg, snfi->regs + NFI_CNFG);
-+ dma_unmap_single(snfi->dev, dma_addr, len,
-+ DMA_FROM_DEVICE);
-+ goto out;
-+ }
-+ /* write OOB into the FDM registers (OOB area in MTK NAND) */
-+ mtk_snfi_write_fdm_data(mem, sectors);
-+ } else {
-+ writew(reg, snfi->regs + NFI_CNFG);
-+ }
-+ writel(lower_32_bits(dma_addr), snfi->regs + NFI_STRADDR);
-+ readw(snfi->regs + NFI_INTR_STA);
-+ writew(INTR_AHB_DONE_EN, snfi->regs + NFI_INTR_EN);
-+
-+ init_completion(&snfi->done);
-+
-+ /* set dummy command to trigger NFI enter SPI mode */
-+ writew(NAND_CMD_DUMMYPROG, snfi->regs + NFI_CMD);
-+ reg = readl(snfi->regs + NFI_CON) | CON_BWR;
-+ writew(reg, snfi->regs + NFI_CON);
-+
-+ ret = wait_for_completion_timeout(&snfi->done, msecs_to_jiffies(500));
-+ if (!ret) {
-+ dev_err(snfi->dev, "custom program done timeout\n");
-+ writew(0, snfi->regs + NFI_INTR_EN);
-+ ret = -ETIMEDOUT;
-+ goto ecc_disable;
-+ }
-+
-+ ret = readl_poll_timeout_atomic(snfi->regs + NFI_ADDRCNTR, reg,
-+ ADDRCNTR_SEC(reg) >= sectors,
-+ 10, MTK_TIMEOUT);
-+ if (ret)
-+ dev_err(snfi->dev, "hwecc write timeout\n");
-+
-+ecc_disable:
-+ mtk_ecc_disable(snfi->ecc);
-+
-+out:
-+ dma_unmap_single(snfi->dev, dma_addr, len, DMA_TO_DEVICE);
-+ writel(0, snfi->regs + NFI_CON);
-+ writel(0, snfi->regs + NFI_CNFG);
-+ reg = readl(snfi->regs + SNFI_MISC_CTL);
-+ reg &= ~WR_CUSTOM_EN;
-+ writel(reg, snfi->regs + SNFI_MISC_CTL);
-+
-+ return ret;
-+}
-+
-+static int mtk_snfi_read(struct spi_mem *mem,
-+ const struct spi_mem_op *op)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u32 col_addr = op->addr.val;
-+ int i, ret, sectors, oob_on = false;
-+
-+ if (col_addr == mtd->writesize)
-+ oob_on = true;
-+
-+ ret = mtk_snfi_read_from_cache(mem, op, oob_on);
-+ if (ret) {
-+ dev_warn(snfi->dev, "read from cache fail\n");
-+ return ret;
-+ }
-+
-+ sectors = mtd->writesize / snfi->caps->nand_sec_size;
-+ for (i = 0; i < sectors; i++) {
-+ if (oob_on)
-+ memcpy(oob_ptr(mem, i),
-+ mtk_oob_ptr(mem, snfi->buffer, i),
-+ snfi->caps->nand_fdm_size);
-+
-+ if (i == snfi_nand->bad_mark.sec && snfi->caps->bad_mark_swap)
-+ snfi_nand->bad_mark.bm_swap(mem, snfi->buffer,
-+ oob_on);
-+ }
-+
-+ if (!oob_on)
-+ memcpy(spinand->databuf, snfi->buffer, mtd->writesize);
-+
-+ return ret;
-+}
-+
-+static int mtk_snfi_write(struct spi_mem *mem,
-+ const struct spi_mem_op *op)
-+{
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ struct mtk_snfi_nand_chip *snfi_nand = &snfi->snfi_nand;
-+ u32 ret, i, sectors, col_addr = op->addr.val;
-+ int oob_on = false;
-+
-+ if (col_addr == mtd->writesize)
-+ oob_on = true;
-+
-+ sectors = mtd->writesize / snfi->caps->nand_sec_size;
-+ memset(snfi->buffer, 0xff, mtd->writesize + mtd->oobsize);
-+
-+ if (!oob_on)
-+ memcpy(snfi->buffer, spinand->databuf, mtd->writesize);
-+
-+ for (i = 0; i < sectors; i++) {
-+ if (i == snfi_nand->bad_mark.sec && snfi->caps->bad_mark_swap)
-+ snfi_nand->bad_mark.bm_swap(mem, snfi->buffer, oob_on);
-+
-+ if (oob_on)
-+ memcpy(mtk_oob_ptr(mem, snfi->buffer, i),
-+ oob_ptr(mem, i),
-+ snfi->caps->nand_fdm_size);
-+ }
-+
-+ ret = mtk_snfi_write_to_cache(mem, op, oob_on);
-+ if (ret)
-+ dev_warn(snfi->dev, "write to cache fail\n");
-+
-+ return ret;
-+}
-+
-+static int mtk_snfi_command_exec(struct mtk_snfi *snfi,
-+ const u8 *txbuf, u8 *rxbuf,
-+ const u32 txlen, const u32 rxlen)
-+{
-+ u32 tmp, i, j, reg, m;
-+ u8 *p_tmp = (u8 *)(&tmp);
-+ int ret = 0;
-+
-+ /* Moving tx data to NFI_SPI GPRAM */
-+ for (i = 0, m = 0; i < txlen; ) {
-+ for (j = 0, tmp = 0; i < txlen && j < 4; i++, j++)
-+ p_tmp[j] = txbuf[i];
-+
-+ writel(tmp, snfi->regs + SNFI_GPRAM_DATA + m);
-+ m += 4;
-+ }
-+
-+ writel(txlen, snfi->regs + SNFI_MAC_OUTL);
-+ writel(rxlen, snfi->regs + SNFI_MAC_INL);
-+ ret = mtk_snfi_mac_op(snfi);
-+ if (ret)
-+ return ret;
-+
-+ /* For NULL input data, this loop will be skipped */
-+ if (rxlen)
-+ for (i = 0, m = 0; i < rxlen; ) {
-+ reg = readl(snfi->regs +
-+ SNFI_GPRAM_DATA + m);
-+ for (j = 0; i < rxlen && j < 4; i++, j++, rxbuf++) {
-+ if (m == 0 && i == 0)
-+ j = i + txlen;
-+ *rxbuf = (reg >> (j * 8)) & 0xFF;
-+ }
-+ m += 4;
-+ }
-+
-+ return ret;
-+}
-+
-+/*
-+ * mtk_snfi_exec_op - to process command/data to send to the
-+ * SPI NAND by mtk controller
-+ */
-+static int mtk_snfi_exec_op(struct spi_mem *mem,
-+ const struct spi_mem_op *op)
-+
-+{
-+ struct mtk_snfi *snfi = spi_controller_get_devdata(mem->spi->master);
-+ struct spinand_device *spinand = spi_mem_get_drvdata(mem);
-+ struct mtd_info *mtd = spinand_to_mtd(spinand);
-+ struct nand_device *nand = mtd_to_nanddev(mtd);
-+ const struct spi_mem_op *read_cache;
-+ const struct spi_mem_op *write_cache;
-+ const struct spi_mem_op *update_cache;
-+ u32 tmpbufsize, txlen = 0, rxlen = 0;
-+ u8 *txbuf, *rxbuf = NULL, *buf;
-+ int i, ret = 0;
-+
-+ ret = mtk_snfi_reset(snfi);
-+ if (ret) {
-+ dev_warn(snfi->dev, "reset spi memory controller fail\n");
-+ return ret;
-+ }
-+
-+ /*if bbt initial, framework have detect nand information */
-+ if (nand->bbt.cache) {
-+ read_cache = spinand->op_templates.read_cache;
-+ write_cache = spinand->op_templates.write_cache;
-+ update_cache = spinand->op_templates.update_cache;
-+
-+ ret = mtk_snfi_hw_runtime_config(mem);
-+ if (ret)
-+ return ret;
-+
-+ /* For Read/Write with cache, Erase use framework flow */
-+ if (op->cmd.opcode == read_cache->cmd.opcode) {
-+ ret = mtk_snfi_read(mem, op);
-+ if (ret)
-+ dev_warn(snfi->dev, "snfi read fail\n");
-+
-+ return ret;
-+ } else if ((op->cmd.opcode == write_cache->cmd.opcode)
-+ || (op->cmd.opcode == update_cache->cmd.opcode)) {
-+ ret = mtk_snfi_write(mem, op);
-+ if (ret)
-+ dev_warn(snfi->dev, "snfi write fail\n");
-+
-+ return ret;
-+ }
-+ }
-+
-+ tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
-+ op->dummy.nbytes + op->data.nbytes;
-+
-+ txbuf = kzalloc(tmpbufsize, GFP_KERNEL);
-+ if (!txbuf)
-+ return -ENOMEM;
-+
-+ txbuf[txlen++] = op->cmd.opcode;
-+
-+ if (op->addr.nbytes)
-+ for (i = 0; i < op->addr.nbytes; i++)
-+ txbuf[txlen++] = op->addr.val >>
-+ (8 * (op->addr.nbytes - i - 1));
-+
-+ txlen += op->dummy.nbytes;
-+
-+ if (op->data.dir == SPI_MEM_DATA_OUT)
-+ for (i = 0; i < op->data.nbytes; i++) {
-+ buf = (u8 *)op->data.buf.out;
-+ txbuf[txlen++] = buf[i];
-+ }
-+
-+ if (op->data.dir == SPI_MEM_DATA_IN) {
-+ rxbuf = (u8 *)op->data.buf.in;
-+ rxlen += op->data.nbytes;
-+ }
-+
-+ ret = mtk_snfi_command_exec(snfi, txbuf, rxbuf, txlen, rxlen);
-+ kfree(txbuf);
-+
-+ return ret;
-+}
-+
-+static int mtk_snfi_init(struct mtk_snfi *snfi)
-+{
-+ int ret;
-+
-+ /* Reset the state machine and data FIFO */
-+ ret = mtk_snfi_reset(snfi);
-+ if (ret) {
-+ dev_warn(snfi->dev, "MTK reset controller fail\n");
-+ return ret;
-+ }
-+
-+ snfi->buffer = devm_kzalloc(snfi->dev, 4096 + 256, GFP_KERNEL);
-+ if (!snfi->buffer)
-+ return -ENOMEM;
-+
-+ /* Clear interrupt, read clear. */
-+ readw(snfi->regs + NFI_INTR_STA);
-+ writew(0, snfi->regs + NFI_INTR_EN);
-+
-+ writel(0, snfi->regs + NFI_CON);
-+ writel(0, snfi->regs + NFI_CNFG);
-+
-+ /* Change to NFI_SPI mode. */
-+ writel(SNFI_MODE_EN, snfi->regs + SNFI_CNFG);
-+
-+ return 0;
-+}
-+
-+static int mtk_snfi_check_buswidth(u8 width)
-+{
-+ switch (width) {
-+ case 1:
-+ case 2:
-+ case 4:
-+ return 0;
-+
-+ default:
-+ break;
-+ }
-+
-+ return -ENOTSUPP;
-+}
-+
-+static bool mtk_snfi_supports_op(struct spi_mem *mem,
-+ const struct spi_mem_op *op)
-+{
-+ int ret = 0;
-+
-+ /* For MTK Spi Nand controller, cmd buswidth just support 1 bit*/
-+ if (op->cmd.buswidth != 1)
-+ ret = -ENOTSUPP;
-+
-+ if (op->addr.nbytes)
-+ ret |= mtk_snfi_check_buswidth(op->addr.buswidth);
-+
-+ if (op->dummy.nbytes)
-+ ret |= mtk_snfi_check_buswidth(op->dummy.buswidth);
-+
-+ if (op->data.nbytes)
-+ ret |= mtk_snfi_check_buswidth(op->data.buswidth);
-+
-+ if (ret)
-+ return false;
-+
-+ return true;
-+}
-+
-+static const struct spi_controller_mem_ops mtk_snfi_ops = {
-+ .supports_op = mtk_snfi_supports_op,
-+ .exec_op = mtk_snfi_exec_op,
-+};
-+
-+static const struct mtk_snfi_caps snfi_mt7622 = {
-+ .spare_size = spare_size_mt7622,
-+ .num_spare_size = 4,
-+ .nand_sec_size = 512,
-+ .nand_fdm_size = 8,
-+ .nand_fdm_ecc_size = 1,
-+ .ecc_parity_bits = 13,
-+ .pageformat_spare_shift = 4,
-+ .bad_mark_swap = 0,
-+};
-+
-+static const struct mtk_snfi_caps snfi_mt7629 = {
-+ .spare_size = spare_size_mt7622,
-+ .num_spare_size = 4,
-+ .nand_sec_size = 512,
-+ .nand_fdm_size = 8,
-+ .nand_fdm_ecc_size = 1,
-+ .ecc_parity_bits = 13,
-+ .pageformat_spare_shift = 4,
-+ .bad_mark_swap = 1,
-+};
-+
-+static const struct of_device_id mtk_snfi_id_table[] = {
-+ { .compatible = "mediatek,mt7622-snfi", .data = &snfi_mt7622, },
-+ { .compatible = "mediatek,mt7629-snfi", .data = &snfi_mt7629, },
-+ { /* sentinel */ }
-+};
-+
-+static int mtk_snfi_probe(struct platform_device *pdev)
-+{
-+ struct device *dev = &pdev->dev;
-+ struct device_node *np = dev->of_node;
-+ struct spi_controller *ctlr;
-+ struct mtk_snfi *snfi;
-+ struct resource *res;
-+ int ret = 0, irq;
-+
-+ ctlr = spi_alloc_master(&pdev->dev, sizeof(*snfi));
-+ if (!ctlr)
-+ return -ENOMEM;
-+
-+ snfi = spi_controller_get_devdata(ctlr);
-+ snfi->caps = of_device_get_match_data(dev);
-+ snfi->dev = dev;
-+
-+ snfi->ecc = of_mtk_ecc_get(np);
-+ if (IS_ERR_OR_NULL(snfi->ecc))
-+ goto err_put_master;
-+
-+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+ snfi->regs = devm_ioremap_resource(dev, res);
-+ if (IS_ERR(snfi->regs)) {
-+ ret = PTR_ERR(snfi->regs);
-+ goto release_ecc;
-+ }
-+
-+ /* find the clocks */
-+ snfi->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
-+ if (IS_ERR(snfi->clk.nfi_clk)) {
-+ dev_err(dev, "no nfi clk\n");
-+ ret = PTR_ERR(snfi->clk.nfi_clk);
-+ goto release_ecc;
-+ }
-+
-+ snfi->clk.spi_clk = devm_clk_get(dev, "spi_clk");
-+ if (IS_ERR(snfi->clk.spi_clk)) {
-+ dev_err(dev, "no spi clk\n");
-+ ret = PTR_ERR(snfi->clk.spi_clk);
-+ goto release_ecc;
-+ }
-+
-+ ret = mtk_snfi_enable_clk(dev, &snfi->clk);
-+ if (ret)
-+ goto release_ecc;
-+
-+ /* find the irq */
-+ irq = platform_get_irq(pdev, 0);
-+ if (irq < 0) {
-+ dev_err(dev, "no snfi irq resource\n");
-+ ret = -EINVAL;
-+ goto clk_disable;
-+ }
-+
-+ ret = devm_request_irq(dev, irq, mtk_snfi_irq, 0, "mtk-snfi", snfi);
-+ if (ret) {
-+ dev_err(dev, "failed to request snfi irq\n");
-+ goto clk_disable;
-+ }
-+
-+ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
-+ if (ret) {
-+ dev_err(dev, "failed to set dma mask\n");
-+ goto clk_disable;
-+ }
-+
-+ ctlr->dev.of_node = np;
-+ ctlr->mem_ops = &mtk_snfi_ops;
-+
-+ platform_set_drvdata(pdev, snfi);
-+ ret = mtk_snfi_init(snfi);
-+ if (ret) {
-+ dev_err(dev, "failed to init snfi\n");
-+ goto clk_disable;
-+ }
-+
-+ ret = devm_spi_register_master(dev, ctlr);
-+ if (ret)
-+ goto clk_disable;
-+
-+ return 0;
-+
-+clk_disable:
-+ mtk_snfi_disable_clk(&snfi->clk);
-+
-+release_ecc:
-+ mtk_ecc_release(snfi->ecc);
-+
-+err_put_master:
-+ spi_master_put(ctlr);
-+
-+ dev_err(dev, "MediaTek SPI NAND interface probe failed %d\n", ret);
-+ return ret;
-+}
-+
-+static int mtk_snfi_remove(struct platform_device *pdev)
-+{
-+ struct mtk_snfi *snfi = platform_get_drvdata(pdev);
-+
-+ mtk_snfi_disable_clk(&snfi->clk);
-+
-+ return 0;
-+}
-+
-+static int mtk_snfi_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct mtk_snfi *snfi = platform_get_drvdata(pdev);
-+
-+ mtk_snfi_disable_clk(&snfi->clk);
-+
-+ return 0;
-+}
-+
-+static int mtk_snfi_resume(struct platform_device *pdev)
-+{
-+ struct device *dev = &pdev->dev;
-+ struct mtk_snfi *snfi = dev_get_drvdata(dev);
-+ int ret;
-+
-+ ret = mtk_snfi_enable_clk(dev, &snfi->clk);
-+ if (ret)
-+ return ret;
-+
-+ ret = mtk_snfi_init(snfi);
-+ if (ret)
-+ dev_err(dev, "failed to init snfi controller\n");
-+
-+ return ret;
-+}
-+
-+static struct platform_driver mtk_snfi_driver = {
-+ .driver = {
-+ .name = "mtk-snfi",
-+ .of_match_table = mtk_snfi_id_table,
-+ },
-+ .probe = mtk_snfi_probe,
-+ .remove = mtk_snfi_remove,
-+ .suspend = mtk_snfi_suspend,
-+ .resume = mtk_snfi_resume,
-+};
-+
-+module_platform_driver(mtk_snfi_driver);
-+
-+MODULE_LICENSE("GPL v2");
-+MODULE_AUTHOR("Xiangsheng Hou <xiangsheng.hou@mediatek.com>");
-+MODULE_DESCRIPTION("Mediatek SPI Memory Interface Driver");