aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/mtmips/Makefile1
-rw-r--r--drivers/clk/mtmips/clk-mt7621.c288
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/mmc/mtk-sd.c13
-rw-r--r--drivers/mtd/nand/raw/Kconfig17
-rw-r--r--drivers/mtd/nand/raw/Makefile2
-rw-r--r--drivers/mtd/nand/raw/mt7621_nand.c1205
-rw-r--r--drivers/mtd/nand/raw/mt7621_nand.h29
-rw-r--r--drivers/mtd/nand/raw/mt7621_nand_spl.c237
-rw-r--r--drivers/net/mtk_eth.c45
-rw-r--r--drivers/phy/Kconfig2
-rw-r--r--drivers/pinctrl/mtmips/Kconfig9
-rw-r--r--drivers/pinctrl/mtmips/Makefile1
-rw-r--r--drivers/pinctrl/mtmips/pinctrl-mt7621.c306
-rw-r--r--drivers/pinctrl/mtmips/pinctrl-mtmips-common.c4
-rw-r--r--drivers/pinctrl/mtmips/pinctrl-mtmips-common.h12
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/watchdog/Kconfig2
19 files changed, 2152 insertions, 27 deletions
diff --git a/drivers/clk/mtmips/Makefile b/drivers/clk/mtmips/Makefile
index 732e7f2545..ee8b5afe87 100644
--- a/drivers/clk/mtmips/Makefile
+++ b/drivers/clk/mtmips/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SOC_MT7620) += clk-mt7620.o
+obj-$(CONFIG_SOC_MT7621) += clk-mt7621.o
obj-$(CONFIG_SOC_MT7628) += clk-mt7628.o
diff --git a/drivers/clk/mtmips/clk-mt7621.c b/drivers/clk/mtmips/clk-mt7621.c
new file mode 100644
index 0000000000..03363b70d7
--- /dev/null
+++ b/drivers/clk/mtmips/clk-mt7621.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <dt-bindings/clock/mt7621-clk.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+
+#define SYSC_MAP_SIZE 0x100
+#define MEMC_MAP_SIZE 0x1000
+
+/* SYSC */
+#define SYSCFG0_REG 0x10
+#define XTAL_MODE_SEL GENMASK(8, 6)
+
+#define CLKCFG0_REG 0x2c
+#define CPU_CLK_SEL GENMASK(31, 30)
+#define PERI_CLK_SEL BIT(4)
+
+#define CLKCFG1_REG 0x30
+
+#define CUR_CLK_STS_REG 0x44
+#define CUR_CPU_FDIV GENMASK(12, 8)
+#define CUR_CPU_FFRAC GENMASK(4, 0)
+
+/* MEMC */
+#define MEMPLL1_REG 0x0604
+#define RG_MEPL_DIV2_SEL GENMASK(2, 1)
+
+#define MEMPLL6_REG 0x0618
+#define MEMPLL18_REG 0x0648
+#define RG_MEPL_PREDIV GENMASK(13, 12)
+#define RG_MEPL_FBDIV GENMASK(10, 4)
+
+/* Fixed 500M clock */
+#define GMPLL_CLK 500000000
+
+struct mt7621_clk_priv {
+ void __iomem *sysc_base;
+ int cpu_clk;
+ int ddr_clk;
+ int sys_clk;
+ int xtal_clk;
+};
+
+enum mt7621_clk_src {
+ CLK_SRC_CPU,
+ CLK_SRC_DDR,
+ CLK_SRC_SYS,
+ CLK_SRC_XTAL,
+ CLK_SRC_PERI,
+ CLK_SRC_125M,
+ CLK_SRC_150M,
+ CLK_SRC_250M,
+ CLK_SRC_270M,
+
+ __CLK_SRC_MAX
+};
+
+struct mt7621_clk_map {
+ u32 cgbit;
+ enum mt7621_clk_src clksrc;
+};
+
+#define CLK_MAP(_id, _cg, _src) \
+ [_id] = { .cgbit = (_cg), .clksrc = (_src) }
+
+#define CLK_MAP_SRC(_id, _src) \
+ [_id] = { .cgbit = UINT32_MAX, .clksrc = (_src) }
+
+static const struct mt7621_clk_map mt7621_clk_mappings[] = {
+ CLK_MAP_SRC(MT7621_CLK_XTAL, CLK_SRC_XTAL),
+ CLK_MAP_SRC(MT7621_CLK_CPU, CLK_SRC_CPU),
+ CLK_MAP_SRC(MT7621_CLK_BUS, CLK_SRC_SYS),
+ CLK_MAP_SRC(MT7621_CLK_50M, CLK_SRC_PERI),
+ CLK_MAP_SRC(MT7621_CLK_125M, CLK_SRC_125M),
+ CLK_MAP_SRC(MT7621_CLK_150M, CLK_SRC_150M),
+ CLK_MAP_SRC(MT7621_CLK_250M, CLK_SRC_250M),
+ CLK_MAP_SRC(MT7621_CLK_270M, CLK_SRC_270M),
+
+ CLK_MAP(MT7621_CLK_HSDMA, 5, CLK_SRC_150M),
+ CLK_MAP(MT7621_CLK_FE, 6, CLK_SRC_250M),
+ CLK_MAP(MT7621_CLK_SP_DIVTX, 7, CLK_SRC_270M),
+ CLK_MAP(MT7621_CLK_TIMER, 8, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_PCM, 11, CLK_SRC_270M),
+ CLK_MAP(MT7621_CLK_PIO, 13, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_GDMA, 14, CLK_SRC_SYS),
+ CLK_MAP(MT7621_CLK_NAND, 15, CLK_SRC_125M),
+ CLK_MAP(MT7621_CLK_I2C, 16, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_I2S, 17, CLK_SRC_270M),
+ CLK_MAP(MT7621_CLK_SPI, 18, CLK_SRC_SYS),
+ CLK_MAP(MT7621_CLK_UART1, 19, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_UART2, 20, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_UART3, 21, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_ETH, 23, CLK_SRC_PERI),
+ CLK_MAP(MT7621_CLK_PCIE0, 24, CLK_SRC_125M),
+ CLK_MAP(MT7621_CLK_PCIE1, 25, CLK_SRC_125M),
+ CLK_MAP(MT7621_CLK_PCIE2, 26, CLK_SRC_125M),
+ CLK_MAP(MT7621_CLK_CRYPTO, 29, CLK_SRC_250M),
+ CLK_MAP(MT7621_CLK_SHXC, 30, CLK_SRC_PERI),
+
+ CLK_MAP_SRC(MT7621_CLK_MAX, __CLK_SRC_MAX),
+
+ CLK_MAP_SRC(MT7621_CLK_DDR, CLK_SRC_DDR),
+};
+
+static ulong mt7621_clk_get_rate(struct clk *clk)
+{
+ struct mt7621_clk_priv *priv = dev_get_priv(clk->dev);
+ u32 val;
+
+ switch (mt7621_clk_mappings[clk->id].clksrc) {
+ case CLK_SRC_CPU:
+ return priv->cpu_clk;
+ case CLK_SRC_DDR:
+ return priv->ddr_clk;
+ case CLK_SRC_SYS:
+ return priv->sys_clk;
+ case CLK_SRC_XTAL:
+ return priv->xtal_clk;
+ case CLK_SRC_PERI:
+ val = readl(priv->sysc_base + CLKCFG0_REG);
+ if (val & PERI_CLK_SEL)
+ return priv->xtal_clk;
+ else
+ return GMPLL_CLK / 10;
+ case CLK_SRC_125M:
+ return 125000000;
+ case CLK_SRC_150M:
+ return 150000000;
+ case CLK_SRC_250M:
+ return 250000000;
+ case CLK_SRC_270M:
+ return 270000000;
+ default:
+ return 0;
+ }
+}
+
+static int mt7621_clk_enable(struct clk *clk)
+{
+ struct mt7621_clk_priv *priv = dev_get_priv(clk->dev);
+ u32 cgbit;
+
+ cgbit = mt7621_clk_mappings[clk->id].cgbit;
+ if (cgbit == UINT32_MAX)
+ return -ENOSYS;
+
+ setbits_32(priv->sysc_base + CLKCFG1_REG, BIT(cgbit));
+
+ return 0;
+}
+
+static int mt7621_clk_disable(struct clk *clk)
+{
+ struct mt7621_clk_priv *priv = dev_get_priv(clk->dev);
+ u32 cgbit;
+
+ cgbit = mt7621_clk_mappings[clk->id].cgbit;
+ if (cgbit == UINT32_MAX)
+ return -ENOSYS;
+
+ clrbits_32(priv->sysc_base + CLKCFG1_REG, BIT(cgbit));
+
+ return 0;
+}
+
+static int mt7621_clk_request(struct clk *clk)
+{
+ if (clk->id >= ARRAY_SIZE(mt7621_clk_mappings))
+ return -EINVAL;
+ return 0;
+}
+
+const struct clk_ops mt7621_clk_ops = {
+ .request = mt7621_clk_request,
+ .enable = mt7621_clk_enable,
+ .disable = mt7621_clk_disable,
+ .get_rate = mt7621_clk_get_rate,
+};
+
+static void mt7621_get_clocks(struct mt7621_clk_priv *priv, struct regmap *memc)
+{
+ u32 bs, xtal_sel, clkcfg0, cur_clk, mempll, dividx, fb;
+ u32 xtal_clk, xtal_div, ffiv, ffrac, cpu_clk, ddr_clk;
+ static const u32 xtal_div_tbl[] = {0, 1, 2, 2};
+
+ bs = readl(priv->sysc_base + SYSCFG0_REG);
+ clkcfg0 = readl(priv->sysc_base + CLKCFG0_REG);
+ cur_clk = readl(priv->sysc_base + CUR_CLK_STS_REG);
+
+ xtal_sel = FIELD_GET(XTAL_MODE_SEL, bs);
+
+ if (xtal_sel <= 2)
+ xtal_clk = 20 * 1000 * 1000;
+ else if (xtal_sel <= 5)
+ xtal_clk = 40 * 1000 * 1000;
+ else
+ xtal_clk = 25 * 1000 * 1000;
+
+ switch (FIELD_GET(CPU_CLK_SEL, clkcfg0)) {
+ case 0:
+ cpu_clk = GMPLL_CLK;
+ break;
+ case 1:
+ regmap_read(memc, MEMPLL18_REG, &mempll);
+ dividx = FIELD_GET(RG_MEPL_PREDIV, mempll);
+ fb = FIELD_GET(RG_MEPL_FBDIV, mempll);
+ xtal_div = 1 << xtal_div_tbl[dividx];
+ cpu_clk = (fb + 1) * xtal_clk / xtal_div;
+ break;
+ default:
+ cpu_clk = xtal_clk;
+ }
+
+ ffiv = FIELD_GET(CUR_CPU_FDIV, cur_clk);
+ ffrac = FIELD_GET(CUR_CPU_FFRAC, cur_clk);
+ cpu_clk = cpu_clk / ffiv * ffrac;
+
+ regmap_read(memc, MEMPLL6_REG, &mempll);
+ dividx = FIELD_GET(RG_MEPL_PREDIV, mempll);
+ fb = FIELD_GET(RG_MEPL_FBDIV, mempll);
+ xtal_div = 1 << xtal_div_tbl[dividx];
+ ddr_clk = fb * xtal_clk / xtal_div;
+
+ regmap_read(memc, MEMPLL1_REG, &bs);
+ if (!FIELD_GET(RG_MEPL_DIV2_SEL, bs))
+ ddr_clk *= 2;
+
+ priv->cpu_clk = cpu_clk;
+ priv->sys_clk = cpu_clk / 4;
+ priv->ddr_clk = ddr_clk;
+ priv->xtal_clk = xtal_clk;
+}
+
+static int mt7621_clk_probe(struct udevice *dev)
+{
+ struct mt7621_clk_priv *priv = dev_get_priv(dev);
+ struct ofnode_phandle_args args;
+ struct udevice *pdev;
+ struct regmap *memc;
+ int ret;
+
+ pdev = dev_get_parent(dev);
+ if (!pdev)
+ return -ENODEV;
+
+ priv->sysc_base = dev_remap_addr(pdev);
+ if (!priv->sysc_base)
+ return -EINVAL;
+
+ /* get corresponding memc phandle */
+ ret = dev_read_phandle_with_args(dev, "mediatek,memc", NULL, 0, 0,
+ &args);
+ if (ret)
+ return ret;
+
+ memc = syscon_node_to_regmap(args.node);
+ if (IS_ERR(memc))
+ return PTR_ERR(memc);
+
+ mt7621_get_clocks(priv, memc);
+
+ return 0;
+}
+
+static const struct udevice_id mt7621_clk_ids[] = {
+ { .compatible = "mediatek,mt7621-clk" },
+ { }
+};
+
+U_BOOT_DRIVER(mt7621_clk) = {
+ .name = "mt7621-clk",
+ .id = UCLASS_CLK,
+ .of_match = mt7621_clk_ids,
+ .probe = mt7621_clk_probe,
+ .priv_auto = sizeof(struct mt7621_clk_priv),
+ .ops = &mt7621_clk_ops,
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 3c73a7f618..aaa152fae7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -553,7 +553,7 @@ config MT7620_GPIO
config MT7621_GPIO
bool "MediaTek MT7621 GPIO driver"
- depends on DM_GPIO && SOC_MT7628
+ depends on DM_GPIO && (SOC_MT7621 || SOC_MT7628)
default y
help
Say yes here to support MediaTek MT7621 compatible GPIOs.
diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c
index 97182ffd7f..e61e8cf4b9 100644
--- a/drivers/mmc/mtk-sd.c
+++ b/drivers/mmc/mtk-sd.c
@@ -1761,6 +1761,18 @@ static const struct msdc_compatible mt7620_compat = {
.default_pad_dly = true,
};
+static const struct msdc_compatible mt7621_compat = {
+ .clk_div_bits = 8,
+ .pad_tune0 = false,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = false,
+ .stop_clk_fix = false,
+ .enhance_rx = false,
+ .builtin_pad_ctrl = true,
+ .default_pad_dly = true,
+};
+
static const struct msdc_compatible mt7622_compat = {
.clk_div_bits = 12,
.pad_tune0 = true,
@@ -1809,6 +1821,7 @@ static const struct msdc_compatible mt8183_compat = {
static const struct udevice_id msdc_ids[] = {
{ .compatible = "mediatek,mt7620-mmc", .data = (ulong)&mt7620_compat },
+ { .compatible = "mediatek,mt7621-mmc", .data = (ulong)&mt7621_compat },
{ .compatible = "mediatek,mt7622-mmc", .data = (ulong)&mt7622_compat },
{ .compatible = "mediatek,mt7623-mmc", .data = (ulong)&mt7623_compat },
{ .compatible = "mediatek,mt8512-mmc", .data = (ulong)&mt8512_compat },
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 190300fc17..ce67d1abde 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -526,12 +526,25 @@ config TEGRA_NAND
help
Enables support for NAND Flash chips on Tegra SoCs platforms.
+config NAND_MT7621
+ bool "Support for MediaTek MT7621 NAND flash controller"
+ depends on SOC_MT7621
+ select SYS_NAND_SELF_INIT
+ select SPL_SYS_NAND_SELF_INIT
+ imply CMD_NAND
+ help
+ This enables NAND driver for the NAND flash controller on MediaTek
+ MT7621 platform.
+ The controller supports 4~12 bits correction per 512 bytes with a
+ maximum 4KB page size.
+
comment "Generic NAND options"
config SYS_NAND_BLOCK_SIZE
hex "NAND chip eraseblock size"
depends on ARCH_SUNXI || SPL_NAND_SUPPORT || TPL_NAND_SUPPORT
- depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_FSL_IFC
+ depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && \
+ !NAND_FSL_IFC && !NAND_MT7621
help
Number of data bytes in one eraseblock for the NAND chip on the
board. This is the multiple of NAND_PAGE_SIZE and the number of
@@ -556,7 +569,7 @@ config SYS_NAND_PAGE_SIZE
depends on ARCH_SUNXI || NAND_OMAP_GPMC || NAND_LPC32XX_SLC || \
SPL_NAND_SIMPLE || (NAND_MXC && SPL_NAND_SUPPORT) || \
(NAND_ATMEL && SPL_NAND_SUPPORT) || SPL_GENERATE_ATMEL_PMECC_HEADER
- depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC
+ depends on !NAND_MXS && !NAND_DENALI_DT && !NAND_LPC32XX_MLC && !NAND_MT7621
help
Number of data bytes in one page for the NAND chip on the
board, not including the OOB area.
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index e3f6b903f7..f278f31f5c 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_NAND_ZYNQ) += zynq_nand.o
obj-$(CONFIG_NAND_STM32_FMC2) += stm32_fmc2_nand.o
obj-$(CONFIG_CORTINA_NAND) += cortina_nand.o
obj-$(CONFIG_ROCKCHIP_NAND) += rockchip_nfc.o
+obj-$(CONFIG_NAND_MT7621) += mt7621_nand.o
else # minimal SPL drivers
@@ -80,5 +81,6 @@ obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o
obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o
obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
+obj-$(CONFIG_NAND_MT7621) += mt7621_nand_spl.o mt7621_nand.o
endif # drivers
diff --git a/drivers/mtd/nand/raw/mt7621_nand.c b/drivers/mtd/nand/raw/mt7621_nand.c
new file mode 100644
index 0000000000..2fd8934939
--- /dev/null
+++ b/drivers/mtd/nand/raw/mt7621_nand.c
@@ -0,0 +1,1205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <log.h>
+#include <nand.h>
+#include <malloc.h>
+#include <asm/addrspace.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include "mt7621_nand.h"
+
+/* NFI core registers */
+#define NFI_CNFG 0x000
+#define CNFG_OP_MODE GENMASK(14, 12)
+#define CNFG_OP_CUSTOM 6
+#define CNFG_AUTO_FMT_EN BIT(9)
+#define CNFG_HW_ECC_EN BIT(8)
+#define CNFG_BYTE_RW BIT(6)
+#define CNFG_READ_MODE BIT(1)
+
+#define NFI_PAGEFMT 0x004
+#define PAGEFMT_FDM_ECC GENMASK(15, 12)
+#define PAGEFMT_FDM GENMASK(11, 8)
+#define PAGEFMT_SPARE GENMASK(5, 4)
+#define PAGEFMT_PAGE GENMASK(1, 0)
+
+#define NFI_CON 0x008
+#define CON_NFI_SEC GENMASK(15, 12)
+#define CON_NFI_BWR BIT(9)
+#define CON_NFI_BRD BIT(8)
+#define CON_NFI_RST BIT(1)
+#define CON_FIFO_FLUSH BIT(0)
+
+#define NFI_ACCCON 0x00c
+#define ACCCON_POECS GENMASK(31, 28)
+#define ACCCON_POECS_DEF 3
+#define ACCCON_PRECS GENMASK(27, 22)
+#define ACCCON_PRECS_DEF 3
+#define ACCCON_C2R GENMASK(21, 16)
+#define ACCCON_C2R_DEF 7
+#define ACCCON_W2R GENMASK(15, 12)
+#define ACCCON_W2R_DEF 7
+#define ACCCON_WH GENMASK(11, 8)
+#define ACCCON_WH_DEF 15
+#define ACCCON_WST GENMASK(7, 4)
+#define ACCCON_WST_DEF 15
+#define ACCCON_WST_MIN 3
+#define ACCCON_RLT GENMASK(3, 0)
+#define ACCCON_RLT_DEF 15
+#define ACCCON_RLT_MIN 3
+
+#define NFI_CMD 0x020
+
+#define NFI_ADDRNOB 0x030
+#define ADDR_ROW_NOB GENMASK(6, 4)
+#define ADDR_COL_NOB GENMASK(2, 0)
+
+#define NFI_COLADDR 0x034
+#define NFI_ROWADDR 0x038
+
+#define NFI_STRDATA 0x040
+#define STR_DATA BIT(0)
+
+#define NFI_CNRNB 0x044
+#define CB2R_TIME GENMASK(7, 4)
+#define STR_CNRNB BIT(0)
+
+#define NFI_DATAW 0x050
+#define NFI_DATAR 0x054
+
+#define NFI_PIO_DIRDY 0x058
+#define PIO_DIRDY BIT(0)
+
+#define NFI_STA 0x060
+#define STA_NFI_FSM GENMASK(19, 16)
+#define STA_FSM_CUSTOM_DATA 14
+#define STA_BUSY BIT(8)
+#define STA_ADDR BIT(1)
+#define STA_CMD BIT(0)
+
+#define NFI_ADDRCNTR 0x070
+#define SEC_CNTR GENMASK(15, 12)
+#define SEC_ADDR GENMASK(9, 0)
+
+#define NFI_CSEL 0x090
+#define CSEL GENMASK(1, 0)
+
+#define NFI_FDM0L 0x0a0
+#define NFI_FDML(n) (0x0a0 + ((n) << 3))
+
+#define NFI_FDM0M 0x0a4
+#define NFI_FDMM(n) (0x0a4 + ((n) << 3))
+
+#define NFI_MASTER_STA 0x210
+#define MAS_ADDR GENMASK(11, 9)
+#define MAS_RD GENMASK(8, 6)
+#define MAS_WR GENMASK(5, 3)
+#define MAS_RDDLY GENMASK(2, 0)
+
+/* ECC engine registers */
+#define ECC_ENCCON 0x000
+#define ENC_EN BIT(0)
+
+#define ECC_ENCCNFG 0x004
+#define ENC_CNFG_MSG GENMASK(28, 16)
+#define ENC_MODE GENMASK(5, 4)
+#define ENC_MODE_NFI 1
+#define ENC_TNUM GENMASK(2, 0)
+
+#define ECC_ENCIDLE 0x00c
+#define ENC_IDLE BIT(0)
+
+#define ECC_DECCON 0x100
+#define DEC_EN BIT(0)
+
+#define ECC_DECCNFG 0x104
+#define DEC_EMPTY_EN BIT(31)
+#define DEC_CS GENMASK(28, 16)
+#define DEC_CON GENMASK(13, 12)
+#define DEC_CON_EL 2
+#define DEC_MODE GENMASK(5, 4)
+#define DEC_MODE_NFI 1
+#define DEC_TNUM GENMASK(2, 0)
+
+#define ECC_DECIDLE 0x10c
+#define DEC_IDLE BIT(1)
+
+#define ECC_DECENUM 0x114
+#define ERRNUM_S 2
+#define ERRNUM_M GENMASK(3, 0)
+
+#define ECC_DECDONE 0x118
+#define DEC_DONE7 BIT(7)
+#define DEC_DONE6 BIT(6)
+#define DEC_DONE5 BIT(5)
+#define DEC_DONE4 BIT(4)
+#define DEC_DONE3 BIT(3)
+#define DEC_DONE2 BIT(2)
+#define DEC_DONE1 BIT(1)
+#define DEC_DONE0 BIT(0)
+
+#define ECC_DECEL(n) (0x11c + (n) * 4)
+#define DEC_EL_ODD_S 16
+#define DEC_EL_M 0x1fff
+#define DEC_EL_BYTE_POS_S 3
+#define DEC_EL_BIT_POS_M GENMASK(2, 0)
+
+#define ECC_FDMADDR 0x13c
+
+/* ENCIDLE and DECIDLE */
+#define ECC_IDLE BIT(0)
+
+#define ACCTIMING(tpoecs, tprecs, tc2r, tw2r, twh, twst, trlt) \
+ (FIELD_PREP(ACCCON_POECS, tpoecs) | \
+ FIELD_PREP(ACCCON_PRECS, tprecs) | \
+ FIELD_PREP(ACCCON_C2R, tc2r) | \
+ FIELD_PREP(ACCCON_W2R, tw2r) | \
+ FIELD_PREP(ACCCON_WH, twh) | \
+ FIELD_PREP(ACCCON_WST, twst) | \
+ FIELD_PREP(ACCCON_RLT, trlt))
+
+#define MASTER_STA_MASK (MAS_ADDR | MAS_RD | MAS_WR | \
+ MAS_RDDLY)
+#define NFI_RESET_TIMEOUT 1000000
+#define NFI_CORE_TIMEOUT 500000
+#define ECC_ENGINE_TIMEOUT 500000
+
+#define ECC_SECTOR_SIZE 512
+#define ECC_PARITY_BITS 13
+
+#define NFI_FDM_SIZE 8
+
+/* Register base */
+#define NFI_BASE 0x1e003000
+#define NFI_ECC_BASE 0x1e003800
+
+static struct mt7621_nfc nfc_dev;
+
+static const u16 mt7621_nfi_page_size[] = { SZ_512, SZ_2K, SZ_4K };
+static const u8 mt7621_nfi_spare_size[] = { 16, 26, 27, 28 };
+static const u8 mt7621_ecc_strength[] = { 4, 6, 8, 10, 12 };
+
+static inline u32 nfi_read32(struct mt7621_nfc *nfc, u32 reg)
+{
+ return readl(nfc->nfi_regs + reg);
+}
+
+static inline void nfi_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
+{
+ writel(val, nfc->nfi_regs + reg);
+}
+
+static inline u16 nfi_read16(struct mt7621_nfc *nfc, u32 reg)
+{
+ return readw(nfc->nfi_regs + reg);
+}
+
+static inline void nfi_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
+{
+ writew(val, nfc->nfi_regs + reg);
+}
+
+static inline void ecc_write16(struct mt7621_nfc *nfc, u32 reg, u16 val)
+{
+ writew(val, nfc->ecc_regs + reg);
+}
+
+static inline u32 ecc_read32(struct mt7621_nfc *nfc, u32 reg)
+{
+ return readl(nfc->ecc_regs + reg);
+}
+
+static inline void ecc_write32(struct mt7621_nfc *nfc, u32 reg, u32 val)
+{
+ return writel(val, nfc->ecc_regs + reg);
+}
+
+static inline u8 *oob_fdm_ptr(struct nand_chip *nand, int sect)
+{
+ return nand->oob_poi + sect * NFI_FDM_SIZE;
+}
+
+static inline u8 *oob_ecc_ptr(struct mt7621_nfc *nfc, int sect)
+{
+ struct nand_chip *nand = &nfc->nand;
+
+ return nand->oob_poi + nand->ecc.steps * NFI_FDM_SIZE +
+ sect * (nfc->spare_per_sector - NFI_FDM_SIZE);
+}
+
+static inline u8 *page_data_ptr(struct nand_chip *nand, const u8 *buf,
+ int sect)
+{
+ return (u8 *)buf + sect * nand->ecc.size;
+}
+
+static int mt7621_ecc_wait_idle(struct mt7621_nfc *nfc, u32 reg)
+{
+ u32 val;
+ int ret;
+
+ ret = readw_poll_timeout(nfc->ecc_regs + reg, val, val & ECC_IDLE,
+ ECC_ENGINE_TIMEOUT);
+ if (ret) {
+ pr_warn("ECC engine timed out entering idle mode\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt7621_ecc_decoder_wait_done(struct mt7621_nfc *nfc, u32 sect)
+{
+ u32 val;
+ int ret;
+
+ ret = readw_poll_timeout(nfc->ecc_regs + ECC_DECDONE, val,
+ val & (1 << sect), ECC_ENGINE_TIMEOUT);
+ if (ret) {
+ pr_warn("ECC decoder for sector %d timed out\n", sect);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void mt7621_ecc_encoder_op(struct mt7621_nfc *nfc, bool enable)
+{
+ mt7621_ecc_wait_idle(nfc, ECC_ENCIDLE);
+ ecc_write16(nfc, ECC_ENCCON, enable ? ENC_EN : 0);
+}
+
+static void mt7621_ecc_decoder_op(struct mt7621_nfc *nfc, bool enable)
+{
+ mt7621_ecc_wait_idle(nfc, ECC_DECIDLE);
+ ecc_write16(nfc, ECC_DECCON, enable ? DEC_EN : 0);
+}
+
+static int mt7621_ecc_correct_check(struct mt7621_nfc *nfc, u8 *sector_buf,
+ u8 *fdm_buf, u32 sect)
+{
+ struct nand_chip *nand = &nfc->nand;
+ u32 decnum, num_error_bits, fdm_end_bits;
+ u32 error_locations, error_bit_loc;
+ u32 error_byte_pos, error_bit_pos;
+ int bitflips = 0;
+ u32 i;
+
+ decnum = ecc_read32(nfc, ECC_DECENUM);
+ num_error_bits = (decnum >> (sect << ERRNUM_S)) & ERRNUM_M;
+ fdm_end_bits = (nand->ecc.size + NFI_FDM_SIZE) << 3;
+
+ if (!num_error_bits)
+ return 0;
+
+ if (num_error_bits == ERRNUM_M)
+ return -1;
+
+ for (i = 0; i < num_error_bits; i++) {
+ error_locations = ecc_read32(nfc, ECC_DECEL(i / 2));
+ error_bit_loc = (error_locations >> ((i % 2) * DEC_EL_ODD_S)) &
+ DEC_EL_M;
+ error_byte_pos = error_bit_loc >> DEC_EL_BYTE_POS_S;
+ error_bit_pos = error_bit_loc & DEC_EL_BIT_POS_M;
+
+ if (error_bit_loc < (nand->ecc.size << 3)) {
+ if (sector_buf) {
+ sector_buf[error_byte_pos] ^=
+ (1 << error_bit_pos);
+ }
+ } else if (error_bit_loc < fdm_end_bits) {
+ if (fdm_buf) {
+ fdm_buf[error_byte_pos - nand->ecc.size] ^=
+ (1 << error_bit_pos);
+ }
+ }
+
+ bitflips++;
+ }
+
+ return bitflips;
+}
+
+static int mt7621_nfc_wait_write_completion(struct mt7621_nfc *nfc,
+ struct nand_chip *nand)
+{
+ u16 val;
+ int ret;
+
+ ret = readw_poll_timeout(nfc->nfi_regs + NFI_ADDRCNTR, val,
+ FIELD_GET(SEC_CNTR, val) >= nand->ecc.steps,
+ NFI_CORE_TIMEOUT);
+
+ if (ret) {
+ pr_warn("NFI core write operation timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return ret;
+}
+
+static void mt7621_nfc_hw_reset(struct mt7621_nfc *nfc)
+{
+ u32 val;
+ int ret;
+
+ /* reset all registers and force the NFI master to terminate */
+ nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
+
+ /* wait for the master to finish the last transaction */
+ ret = readw_poll_timeout(nfc->nfi_regs + NFI_MASTER_STA, val,
+ !(val & MASTER_STA_MASK), NFI_RESET_TIMEOUT);
+ if (ret) {
+ pr_warn("Failed to reset NFI master in %dms\n",
+ NFI_RESET_TIMEOUT);
+ }
+
+ /* ensure any status register affected by the NFI master is reset */
+ nfi_write16(nfc, NFI_CON, CON_FIFO_FLUSH | CON_NFI_RST);
+ nfi_write16(nfc, NFI_STRDATA, 0);
+}
+
+static inline void mt7621_nfc_hw_init(struct mt7621_nfc *nfc)
+{
+ u32 acccon;
+
+ /*
+ * CNRNB: nand ready/busy register
+ * -------------------------------
+ * 7:4: timeout register for polling the NAND busy/ready signal
+ * 0 : poll the status of the busy/ready signal after [7:4]*16 cycles.
+ */
+ nfi_write16(nfc, NFI_CNRNB, CB2R_TIME | STR_CNRNB);
+
+ mt7621_nfc_hw_reset(nfc);
+
+ /* Apply default access timing */
+ acccon = ACCTIMING(ACCCON_POECS_DEF, ACCCON_PRECS_DEF, ACCCON_C2R_DEF,
+ ACCCON_W2R_DEF, ACCCON_WH_DEF, ACCCON_WST_DEF,
+ ACCCON_RLT_DEF);
+
+ nfi_write32(nfc, NFI_ACCCON, acccon);
+}
+
+static int mt7621_nfc_send_command(struct mt7621_nfc *nfc, u8 command)
+{
+ u32 val;
+ int ret;
+
+ nfi_write32(nfc, NFI_CMD, command);
+
+ ret = readl_poll_timeout(nfc->nfi_regs + NFI_STA, val, !(val & STA_CMD),
+ NFI_CORE_TIMEOUT);
+ if (ret) {
+ pr_warn("NFI core timed out entering command mode\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mt7621_nfc_send_address_byte(struct mt7621_nfc *nfc, int addr)
+{
+ u32 val;
+ int ret;
+
+ nfi_write32(nfc, NFI_COLADDR, addr);
+ nfi_write32(nfc, NFI_ROWADDR, 0);
+ nfi_write16(nfc, NFI_ADDRNOB, 1);
+
+ ret = readl_poll_timeout(nfc->nfi_regs + NFI_STA, val,
+ !(val & STA_ADDR), NFI_CORE_TIMEOUT);
+ if (ret) {
+ pr_warn("NFI core timed out entering address mode\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void mt7621_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+ unsigned int ctrl)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ if (ctrl & NAND_ALE) {
+ mt7621_nfc_send_address_byte(nfc, dat & 0xff);
+ } else if (ctrl & NAND_CLE) {
+ mt7621_nfc_hw_reset(nfc);
+ nfi_write16(nfc, NFI_CNFG,
+ FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM));
+ mt7621_nfc_send_command(nfc, dat);
+ }
+}
+
+static int mt7621_nfc_dev_ready(struct mtd_info *mtd)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ if (nfi_read32(nfc, NFI_STA) & STA_BUSY)
+ return 0;
+
+ return 1;
+}
+
+static void mt7621_nfc_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ nfi_write16(nfc, NFI_CSEL, 0);
+}
+
+static void mt7621_nfc_wait_pio_ready(struct mt7621_nfc *nfc)
+{
+ int ret;
+ u16 val;
+
+ ret = readw_poll_timeout(nfc->nfi_regs + NFI_PIO_DIRDY, val,
+ val & PIO_DIRDY, NFI_CORE_TIMEOUT);
+ if (ret < 0)
+ pr_err("NFI core PIO mode not ready\n");
+}
+
+static u32 mt7621_nfc_pio_read(struct mt7621_nfc *nfc, bool br)
+{
+ u32 reg, fsm;
+
+ /* after each byte read, the NFI_STA reg is reset by the hardware */
+ reg = nfi_read32(nfc, NFI_STA);
+ fsm = FIELD_GET(STA_NFI_FSM, reg);
+
+ if (fsm != STA_FSM_CUSTOM_DATA) {
+ reg = nfi_read16(nfc, NFI_CNFG);
+ reg |= CNFG_READ_MODE | CNFG_BYTE_RW;
+ if (!br)
+ reg &= ~CNFG_BYTE_RW;
+ nfi_write16(nfc, NFI_CNFG, reg);
+
+ /*
+ * set to max sector to allow the HW to continue reading over
+ * unaligned accesses
+ */
+ nfi_write16(nfc, NFI_CON, CON_NFI_SEC | CON_NFI_BRD);
+
+ /* trigger to fetch data */
+ nfi_write16(nfc, NFI_STRDATA, STR_DATA);
+ }
+
+ mt7621_nfc_wait_pio_ready(nfc);
+
+ return nfi_read32(nfc, NFI_DATAR);
+}
+
+static void mt7621_nfc_read_data(struct mt7621_nfc *nfc, u8 *buf, u32 len)
+{
+ while (((uintptr_t)buf & 3) && len) {
+ *buf = mt7621_nfc_pio_read(nfc, true);
+ buf++;
+ len--;
+ }
+
+ while (len >= 4) {
+ *(u32 *)buf = mt7621_nfc_pio_read(nfc, false);
+ buf += 4;
+ len -= 4;
+ }
+
+ while (len) {
+ *buf = mt7621_nfc_pio_read(nfc, true);
+ buf++;
+ len--;
+ }
+}
+
+static void mt7621_nfc_read_data_discard(struct mt7621_nfc *nfc, u32 len)
+{
+ while (len >= 4) {
+ mt7621_nfc_pio_read(nfc, false);
+ len -= 4;
+ }
+
+ while (len) {
+ mt7621_nfc_pio_read(nfc, true);
+ len--;
+ }
+}
+
+static void mt7621_nfc_pio_write(struct mt7621_nfc *nfc, u32 val, bool bw)
+{
+ u32 reg, fsm;
+
+ reg = nfi_read32(nfc, NFI_STA);
+ fsm = FIELD_GET(STA_NFI_FSM, reg);
+
+ if (fsm != STA_FSM_CUSTOM_DATA) {
+ reg = nfi_read16(nfc, NFI_CNFG);
+ reg &= ~(CNFG_READ_MODE | CNFG_BYTE_RW);
+ if (bw)
+ reg |= CNFG_BYTE_RW;
+ nfi_write16(nfc, NFI_CNFG, reg);
+
+ nfi_write16(nfc, NFI_CON, CON_NFI_SEC | CON_NFI_BWR);
+ nfi_write16(nfc, NFI_STRDATA, STR_DATA);
+ }
+
+ mt7621_nfc_wait_pio_ready(nfc);
+ nfi_write32(nfc, NFI_DATAW, val);
+}
+
+static void mt7621_nfc_write_data(struct mt7621_nfc *nfc, const u8 *buf,
+ u32 len)
+{
+ while (((uintptr_t)buf & 3) && len) {
+ mt7621_nfc_pio_write(nfc, *buf, true);
+ buf++;
+ len--;
+ }
+
+ while (len >= 4) {
+ mt7621_nfc_pio_write(nfc, *(const u32 *)buf, false);
+ buf += 4;
+ len -= 4;
+ }
+
+ while (len) {
+ mt7621_nfc_pio_write(nfc, *buf, true);
+ buf++;
+ len--;
+ }
+}
+
+static void mt7621_nfc_write_data_empty(struct mt7621_nfc *nfc, u32 len)
+{
+ while (len >= 4) {
+ mt7621_nfc_pio_write(nfc, 0xffffffff, false);
+ len -= 4;
+ }
+
+ while (len) {
+ mt7621_nfc_pio_write(nfc, 0xff, true);
+ len--;
+ }
+}
+
+static void mt7621_nfc_write_byte(struct mtd_info *mtd, u8 byte)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ mt7621_nfc_pio_write(nfc, byte, true);
+}
+
+static void mt7621_nfc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ return mt7621_nfc_write_data(nfc, buf, len);
+}
+
+static u8 mt7621_nfc_read_byte(struct mtd_info *mtd)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ return mt7621_nfc_pio_read(nfc, true);
+}
+
+static void mt7621_nfc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+ mt7621_nfc_read_data(nfc, buf, len);
+}
+
+static int mt7621_nfc_calc_ecc_strength(struct mt7621_nfc *nfc,
+ u32 avail_ecc_bytes)
+{
+ struct nand_chip *nand = &nfc->nand;
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ u32 strength;
+ int i;
+
+ strength = avail_ecc_bytes * 8 / ECC_PARITY_BITS;
+
+ /* Find the closest supported ecc strength */
+ for (i = ARRAY_SIZE(mt7621_ecc_strength) - 1; i >= 0; i--) {
+ if (mt7621_ecc_strength[i] <= strength)
+ break;
+ }
+
+ if (unlikely(i < 0)) {
+ pr_err("OOB size (%u) is not supported\n", mtd->oobsize);
+ return -EINVAL;
+ }
+
+ nand->ecc.strength = mt7621_ecc_strength[i];
+ nand->ecc.bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
+
+ pr_debug("ECC strength adjusted to %u bits\n", nand->ecc.strength);
+
+ return i;
+}
+
+static int mt7621_nfc_set_spare_per_sector(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ u32 size;
+ int i;
+
+ size = nand->ecc.bytes + NFI_FDM_SIZE;
+
+ /* Find the closest supported spare size */
+ for (i = 0; i < ARRAY_SIZE(mt7621_nfi_spare_size); i++) {
+ if (mt7621_nfi_spare_size[i] >= size)
+ break;
+ }
+
+ if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_spare_size))) {
+ pr_err("OOB size (%u) is not supported\n", mtd->oobsize);
+ return -EINVAL;
+ }
+
+ nfc->spare_per_sector = mt7621_nfi_spare_size[i];
+
+ return i;
+}
+
+static int mt7621_nfc_ecc_init(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ u32 avail_ecc_bytes, encode_block_size, decode_block_size;
+ u32 ecc_enccfg, ecc_deccfg;
+ int ecc_cap;
+
+ nand->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
+
+ nand->ecc.size = ECC_SECTOR_SIZE;
+ nand->ecc.steps = mtd->writesize / nand->ecc.size;
+
+ avail_ecc_bytes = mtd->oobsize / nand->ecc.steps - NFI_FDM_SIZE;
+
+ ecc_cap = mt7621_nfc_calc_ecc_strength(nfc, avail_ecc_bytes);
+ if (ecc_cap < 0)
+ return ecc_cap;
+
+ /* Sector + FDM */
+ encode_block_size = (nand->ecc.size + NFI_FDM_SIZE) * 8;
+ ecc_enccfg = ecc_cap | FIELD_PREP(ENC_MODE, ENC_MODE_NFI) |
+ FIELD_PREP(ENC_CNFG_MSG, encode_block_size);
+
+ /* Sector + FDM + ECC parity bits */
+ decode_block_size = ((nand->ecc.size + NFI_FDM_SIZE) * 8) +
+ nand->ecc.strength * ECC_PARITY_BITS;
+ ecc_deccfg = ecc_cap | FIELD_PREP(DEC_MODE, DEC_MODE_NFI) |
+ FIELD_PREP(DEC_CS, decode_block_size) |
+ FIELD_PREP(DEC_CON, DEC_CON_EL) | DEC_EMPTY_EN;
+
+ mt7621_ecc_encoder_op(nfc, false);
+ ecc_write32(nfc, ECC_ENCCNFG, ecc_enccfg);
+
+ mt7621_ecc_decoder_op(nfc, false);
+ ecc_write32(nfc, ECC_DECCNFG, ecc_deccfg);
+
+ return 0;
+}
+
+static int mt7621_nfc_set_page_format(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ int i, spare_size;
+ u32 pagefmt;
+
+ spare_size = mt7621_nfc_set_spare_per_sector(nfc);
+ if (spare_size < 0)
+ return spare_size;
+
+ for (i = 0; i < ARRAY_SIZE(mt7621_nfi_page_size); i++) {
+ if (mt7621_nfi_page_size[i] == mtd->writesize)
+ break;
+ }
+
+ if (unlikely(i >= ARRAY_SIZE(mt7621_nfi_page_size))) {
+ pr_err("Page size (%u) is not supported\n", mtd->writesize);
+ return -EINVAL;
+ }
+
+ pagefmt = FIELD_PREP(PAGEFMT_PAGE, i) |
+ FIELD_PREP(PAGEFMT_SPARE, spare_size) |
+ FIELD_PREP(PAGEFMT_FDM, NFI_FDM_SIZE) |
+ FIELD_PREP(PAGEFMT_FDM_ECC, NFI_FDM_SIZE);
+
+ nfi_write16(nfc, NFI_PAGEFMT, pagefmt);
+
+ return 0;
+}
+
+static int mt7621_nfc_attach_chip(struct nand_chip *nand)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+ int ret;
+
+ if (nand->options & NAND_BUSWIDTH_16) {
+ pr_err("16-bit buswidth is not supported");
+ return -EINVAL;
+ }
+
+ ret = mt7621_nfc_ecc_init(nfc);
+ if (ret)
+ return ret;
+
+ return mt7621_nfc_set_page_format(nfc);
+}
+
+static void mt7621_nfc_write_fdm(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+ u32 vall, valm;
+ u8 *oobptr;
+ int i, j;
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ vall = 0;
+ valm = 0;
+ oobptr = oob_fdm_ptr(nand, i);
+
+ for (j = 0; j < 4; j++)
+ vall |= (u32)oobptr[j] << (j * 8);
+
+ for (j = 0; j < 4; j++)
+ valm |= (u32)oobptr[j + 4] << (j * 8);
+
+ nfi_write32(nfc, NFI_FDML(i), vall);
+ nfi_write32(nfc, NFI_FDMM(i), valm);
+ }
+}
+
+static void mt7621_nfc_read_sector_fdm(struct mt7621_nfc *nfc, u32 sect)
+{
+ struct nand_chip *nand = &nfc->nand;
+ u32 vall, valm;
+ u8 *oobptr;
+ int i;
+
+ vall = nfi_read32(nfc, NFI_FDML(sect));
+ valm = nfi_read32(nfc, NFI_FDMM(sect));
+ oobptr = oob_fdm_ptr(nand, sect);
+
+ for (i = 0; i < 4; i++)
+ oobptr[i] = (vall >> (i * 8)) & 0xff;
+
+ for (i = 0; i < 4; i++)
+ oobptr[i + 4] = (valm >> (i * 8)) & 0xff;
+}
+
+static int mt7621_nfc_read_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *nand, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+ int bitflips = 0, ret = 0;
+ int rc, i;
+
+ nand_read_page_op(nand, page, 0, NULL, 0);
+
+ nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
+ CNFG_READ_MODE | CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+
+ mt7621_ecc_decoder_op(nfc, true);
+
+ nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+ CON_NFI_BRD);
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ if (buf)
+ mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
+ nand->ecc.size);
+ else
+ mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
+
+ rc = mt7621_ecc_decoder_wait_done(nfc, i);
+
+ mt7621_nfc_read_sector_fdm(nfc, i);
+
+ if (rc < 0) {
+ ret = -EIO;
+ continue;
+ }
+
+ rc = mt7621_ecc_correct_check(nfc,
+ buf ? page_data_ptr(nand, buf, i) : NULL,
+ oob_fdm_ptr(nand, i), i);
+
+ if (rc < 0) {
+ pr_warn("Uncorrectable ECC error at page %d step %d\n",
+ page, i);
+ bitflips = nand->ecc.strength + 1;
+ mtd->ecc_stats.failed++;
+ } else {
+ if (rc > bitflips)
+ bitflips = rc;
+ mtd->ecc_stats.corrected += rc;
+ }
+ }
+
+ mt7621_ecc_decoder_op(nfc, false);
+
+ nfi_write16(nfc, NFI_CON, 0);
+
+ if (ret < 0)
+ return ret;
+
+ return bitflips;
+}
+
+static int mt7621_nfc_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+ int i;
+
+ nand_read_page_op(nand, page, 0, NULL, 0);
+
+ nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
+ CNFG_READ_MODE);
+
+ nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+ CON_NFI_BRD);
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ /* Read data */
+ if (buf)
+ mt7621_nfc_read_data(nfc, page_data_ptr(nand, buf, i),
+ nand->ecc.size);
+ else
+ mt7621_nfc_read_data_discard(nfc, nand->ecc.size);
+
+ /* Read FDM */
+ mt7621_nfc_read_data(nfc, oob_fdm_ptr(nand, i), NFI_FDM_SIZE);
+
+ /* Read ECC parity data */
+ mt7621_nfc_read_data(nfc, oob_ecc_ptr(nfc, i),
+ nfc->spare_per_sector - NFI_FDM_SIZE);
+ }
+
+ nfi_write16(nfc, NFI_CON, 0);
+
+ return 0;
+}
+
+static int mt7621_nfc_read_oob_hwecc(struct mtd_info *mtd,
+ struct nand_chip *nand, int page)
+{
+ return mt7621_nfc_read_page_hwecc(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_read_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, int page)
+{
+ return mt7621_nfc_read_page_raw(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_check_empty_page(struct nand_chip *nand, const u8 *buf)
+{
+ struct mtd_info *mtd = nand_to_mtd(nand);
+ u8 *oobptr;
+ u32 i, j;
+
+ if (buf) {
+ for (i = 0; i < mtd->writesize; i++)
+ if (buf[i] != 0xff)
+ return 0;
+ }
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ oobptr = oob_fdm_ptr(nand, i);
+ for (j = 0; j < NFI_FDM_SIZE; j++)
+ if (oobptr[j] != 0xff)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int mt7621_nfc_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *nand,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+
+ if (mt7621_nfc_check_empty_page(nand, buf)) {
+ /*
+ * MT7621 ECC engine always generates parity code for input
+ * pages, even for empty pages. Doing so will write back ECC
+ * parity code to the oob region, which means such pages will
+ * no longer be empty pages.
+ *
+ * To avoid this, stop write operation if current page is an
+ * empty page.
+ */
+ return 0;
+ }
+
+ nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+
+ nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM) |
+ CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+
+ mt7621_ecc_encoder_op(nfc, true);
+
+ mt7621_nfc_write_fdm(nfc);
+
+ nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+ CON_NFI_BWR);
+
+ if (buf)
+ mt7621_nfc_write_data(nfc, buf, mtd->writesize);
+ else
+ mt7621_nfc_write_data_empty(nfc, mtd->writesize);
+
+ mt7621_nfc_wait_write_completion(nfc, nand);
+
+ mt7621_ecc_encoder_op(nfc, false);
+
+ nfi_write16(nfc, NFI_CON, 0);
+
+ return nand_prog_page_end_op(nand);
+}
+
+static int mt7621_nfc_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *nand,
+ const u8 *buf, int oob_required,
+ int page)
+{
+ struct mt7621_nfc *nfc = nand_get_controller_data(nand);
+ int i;
+
+ nand_prog_page_begin_op(nand, page, 0, NULL, 0);
+
+ nfi_write16(nfc, NFI_CNFG, FIELD_PREP(CNFG_OP_MODE, CNFG_OP_CUSTOM));
+
+ nfi_write16(nfc, NFI_CON, FIELD_PREP(CON_NFI_SEC, nand->ecc.steps) |
+ CON_NFI_BWR);
+
+ for (i = 0; i < nand->ecc.steps; i++) {
+ /* Write data */
+ if (buf)
+ mt7621_nfc_write_data(nfc, page_data_ptr(nand, buf, i),
+ nand->ecc.size);
+ else
+ mt7621_nfc_write_data_empty(nfc, nand->ecc.size);
+
+ /* Write FDM */
+ mt7621_nfc_write_data(nfc, oob_fdm_ptr(nand, i),
+ NFI_FDM_SIZE);
+
+ /* Write dummy ECC parity data */
+ mt7621_nfc_write_data_empty(nfc, nfc->spare_per_sector -
+ NFI_FDM_SIZE);
+ }
+
+ mt7621_nfc_wait_write_completion(nfc, nand);
+
+ nfi_write16(nfc, NFI_CON, 0);
+
+ return nand_prog_page_end_op(nand);
+}
+
+static int mt7621_nfc_write_oob_hwecc(struct mtd_info *mtd,
+ struct nand_chip *nand, int page)
+{
+ return mt7621_nfc_write_page_hwecc(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_write_oob_raw(struct mtd_info *mtd,
+ struct nand_chip *nand, int page)
+{
+ return mt7621_nfc_write_page_raw(mtd, nand, NULL, 1, page);
+}
+
+static int mt7621_nfc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oob_region)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+
+ if (section >= nand->ecc.steps)
+ return -ERANGE;
+
+ oob_region->length = NFI_FDM_SIZE - 1;
+ oob_region->offset = section * NFI_FDM_SIZE + 1;
+
+ return 0;
+}
+
+static int mt7621_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oob_region)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+
+ if (section)
+ return -ERANGE;
+
+ oob_region->offset = NFI_FDM_SIZE * nand->ecc.steps;
+ oob_region->length = mtd->oobsize - oob_region->offset;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops mt7621_nfc_ooblayout_ops = {
+ .rfree = mt7621_nfc_ooblayout_free,
+ .ecc = mt7621_nfc_ooblayout_ecc,
+};
+
+/*
+ * This function will override the default one which is not supposed to be
+ * used for ECC syndrome based pages.
+ */
+static int mt7621_nfc_block_bad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct nand_chip *nand = mtd_to_nand(mtd);
+ struct mtd_oob_ops ops;
+ int ret, i = 0;
+ u16 bad;
+
+ memset(&ops, 0, sizeof(ops));
+ ops.oobbuf = (uint8_t *)&bad;
+ ops.ooboffs = nand->badblockpos;
+ if (nand->options & NAND_BUSWIDTH_16) {
+ ops.ooboffs &= ~0x01;
+ ops.ooblen = 2;
+ } else {
+ ops.ooblen = 1;
+ }
+ ops.mode = MTD_OPS_RAW;
+
+ /* Read from first/last page(s) if necessary */
+ if (nand->bbt_options & NAND_BBT_SCANLASTPAGE)
+ ofs += mtd->erasesize - mtd->writesize;
+
+ do {
+ ret = mtd_read_oob(mtd, ofs, &ops);
+ if (ret)
+ return ret;
+
+ if (likely(nand->badblockbits == 8))
+ ret = bad != 0xFF;
+ else
+ ret = hweight8(bad) < nand->badblockbits;
+
+ i++;
+ ofs += mtd->writesize;
+ } while (!ret && (nand->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+ return ret;
+}
+
+static void mt7621_nfc_init_chip(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+ struct mtd_info *mtd;
+ int ret;
+
+ nand_set_controller_data(nand, nfc);
+
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+ nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
+ nand->ecc.read_page_raw = mt7621_nfc_read_page_raw;
+ nand->ecc.write_page = mt7621_nfc_write_page_hwecc;
+ nand->ecc.write_page_raw = mt7621_nfc_write_page_raw;
+ nand->ecc.read_oob = mt7621_nfc_read_oob_hwecc;
+ nand->ecc.read_oob_raw = mt7621_nfc_read_oob_raw;
+ nand->ecc.write_oob = mt7621_nfc_write_oob_hwecc;
+ nand->ecc.write_oob_raw = mt7621_nfc_write_oob_raw;
+
+ nand->dev_ready = mt7621_nfc_dev_ready;
+ nand->select_chip = mt7621_nfc_select_chip;
+ nand->write_byte = mt7621_nfc_write_byte;
+ nand->write_buf = mt7621_nfc_write_buf;
+ nand->read_byte = mt7621_nfc_read_byte;
+ nand->read_buf = mt7621_nfc_read_buf;
+ nand->cmd_ctrl = mt7621_nfc_cmd_ctrl;
+ nand->block_bad = mt7621_nfc_block_bad;
+
+ mtd = nand_to_mtd(nand);
+ mtd_set_ooblayout(mtd, &mt7621_nfc_ooblayout_ops);
+
+ /* Reset NFI master */
+ mt7621_nfc_hw_init(nfc);
+
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ return;
+
+ mt7621_nfc_attach_chip(nand);
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return;
+
+ nand_register(0, mtd);
+}
+
+static void mt7621_nfc_set_regs(struct mt7621_nfc *nfc)
+{
+ nfc->nfi_regs = (void __iomem *)CKSEG1ADDR(NFI_BASE);
+ nfc->ecc_regs = (void __iomem *)CKSEG1ADDR(NFI_ECC_BASE);
+}
+
+void mt7621_nfc_spl_init(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+
+ mt7621_nfc_set_regs(nfc);
+
+ nand_set_controller_data(nand, nfc);
+
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
+
+ nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+ nand->ecc.read_page = mt7621_nfc_read_page_hwecc;
+
+ nand->dev_ready = mt7621_nfc_dev_ready;
+ nand->select_chip = mt7621_nfc_select_chip;
+ nand->read_byte = mt7621_nfc_read_byte;
+ nand->read_buf = mt7621_nfc_read_buf;
+ nand->cmd_ctrl = mt7621_nfc_cmd_ctrl;
+
+ /* Reset NFI master */
+ mt7621_nfc_hw_init(nfc);
+}
+
+int mt7621_nfc_spl_post_init(struct mt7621_nfc *nfc)
+{
+ struct nand_chip *nand = &nfc->nand;
+ int nand_maf_id, nand_dev_id;
+ struct nand_flash_dev *type;
+
+ type = nand_get_flash_type(&nand->mtd, nand, &nand_maf_id,
+ &nand_dev_id, NULL);
+
+ if (IS_ERR(type))
+ return PTR_ERR(type);
+
+ nand->numchips = 1;
+ nand->mtd.size = nand->chipsize;
+
+ return mt7621_nfc_attach_chip(nand);
+}
+
+void board_nand_init(void)
+{
+ mt7621_nfc_set_regs(&nfc_dev);
+ mt7621_nfc_init_chip(&nfc_dev);
+}
diff --git a/drivers/mtd/nand/raw/mt7621_nand.h b/drivers/mtd/nand/raw/mt7621_nand.h
new file mode 100644
index 0000000000..af4bc55961
--- /dev/null
+++ b/drivers/mtd/nand/raw/mt7621_nand.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#ifndef _MT7621_NAND_H_
+#define _MT7621_NAND_H_
+
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
+#include <linux/mtd/rawnand.h>
+
+struct mt7621_nfc {
+ struct nand_chip nand;
+
+ void __iomem *nfi_regs;
+ void __iomem *ecc_regs;
+
+ u32 spare_per_sector;
+};
+
+/* for SPL */
+void mt7621_nfc_spl_init(struct mt7621_nfc *nfc);
+int mt7621_nfc_spl_post_init(struct mt7621_nfc *nfc);
+
+#endif /* _MT7621_NAND_H_ */
diff --git a/drivers/mtd/nand/raw/mt7621_nand_spl.c b/drivers/mtd/nand/raw/mt7621_nand_spl.c
new file mode 100644
index 0000000000..114fc8b7ce
--- /dev/null
+++ b/drivers/mtd/nand/raw/mt7621_nand_spl.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <image.h>
+#include <malloc.h>
+#include <linux/sizes.h>
+#include <linux/delay.h>
+#include <linux/mtd/rawnand.h>
+#include "mt7621_nand.h"
+
+static struct mt7621_nfc nfc_dev;
+static u8 *buffer;
+static int nand_valid;
+
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ register struct nand_chip *chip = mtd_to_nand(mtd);
+
+ /* Command latch cycle */
+ chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+ if (column != -1 || page_addr != -1) {
+ int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+ /* Serially input address */
+ if (column != -1) {
+ chip->cmd_ctrl(mtd, column, ctrl);
+ ctrl &= ~NAND_CTRL_CHANGE;
+ if (command != NAND_CMD_READID)
+ chip->cmd_ctrl(mtd, column >> 8, ctrl);
+ }
+ if (page_addr != -1) {
+ chip->cmd_ctrl(mtd, page_addr, ctrl);
+ chip->cmd_ctrl(mtd, page_addr >> 8,
+ NAND_NCE | NAND_ALE);
+ if (chip->options & NAND_ROW_ADDR_3)
+ chip->cmd_ctrl(mtd, page_addr >> 16,
+ NAND_NCE | NAND_ALE);
+ }
+ }
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+ /*
+ * Program and erase have their own busy handlers status, sequential
+ * in and status need no delay.
+ */
+ switch (command) {
+ case NAND_CMD_STATUS:
+ case NAND_CMD_READID:
+ case NAND_CMD_SET_FEATURES:
+ return;
+
+ case NAND_CMD_READ0:
+ chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+ }
+
+ /*
+ * Apply this short delay always to ensure that we do wait tWB in
+ * any case on any machine.
+ */
+ ndelay(100);
+
+ nand_wait_ready(mtd);
+}
+
+static int nfc_read_page_hwecc(struct mtd_info *mtd, void *buf,
+ unsigned int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+
+ ret = chip->ecc.read_page(mtd, chip, buf, 1, page);
+ if (ret < 0 || ret > chip->ecc.strength)
+ return -1;
+
+ return 0;
+}
+
+static int nfc_read_oob_hwecc(struct mtd_info *mtd, void *buf, u32 len,
+ unsigned int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ int ret;
+
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, page);
+
+ ret = chip->ecc.read_page(mtd, chip, NULL, 1, page);
+ if (ret < 0)
+ return -1;
+
+ if (len > mtd->oobsize)
+ len = mtd->oobsize;
+
+ memcpy(buf, chip->oob_poi, len);
+
+ return 0;
+}
+
+static int nfc_check_bad_block(struct mtd_info *mtd, unsigned int page)
+{
+ struct nand_chip *chip = mtd_to_nand(mtd);
+ u32 pages_per_block, i = 0;
+ int ret;
+ u8 bad;
+
+ pages_per_block = 1 << (mtd->erasesize_shift - mtd->writesize_shift);
+
+ /* Read from first/last page(s) if necessary */
+ if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) {
+ page += pages_per_block - 1;
+ if (chip->bbt_options & NAND_BBT_SCAN2NDPAGE)
+ page--;
+ }
+
+ do {
+ ret = nfc_read_oob_hwecc(mtd, &bad, 1, page);
+ if (ret)
+ return ret;
+
+ ret = bad != 0xFF;
+
+ i++;
+ page++;
+ } while (!ret && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2);
+
+ return ret;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
+{
+ struct mt7621_nfc *nfc = &nfc_dev;
+ struct nand_chip *chip = &nfc->nand;
+ struct mtd_info *mtd = &chip->mtd;
+ u32 addr, col, page, chksz;
+ bool check_bad = true;
+
+ if (!nand_valid)
+ return -ENODEV;
+
+ while (size) {
+ if (check_bad || !(offs & mtd->erasesize_mask)) {
+ addr = offs & (~mtd->erasesize_mask);
+ page = addr >> mtd->writesize_shift;
+ if (nfc_check_bad_block(mtd, page)) {
+ /* Skip bad block */
+ if (addr >= mtd->size - mtd->erasesize)
+ return -1;
+
+ offs += mtd->erasesize;
+ continue;
+ }
+
+ check_bad = false;
+ }
+
+ col = offs & mtd->writesize_mask;
+ page = offs >> mtd->writesize_shift;
+ chksz = min(mtd->writesize - col, (uint32_t)size);
+
+ if (unlikely(chksz < mtd->writesize)) {
+ /* Not reading a full page */
+ if (nfc_read_page_hwecc(mtd, buffer, page))
+ return -1;
+
+ memcpy(dest, buffer + col, chksz);
+ } else {
+ if (nfc_read_page_hwecc(mtd, dest, page))
+ return -1;
+ }
+
+ dest += chksz;
+ offs += chksz;
+ size -= chksz;
+ }
+
+ return 0;
+}
+
+int nand_default_bbt(struct mtd_info *mtd)
+{
+ return 0;
+}
+
+unsigned long nand_size(void)
+{
+ if (!nand_valid)
+ return 0;
+
+ /* Unlikely that NAND size > 2GBytes */
+ if (nfc_dev.nand.chipsize <= SZ_2G)
+ return nfc_dev.nand.chipsize;
+
+ return SZ_2G;
+}
+
+void nand_deselect(void)
+{
+}
+
+void nand_init(void)
+{
+ struct mtd_info *mtd;
+ struct nand_chip *chip;
+
+ if (nand_valid)
+ return;
+
+ mt7621_nfc_spl_init(&nfc_dev);
+
+ chip = &nfc_dev.nand;
+ mtd = &chip->mtd;
+ chip->cmdfunc = nand_command_lp;
+
+ if (mt7621_nfc_spl_post_init(&nfc_dev))
+ return;
+
+ mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
+ mtd->writesize_shift = ffs(mtd->writesize) - 1;
+ mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
+ mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
+
+ buffer = malloc(mtd->writesize);
+ if (!buffer)
+ return;
+
+ nand_valid = 1;
+}
diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c
index 666ddeb10d..4fe7ee0d36 100644
--- a/drivers/net/mtk_eth.c
+++ b/drivers/net/mtk_eth.c
@@ -145,7 +145,8 @@ enum mtk_switch {
enum mtk_soc {
SOC_MT7623,
SOC_MT7629,
- SOC_MT7622
+ SOC_MT7622,
+ SOC_MT7621
};
struct mtk_eth_priv {
@@ -159,9 +160,10 @@ struct mtk_eth_priv {
void __iomem *fe_base;
void __iomem *gmac_base;
- void __iomem *ethsys_base;
void __iomem *sgmii_base;
+ struct regmap *ethsys_regmap;
+
struct mii_dev *mdio_bus;
int (*mii_read)(struct mtk_eth_priv *priv, u8 phy, u8 reg);
int (*mii_write)(struct mtk_eth_priv *priv, u8 phy, u8 reg, u16 val);
@@ -233,7 +235,12 @@ static void mtk_gmac_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr, u32 set)
static void mtk_ethsys_rmw(struct mtk_eth_priv *priv, u32 reg, u32 clr,
u32 set)
{
- clrsetbits_le32(priv->ethsys_base + reg, clr, set);
+ uint val;
+
+ regmap_read(priv->ethsys_regmap, reg, &val);
+ val &= ~clr;
+ val |= set;
+ regmap_write(priv->ethsys_regmap, reg, val);
}
/* Direct MDIO clause 22/45 access via SoC */
@@ -669,12 +676,18 @@ static int mt7530_pad_clk_setup(struct mtk_eth_priv *priv, int mode)
static int mt7530_setup(struct mtk_eth_priv *priv)
{
u16 phy_addr, phy_val;
- u32 val;
+ u32 val, txdrv;
int i;
- /* Select 250MHz clk for RGMII mode */
- mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG,
- ETHSYS_TRGMII_CLK_SEL362_5, 0);
+ if (priv->soc != SOC_MT7621) {
+ /* Select 250MHz clk for RGMII mode */
+ mtk_ethsys_rmw(priv, ETHSYS_CLKCFG0_REG,
+ ETHSYS_TRGMII_CLK_SEL362_5, 0);
+
+ txdrv = 8;
+ } else {
+ txdrv = 4;
+ }
/* Modify HWTRAP first to allow direct access to internal PHYs */
mt753x_reg_read(priv, HWTRAP_REG, &val);
@@ -732,7 +745,8 @@ static int mt7530_setup(struct mtk_eth_priv *priv)
/* Lower Tx Driving for TRGMII path */
for (i = 0 ; i < NUM_TRGMII_CTRL ; i++)
mt753x_reg_write(priv, MT7530_TRGMII_TD_ODT(i),
- (8 << TD_DM_DRVP_S) | (8 << TD_DM_DRVN_S));
+ (txdrv << TD_DM_DRVP_S) |
+ (txdrv << TD_DM_DRVN_S));
for (i = 0 ; i < NUM_TRGMII_CTRL; i++)
mt753x_reg_rmw(priv, MT7530_TRGMII_RD(i), RD_TAP_M, 16);
@@ -1419,7 +1433,7 @@ static int mtk_eth_of_to_plat(struct udevice *dev)
priv->soc = dev_get_driver_data(dev);
- pdata->iobase = dev_read_addr(dev);
+ pdata->iobase = (phys_addr_t)dev_remap_addr(dev);
/* get corresponding ethsys phandle */
ret = dev_read_phandle_with_args(dev, "mediatek,ethsys", NULL, 0, 0,
@@ -1427,15 +1441,9 @@ static int mtk_eth_of_to_plat(struct udevice *dev)
if (ret)
return ret;
- regmap = syscon_node_to_regmap(args.node);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- priv->ethsys_base = regmap_get_range(regmap, 0);
- if (!priv->ethsys_base) {
- dev_err(dev, "Unable to find ethsys\n");
- return -ENODEV;
- }
+ priv->ethsys_regmap = syscon_node_to_regmap(args.node);
+ if (IS_ERR(priv->ethsys_regmap))
+ return PTR_ERR(priv->ethsys_regmap);
/* Reset controllers */
ret = reset_get_by_name(dev, "fe", &priv->rst_fe);
@@ -1540,6 +1548,7 @@ static const struct udevice_id mtk_eth_ids[] = {
{ .compatible = "mediatek,mt7629-eth", .data = SOC_MT7629 },
{ .compatible = "mediatek,mt7623-eth", .data = SOC_MT7623 },
{ .compatible = "mediatek,mt7622-eth", .data = SOC_MT7622 },
+ { .compatible = "mediatek,mt7621-eth", .data = SOC_MT7621 },
{}
};
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 4a3856d3c2..c25b42c68f 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -266,7 +266,7 @@ config MT76X8_USB_PHY
config PHY_MTK_TPHY
bool "MediaTek T-PHY Driver"
depends on PHY
- depends on ARCH_MEDIATEK
+ depends on ARCH_MEDIATEK || SOC_MT7621
help
MediaTek T-PHY driver supports usb2.0, usb3.0 ports, PCIe and
SATA, and meanwhile supports two version T-PHY which have
diff --git a/drivers/pinctrl/mtmips/Kconfig b/drivers/pinctrl/mtmips/Kconfig
index 844d5b743f..456f3ea25d 100644
--- a/drivers/pinctrl/mtmips/Kconfig
+++ b/drivers/pinctrl/mtmips/Kconfig
@@ -12,6 +12,15 @@ config PINCTRL_MT7620
The driver is controlled by a device tree node which contains
the pin mux functions for each available pin groups.
+config PINCTRL_MT7621
+ bool "MediaTek MT7621 pin control driver"
+ select PINCTRL_MTMIPS
+ depends on SOC_MT7621 && PINCTRL_GENERIC
+ help
+ Support pin multiplexing control on MediaTek MT7621.
+ The driver is controlled by a device tree node which contains
+ the pin mux functions for each available pin groups.
+
config PINCTRL_MT7628
bool "MediaTek MT7628 pin control driver"
select PINCTRL_MTMIPS
diff --git a/drivers/pinctrl/mtmips/Makefile b/drivers/pinctrl/mtmips/Makefile
index ba945a89a7..8fece4f5fa 100644
--- a/drivers/pinctrl/mtmips/Makefile
+++ b/drivers/pinctrl/mtmips/Makefile
@@ -5,4 +5,5 @@ obj-$(CONFIG_PINCTRL_MTMIPS) += pinctrl-mtmips-common.o
# SoC Drivers
obj-$(CONFIG_PINCTRL_MT7620) += pinctrl-mt7620.o
+obj-$(CONFIG_PINCTRL_MT7621) += pinctrl-mt7621.o
obj-$(CONFIG_PINCTRL_MT7628) += pinctrl-mt7628.o
diff --git a/drivers/pinctrl/mtmips/pinctrl-mt7621.c b/drivers/pinctrl/mtmips/pinctrl-mt7621.c
new file mode 100644
index 0000000000..3e98a01bad
--- /dev/null
+++ b/drivers/pinctrl/mtmips/pinctrl-mt7621.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 MediaTek Inc. All rights reserved.
+ *
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#include "pinctrl-mtmips-common.h"
+
+#define SYSC_MAP_SIZE 0x100
+
+#define PAD_UART1_GPIO0_OFS 0x00
+#define PAD_UART3_I2C_OFS 0x04
+#define PAD_UART2_JTAG_OFS 0x08
+#define PAD_PERST_WDT_OFS 0x0c
+#define PAD_RGMII2_MDIO_OFS 0x10
+#define PAD_SDXC_SPI_OFS 0x14
+#define GPIOMODE_OFS 0x18
+#define PAD_BOPT_ESWINT_OFS 0x28
+
+#define ESWINT_SHIFT 20
+#define SDXC_SHIFT 18
+#define SPI_SHIFT 16
+#define RGMII2_SHIFT 15
+#define RGMII1_SHIFT 14
+#define MDIO_SHIFT 12
+#define PERST_SHIFT 10
+#define WDT_SHIFT 8
+#define JTAG_SHIFT 7
+#define UART2_SHIFT 5
+#define UART3_SHIFT 3
+#define I2C_SHIFT 2
+#define UART1_SHIFT 1
+#define GPIO0_SHIFT 0 /* Dummy */
+
+#define GM4_MASK 3
+
+#define E4_E2_M 0x03
+#define E4_E2_S 4
+#define PULL_UP BIT(3)
+#define PULL_DOWN BIT(2)
+#define SMT BIT(1)
+#define SR BIT(0)
+
+struct mt7621_pinctrl_priv {
+ struct mtmips_pinctrl_priv mp;
+};
+
+#if CONFIG_IS_ENABLED(PINMUX)
+static const struct mtmips_pmx_func esw_int_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("esw int", 0),
+};
+
+static const struct mtmips_pmx_func sdxc_grp[] = {
+ FUNC("nand", 2),
+ FUNC("gpio", 1),
+ FUNC("sdxc", 0),
+};
+
+static const struct mtmips_pmx_func spi_grp[] = {
+ FUNC("nand", 2),
+ FUNC("gpio", 1),
+ FUNC("spi", 0),
+};
+
+static const struct mtmips_pmx_func rgmii2_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("rgmii", 0),
+};
+
+static const struct mtmips_pmx_func rgmii1_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("rgmii", 0),
+};
+
+static const struct mtmips_pmx_func mdio_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("mdio", 0),
+};
+
+static const struct mtmips_pmx_func perst_grp[] = {
+ FUNC("refclk", 2),
+ FUNC("gpio", 1),
+ FUNC("pcie reset", 0),
+};
+
+static const struct mtmips_pmx_func wdt_grp[] = {
+ FUNC("refclk", 2),
+ FUNC("gpio", 1),
+ FUNC("wdt rst", 0),
+};
+
+static const struct mtmips_pmx_func jtag_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("jtag", 0),
+};
+
+static const struct mtmips_pmx_func uart2_grp[] = {
+ FUNC("spdif", 3),
+ FUNC("pcm", 2),
+ FUNC("gpio", 1),
+ FUNC("uart", 0),
+};
+
+static const struct mtmips_pmx_func uart3_grp[] = {
+ FUNC("spdif", 3),
+ FUNC("i2s", 2),
+ FUNC("gpio", 1),
+ FUNC("uart", 0),
+};
+
+static const struct mtmips_pmx_func i2c_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("i2c", 0),
+};
+
+static const struct mtmips_pmx_func uart1_grp[] = {
+ FUNC("gpio", 1),
+ FUNC("uart", 0),
+};
+
+static const struct mtmips_pmx_func gpio0_grp[] = {
+ FUNC("gpio", 0),
+};
+
+static const struct mtmips_pmx_group mt7621_pmx_data[] = {
+ GRP_PCONF("esw int", esw_int_grp, GPIOMODE_OFS, ESWINT_SHIFT, 1,
+ PAD_BOPT_ESWINT_OFS, 0),
+ GRP_PCONF("sdxc", sdxc_grp, GPIOMODE_OFS, SDXC_SHIFT, GM4_MASK,
+ PAD_SDXC_SPI_OFS, 16),
+ GRP_PCONF("spi", spi_grp, GPIOMODE_OFS, SPI_SHIFT, GM4_MASK,
+ PAD_SDXC_SPI_OFS, 0),
+ GRP_PCONF("rgmii2", rgmii2_grp, GPIOMODE_OFS, RGMII2_SHIFT, 1,
+ PAD_RGMII2_MDIO_OFS, 16),
+ GRP("rgmii1", rgmii1_grp, GPIOMODE_OFS, RGMII1_SHIFT, 1),
+ GRP_PCONF("mdio", mdio_grp, GPIOMODE_OFS, MDIO_SHIFT, GM4_MASK,
+ PAD_RGMII2_MDIO_OFS, 0),
+ GRP_PCONF("pcie reset", perst_grp, GPIOMODE_OFS, PERST_SHIFT, GM4_MASK,
+ PAD_PERST_WDT_OFS, 16),
+ GRP_PCONF("wdt", wdt_grp, GPIOMODE_OFS, WDT_SHIFT, GM4_MASK,
+ PAD_PERST_WDT_OFS, 0),
+ GRP_PCONF("jtag", jtag_grp, GPIOMODE_OFS, JTAG_SHIFT, 1,
+ PAD_UART2_JTAG_OFS, 16),
+ GRP_PCONF("uart2", uart2_grp, GPIOMODE_OFS, UART2_SHIFT, GM4_MASK,
+ PAD_UART2_JTAG_OFS, 0),
+ GRP_PCONF("uart3", uart3_grp, GPIOMODE_OFS, UART3_SHIFT, GM4_MASK,
+ PAD_UART3_I2C_OFS, 16),
+ GRP_PCONF("i2c", i2c_grp, GPIOMODE_OFS, I2C_SHIFT, 1,
+ PAD_UART3_I2C_OFS, 0),
+ GRP_PCONF("uart1", uart1_grp, GPIOMODE_OFS, UART1_SHIFT, 1,
+ PAD_UART1_GPIO0_OFS, 16),
+ GRP_PCONF("gpio0", gpio0_grp, GPIOMODE_OFS, GPIO0_SHIFT, 1,
+ PAD_UART1_GPIO0_OFS, 0),
+};
+
+static int mt7621_get_groups_count(struct udevice *dev)
+{
+ return ARRAY_SIZE(mt7621_pmx_data);
+}
+
+static const char *mt7621_get_group_name(struct udevice *dev,
+ unsigned int selector)
+{
+ return mt7621_pmx_data[selector].name;
+}
+#endif /* CONFIG_IS_ENABLED(PINMUX) */
+
+#if CONFIG_IS_ENABLED(PINCONF)
+static const struct pinconf_param mt7621_conf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+ { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+ { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+ { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
+};
+
+static const u32 mt7621_pconf_drv_strength_tbl[] = {2, 4, 6, 8};
+
+static int mt7621_pinconf_group_set(struct udevice *dev,
+ unsigned int group_selector,
+ unsigned int param, unsigned int arg)
+{
+ struct mt7621_pinctrl_priv *priv = dev_get_priv(dev);
+ const struct mtmips_pmx_group *grp = &mt7621_pmx_data[group_selector];
+ u32 clr = 0, set = 0;
+ int i;
+
+ if (!grp->pconf_avail)
+ return 0;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ clr = PULL_UP | PULL_DOWN;
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_UP:
+ clr = PULL_DOWN;
+ set = PULL_UP;
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ clr = PULL_UP;
+ set = PULL_DOWN;
+ break;
+
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ if (arg)
+ set = SMT;
+ else
+ clr = SMT;
+ break;
+
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ for (i = 0; i < ARRAY_SIZE(mt7621_pconf_drv_strength_tbl); i++)
+ if (mt7621_pconf_drv_strength_tbl[i] == arg)
+ break;
+
+ if (i >= ARRAY_SIZE(mt7621_pconf_drv_strength_tbl))
+ return -EINVAL;
+
+ clr = E4_E2_M << E4_E2_S;
+ set = i << E4_E2_S;
+ break;
+
+ case PIN_CONFIG_SLEW_RATE:
+ if (arg)
+ set = SR;
+ else
+ clr = SR;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ mtmips_pinctrl_reg_set(&priv->mp, grp->pconf_reg, grp->pconf_shift,
+ clr, set);
+
+ return 0;
+}
+#endif
+
+static int mt7621_pinctrl_probe(struct udevice *dev)
+{
+ struct mt7621_pinctrl_priv *priv = dev_get_priv(dev);
+ int ret = 0;
+
+#if CONFIG_IS_ENABLED(PINMUX)
+ ret = mtmips_pinctrl_probe(&priv->mp, ARRAY_SIZE(mt7621_pmx_data),
+ mt7621_pmx_data);
+#endif /* CONFIG_IS_ENABLED(PINMUX) */
+
+ return ret;
+}
+
+static int mt7621_pinctrl_of_to_plat(struct udevice *dev)
+{
+ struct mt7621_pinctrl_priv *priv = dev_get_priv(dev);
+
+ priv->mp.base = (void __iomem *)dev_remap_addr_index(dev, 0);
+
+ if (!priv->mp.base)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct pinctrl_ops mt7621_pinctrl_ops = {
+#if CONFIG_IS_ENABLED(PINMUX)
+ .get_groups_count = mt7621_get_groups_count,
+ .get_group_name = mt7621_get_group_name,
+ .get_functions_count = mtmips_get_functions_count,
+ .get_function_name = mtmips_get_function_name,
+ .pinmux_group_set = mtmips_pinmux_group_set,
+#endif /* CONFIG_IS_ENABLED(PINMUX) */
+#if CONFIG_IS_ENABLED(PINCONF)
+ .pinconf_num_params = ARRAY_SIZE(mt7621_conf_params),
+ .pinconf_params = mt7621_conf_params,
+ .pinconf_group_set = mt7621_pinconf_group_set,
+#endif /* CONFIG_IS_ENABLED(PINCONF) */
+ .set_state = pinctrl_generic_set_state,
+};
+
+static const struct udevice_id mt7621_pinctrl_ids[] = {
+ { .compatible = "mediatek,mt7621-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(mt7621_pinctrl) = {
+ .name = "mt7621-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = mt7621_pinctrl_ids,
+ .of_to_plat = mt7621_pinctrl_of_to_plat,
+ .ops = &mt7621_pinctrl_ops,
+ .probe = mt7621_pinctrl_probe,
+ .priv_auto = sizeof(struct mt7621_pinctrl_priv),
+};
diff --git a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c
index e361916eb2..869b781068 100644
--- a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c
+++ b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.c
@@ -13,8 +13,8 @@
#include "pinctrl-mtmips-common.h"
-static void mtmips_pinctrl_reg_set(struct mtmips_pinctrl_priv *priv,
- u32 reg, u32 shift, u32 mask, u32 value)
+void mtmips_pinctrl_reg_set(struct mtmips_pinctrl_priv *priv,
+ u32 reg, u32 shift, u32 mask, u32 value)
{
u32 val;
diff --git a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h
index b51d8f009c..1f1023ef42 100644
--- a/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h
+++ b/drivers/pinctrl/mtmips/pinctrl-mtmips-common.h
@@ -22,6 +22,10 @@ struct mtmips_pmx_group {
u32 shift;
char mask;
+ int pconf_avail;
+ u32 pconf_reg;
+ u32 pconf_shift;
+
int nfuncs;
const struct mtmips_pmx_func *funcs;
};
@@ -42,6 +46,14 @@ struct mtmips_pinctrl_priv {
{ .name = (_name), .reg = (_reg), .shift = (_shift), .mask = (_mask), \
.funcs = (_funcs), .nfuncs = ARRAY_SIZE(_funcs) }
+#define GRP_PCONF(_name, _funcs, _reg, _shift, _mask, _pconf_reg, _pconf_shift) \
+ { .name = (_name), .reg = (_reg), .shift = (_shift), .mask = (_mask), \
+ .funcs = (_funcs), .nfuncs = ARRAY_SIZE(_funcs), .pconf_avail = 1, \
+ .pconf_reg = (_pconf_reg), .pconf_shift = (_pconf_shift) }
+
+void mtmips_pinctrl_reg_set(struct mtmips_pinctrl_priv *priv,
+ u32 reg, u32 shift, u32 mask, u32 value);
+
int mtmips_get_functions_count(struct udevice *dev);
const char *mtmips_get_function_name(struct udevice *dev,
unsigned int selector);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 766d5636c0..75b794548b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -254,7 +254,7 @@ config MT7620_SPI
config MT7621_SPI
bool "MediaTek MT7621 SPI driver"
- depends on SOC_MT7628
+ depends on SOC_MT7621 || SOC_MT7628
help
Enable the MT7621 SPI driver. This driver can be used to access
the SPI NOR flash on platforms embedding this Ralink / MediaTek
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 31ae9f74e7..c3b97f48f0 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -42,7 +42,7 @@ config USB_XHCI_EXYNOS
config USB_XHCI_MTK
bool "Support for MediaTek on-chip xHCI USB controller"
- depends on ARCH_MEDIATEK
+ depends on ARCH_MEDIATEK || SOC_MT7621
help
Enables support for the on-chip xHCI controller on MediaTek SoCs.
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 532ada89c1..6043fe717f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -191,7 +191,7 @@ config WDT_MT7620
config WDT_MT7621
bool "MediaTek MT7621 watchdog timer support"
- depends on WDT && SOC_MT7628
+ depends on WDT && (SOC_MT7621 || SOC_MT7628)
help
Select this to enable Ralink / Mediatek watchdog timer,
which can be found on some MediaTek chips.