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/i2c/stm32f7_i2c.c5
-rw-r--r--drivers/misc/stm32_rcc.c1
-rw-r--r--drivers/mmc/mtk-sd.c13
-rw-r--r--drivers/mmc/stm32_sdmmc2.c224
-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/rng/stm32mp1_rng.c61
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/usb/host/Kconfig2
-rw-r--r--drivers/watchdog/Kconfig2
23 files changed, 2317 insertions, 153 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/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c
index c6ae65badb..bf2a6c9b4b 100644
--- a/drivers/i2c/stm32f7_i2c.c
+++ b/drivers/i2c/stm32f7_i2c.c
@@ -267,6 +267,10 @@ static const struct stm32_i2c_data stm32mp15_data = {
.fmp_clr_offset = 0x40,
};
+static const struct stm32_i2c_data stm32mp13_data = {
+ .fmp_clr_offset = 0x4,
+};
+
static int stm32_i2c_check_device_busy(struct stm32_i2c_priv *i2c_priv)
{
struct stm32_i2c_regs *regs = i2c_priv->regs;
@@ -957,6 +961,7 @@ static const struct dm_i2c_ops stm32_i2c_ops = {
static const struct udevice_id stm32_i2c_of_match[] = {
{ .compatible = "st,stm32f7-i2c", .data = (ulong)&stm32f7_data },
{ .compatible = "st,stm32mp15-i2c", .data = (ulong)&stm32mp15_data },
+ { .compatible = "st,stm32mp13-i2c", .data = (ulong)&stm32mp13_data },
{}
};
diff --git a/drivers/misc/stm32_rcc.c b/drivers/misc/stm32_rcc.c
index b816503bfa..c1e5428a6b 100644
--- a/drivers/misc/stm32_rcc.c
+++ b/drivers/misc/stm32_rcc.c
@@ -84,6 +84,7 @@ static const struct udevice_id stm32_rcc_ids[] = {
{.compatible = "st,stm32f746-rcc", .data = (ulong)&stm32_rcc_clk_f7 },
{.compatible = "st,stm32h743-rcc", .data = (ulong)&stm32_rcc_clk_h7 },
{.compatible = "st,stm32mp1-rcc", .data = (ulong)&stm32_rcc_clk_mp1 },
+ {.compatible = "st,stm32mp1-rcc-secure", .data = (ulong)&stm32_rcc_clk_mp1 },
{.compatible = "st,stm32mp13-rcc", .data = (ulong)&stm32_rcc_clk_mp13 },
{ }
};
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/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c
index 81b07609a9..bfce8a2e4a 100644
--- a/drivers/mmc/stm32_sdmmc2.c
+++ b/drivers/mmc/stm32_sdmmc2.c
@@ -30,9 +30,6 @@
struct stm32_sdmmc2_plat {
struct mmc_config cfg;
struct mmc mmc;
-};
-
-struct stm32_sdmmc2_priv {
fdt_addr_t base;
struct clk clk;
struct reset_ctl reset_ctl;
@@ -208,7 +205,7 @@ static void stm32_sdmmc2_start_data(struct udevice *dev,
struct mmc_data *data,
struct stm32_sdmmc2_ctx *ctx)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
u32 data_ctrl, idmabase0;
/* Configure the SDMMC DPSM (Data Path State Machine) */
@@ -224,10 +221,10 @@ static void stm32_sdmmc2_start_data(struct udevice *dev,
}
/* Set the SDMMC DataLength value */
- writel(ctx->data_length, priv->base + SDMMC_DLEN);
+ writel(ctx->data_length, plat->base + SDMMC_DLEN);
/* Write to SDMMC DCTRL */
- writel(data_ctrl, priv->base + SDMMC_DCTRL);
+ writel(data_ctrl, plat->base + SDMMC_DCTRL);
/* Cache align */
ctx->cache_start = rounddown(idmabase0, ARCH_DMA_MINALIGN);
@@ -242,19 +239,19 @@ static void stm32_sdmmc2_start_data(struct udevice *dev,
flush_dcache_range(ctx->cache_start, ctx->cache_end);
/* Enable internal DMA */
- writel(idmabase0, priv->base + SDMMC_IDMABASE0);
- writel(SDMMC_IDMACTRL_IDMAEN, priv->base + SDMMC_IDMACTRL);
+ writel(idmabase0, plat->base + SDMMC_IDMABASE0);
+ writel(SDMMC_IDMACTRL_IDMAEN, plat->base + SDMMC_IDMACTRL);
}
static void stm32_sdmmc2_start_cmd(struct udevice *dev,
struct mmc_cmd *cmd, u32 cmd_param,
struct stm32_sdmmc2_ctx *ctx)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
u32 timeout = 0;
- if (readl(priv->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN)
- writel(0, priv->base + SDMMC_CMD);
+ if (readl(plat->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN)
+ writel(0, plat->base + SDMMC_CMD);
cmd_param |= cmd->cmdidx | SDMMC_CMD_CPSMEN;
if (cmd->resp_type & MMC_RSP_PRESENT) {
@@ -277,30 +274,30 @@ static void stm32_sdmmc2_start_cmd(struct udevice *dev,
if (ctx->data_length) {
timeout = SDMMC_CMD_TIMEOUT;
} else {
- writel(0, priv->base + SDMMC_DCTRL);
+ writel(0, plat->base + SDMMC_DCTRL);
if (cmd->resp_type & MMC_RSP_BUSY)
timeout = SDMMC_CMD_TIMEOUT;
}
/* Set the SDMMC Data TimeOut value */
- writel(timeout, priv->base + SDMMC_DTIMER);
+ writel(timeout, plat->base + SDMMC_DTIMER);
/* Clear flags */
- writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
+ writel(SDMMC_ICR_STATIC_FLAGS, plat->base + SDMMC_ICR);
/* Set SDMMC argument value */
- writel(cmd->cmdarg, priv->base + SDMMC_ARG);
+ writel(cmd->cmdarg, plat->base + SDMMC_ARG);
/* Set SDMMC command parameters */
- writel(cmd_param, priv->base + SDMMC_CMD);
+ writel(cmd_param, plat->base + SDMMC_CMD);
}
static int stm32_sdmmc2_end_cmd(struct udevice *dev,
struct mmc_cmd *cmd,
struct stm32_sdmmc2_ctx *ctx)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
u32 mask = SDMMC_STA_CTIMEOUT;
u32 status;
int ret;
@@ -314,7 +311,7 @@ static int stm32_sdmmc2_end_cmd(struct udevice *dev,
}
/* Polling status register */
- ret = readl_poll_timeout(priv->base + SDMMC_STA, status, status & mask,
+ ret = readl_poll_timeout(plat->base + SDMMC_STA, status, status & mask,
10000);
if (ret < 0) {
@@ -339,11 +336,11 @@ static int stm32_sdmmc2_end_cmd(struct udevice *dev,
}
if (status & SDMMC_STA_CMDREND && cmd->resp_type & MMC_RSP_PRESENT) {
- cmd->response[0] = readl(priv->base + SDMMC_RESP1);
+ cmd->response[0] = readl(plat->base + SDMMC_RESP1);
if (cmd->resp_type & MMC_RSP_136) {
- cmd->response[1] = readl(priv->base + SDMMC_RESP2);
- cmd->response[2] = readl(priv->base + SDMMC_RESP3);
- cmd->response[3] = readl(priv->base + SDMMC_RESP4);
+ cmd->response[1] = readl(plat->base + SDMMC_RESP2);
+ cmd->response[2] = readl(plat->base + SDMMC_RESP3);
+ cmd->response[3] = readl(plat->base + SDMMC_RESP4);
}
/* Wait for BUSYD0END flag if busy status is detected */
@@ -352,7 +349,7 @@ static int stm32_sdmmc2_end_cmd(struct udevice *dev,
mask = SDMMC_STA_DTIMEOUT | SDMMC_STA_BUSYD0END;
/* Polling status register */
- ret = readl_poll_timeout(priv->base + SDMMC_STA,
+ ret = readl_poll_timeout(plat->base + SDMMC_STA,
status, status & mask,
SDMMC_BUSYD0END_TIMEOUT_US);
@@ -380,7 +377,7 @@ static int stm32_sdmmc2_end_data(struct udevice *dev,
struct mmc_data *data,
struct stm32_sdmmc2_ctx *ctx)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
u32 mask = SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT |
SDMMC_STA_IDMATE | SDMMC_STA_DATAEND;
u32 status;
@@ -390,9 +387,9 @@ static int stm32_sdmmc2_end_data(struct udevice *dev,
else
mask |= SDMMC_STA_TXUNDERR;
- status = readl(priv->base + SDMMC_STA);
+ status = readl(plat->base + SDMMC_STA);
while (!(status & mask))
- status = readl(priv->base + SDMMC_STA);
+ status = readl(plat->base + SDMMC_STA);
/*
* Need invalidate the dcache again to avoid any
@@ -404,7 +401,7 @@ static int stm32_sdmmc2_end_data(struct udevice *dev,
if (status & SDMMC_STA_DCRCFAIL) {
dev_dbg(dev, "error SDMMC_STA_DCRCFAIL (0x%x) for cmd %d\n",
status, cmd->cmdidx);
- if (readl(priv->base + SDMMC_DCOUNT))
+ if (readl(plat->base + SDMMC_DCOUNT))
ctx->dpsm_abort = true;
return -EILSEQ;
}
@@ -443,7 +440,7 @@ static int stm32_sdmmc2_end_data(struct udevice *dev,
static int stm32_sdmmc2_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
struct mmc_data *data)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
struct stm32_sdmmc2_ctx ctx;
u32 cmdat = data ? SDMMC_CMD_CMDTRANS : 0;
int ret, retry = 3;
@@ -470,9 +467,9 @@ retry_cmd:
ret = stm32_sdmmc2_end_data(dev, cmd, data, &ctx);
/* Clear flags */
- writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
+ writel(SDMMC_ICR_STATIC_FLAGS, plat->base + SDMMC_ICR);
if (data)
- writel(0x0, priv->base + SDMMC_IDMACTRL);
+ writel(0x0, plat->base + SDMMC_IDMACTRL);
/*
* To stop Data Path State Machine, a stop_transmission command
@@ -493,7 +490,7 @@ retry_cmd:
SDMMC_CMD_CMDSTOP, &ctx);
stm32_sdmmc2_end_cmd(dev, &stop_cmd, &ctx);
- writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
+ writel(SDMMC_ICR_STATIC_FLAGS, plat->base + SDMMC_ICR);
}
if ((ret != -ETIMEDOUT) && (ret != 0) && retry) {
@@ -512,17 +509,17 @@ retry_cmd:
* This will reset the SDMMC to the reset state and the CPSM and DPSM
* to the Idle state. SDMMC is disabled, Signals Hiz.
*/
-static void stm32_sdmmc2_reset(struct stm32_sdmmc2_priv *priv)
+static void stm32_sdmmc2_reset(struct stm32_sdmmc2_plat *plat)
{
- if (reset_valid(&priv->reset_ctl)) {
+ if (reset_valid(&plat->reset_ctl)) {
/* Reset */
- reset_assert(&priv->reset_ctl);
+ reset_assert(&plat->reset_ctl);
udelay(2);
- reset_deassert(&priv->reset_ctl);
+ reset_deassert(&plat->reset_ctl);
}
/* init the needed SDMMC register after reset */
- writel(priv->pwr_reg_msk, priv->base + SDMMC_POWER);
+ writel(plat->pwr_reg_msk, plat->base + SDMMC_POWER);
}
/*
@@ -531,13 +528,13 @@ static void stm32_sdmmc2_reset(struct stm32_sdmmc2_priv *priv)
* SDMMC_CMD and SDMMC_CK are driven low, to prevent the card from being
* supplied through the signal lines.
*/
-static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_priv *priv)
+static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_plat *plat)
{
- if ((readl(priv->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK) ==
+ if ((readl(plat->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK) ==
SDMMC_POWER_PWRCTRL_CYCLE)
return;
- stm32_sdmmc2_reset(priv);
+ stm32_sdmmc2_reset(plat);
}
/*
@@ -546,10 +543,10 @@ static void stm32_sdmmc2_pwrcycle(struct stm32_sdmmc2_priv *priv)
* Reset => Power-Cycle => Power-Off => Power
* PWRCTRL=10 PWCTRL=00 PWCTRL=11
*/
-static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
+static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_plat *plat)
{
u32 pwrctrl =
- readl(priv->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK;
+ readl(plat->base + SDMMC_POWER) & SDMMC_POWER_PWRCTRL_MASK;
if (pwrctrl == SDMMC_POWER_PWRCTRL_ON)
return;
@@ -558,21 +555,21 @@ static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
* it is the reset state here = the only managed by the driver
*/
if (pwrctrl == SDMMC_POWER_PWRCTRL_OFF) {
- writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk,
- priv->base + SDMMC_POWER);
+ writel(SDMMC_POWER_PWRCTRL_CYCLE | plat->pwr_reg_msk,
+ plat->base + SDMMC_POWER);
}
/*
* the remaining case is SDMMC_POWER_PWRCTRL_CYCLE
* switch to Power-Off state: SDMCC disable, signals drive 1
*/
- writel(SDMMC_POWER_PWRCTRL_OFF | priv->pwr_reg_msk,
- priv->base + SDMMC_POWER);
+ writel(SDMMC_POWER_PWRCTRL_OFF | plat->pwr_reg_msk,
+ plat->base + SDMMC_POWER);
/* After the 1ms delay set the SDMMC to power-on */
mdelay(1);
- writel(SDMMC_POWER_PWRCTRL_ON | priv->pwr_reg_msk,
- priv->base + SDMMC_POWER);
+ writel(SDMMC_POWER_PWRCTRL_ON | plat->pwr_reg_msk,
+ plat->base + SDMMC_POWER);
/* during the first 74 SDMMC_CK cycles the SDMMC is still disabled. */
}
@@ -581,18 +578,18 @@ static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
static int stm32_sdmmc2_set_ios(struct udevice *dev)
{
struct mmc *mmc = mmc_get_mmc_dev(dev);
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
u32 desired = mmc->clock;
- u32 sys_clock = clk_get_rate(&priv->clk);
+ u32 sys_clock = clk_get_rate(&plat->clk);
u32 clk = 0;
dev_dbg(dev, "bus_with = %d, clock = %d\n",
mmc->bus_width, mmc->clock);
if (mmc->clk_disable)
- stm32_sdmmc2_pwrcycle(priv);
+ stm32_sdmmc2_pwrcycle(plat);
else
- stm32_sdmmc2_pwron(priv);
+ stm32_sdmmc2_pwron(plat);
/*
* clk_div = 0 => command and data generated on SDMMCCLK falling edge
@@ -602,7 +599,7 @@ static int stm32_sdmmc2_set_ios(struct udevice *dev)
* SDMMCCLK falling edge
*/
if (desired && ((sys_clock > desired) ||
- IS_RISING_EDGE(priv->clk_reg_msk))) {
+ IS_RISING_EDGE(plat->clk_reg_msk))) {
clk = DIV_ROUND_UP(sys_clock, 2 * desired);
if (clk > SDMMC_CLKCR_CLKDIV_MAX)
clk = SDMMC_CLKCR_CLKDIV_MAX;
@@ -613,30 +610,30 @@ static int stm32_sdmmc2_set_ios(struct udevice *dev)
if (mmc->bus_width == 8)
clk |= SDMMC_CLKCR_WIDBUS_8;
- writel(clk | priv->clk_reg_msk | SDMMC_CLKCR_HWFC_EN,
- priv->base + SDMMC_CLKCR);
+ writel(clk | plat->clk_reg_msk | SDMMC_CLKCR_HWFC_EN,
+ plat->base + SDMMC_CLKCR);
return 0;
}
static int stm32_sdmmc2_getcd(struct udevice *dev)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
dev_dbg(dev, "%s called\n", __func__);
- if (dm_gpio_is_valid(&priv->cd_gpio))
- return dm_gpio_get_value(&priv->cd_gpio);
+ if (dm_gpio_is_valid(&plat->cd_gpio))
+ return dm_gpio_get_value(&plat->cd_gpio);
return 1;
}
static int stm32_sdmmc2_host_power_cycle(struct udevice *dev)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
- writel(SDMMC_POWER_PWRCTRL_CYCLE | priv->pwr_reg_msk,
- priv->base + SDMMC_POWER);
+ writel(SDMMC_POWER_PWRCTRL_CYCLE | plat->pwr_reg_msk,
+ plat->base + SDMMC_POWER);
return 0;
}
@@ -648,20 +645,55 @@ static const struct dm_mmc_ops stm32_sdmmc2_ops = {
.host_power_cycle = stm32_sdmmc2_host_power_cycle,
};
+static int stm32_sdmmc2_of_to_plat(struct udevice *dev)
+{
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
+ struct mmc_config *cfg = &plat->cfg;
+ int ret;
+
+ plat->base = dev_read_addr(dev);
+ if (plat->base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ if (dev_read_bool(dev, "st,neg-edge"))
+ plat->clk_reg_msk |= SDMMC_CLKCR_NEGEDGE;
+ if (dev_read_bool(dev, "st,sig-dir"))
+ plat->pwr_reg_msk |= SDMMC_POWER_DIRPOL;
+ if (dev_read_bool(dev, "st,use-ckin"))
+ plat->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN;
+
+ cfg->f_min = 400000;
+ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+ cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+ cfg->name = "STM32 SD/MMC";
+ cfg->host_caps = 0;
+ cfg->f_max = 52000000;
+ ret = mmc_of_parse(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_index(dev, 0, &plat->clk);
+ if (ret)
+ return ret;
+
+ ret = reset_get_by_index(dev, 0, &plat->reset_ctl);
+ if (ret)
+ dev_dbg(dev, "No reset provided\n");
+
+ gpio_request_by_name(dev, "cd-gpios", 0, &plat->cd_gpio,
+ GPIOD_IS_IN);
+
+ return 0;
+}
+
static int stm32_sdmmc2_probe_level_translator(struct udevice *dev)
{
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
+ struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
struct gpio_desc cmd_gpio;
struct gpio_desc ck_gpio;
struct gpio_desc ckin_gpio;
int clk_hi, clk_lo, ret;
- /*
- * Assume the level translator is present if st,use-ckin is set.
- * This is to cater for DTs which do not implement this test.
- */
- priv->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN;
-
ret = gpio_request_by_name(dev, "st,cmd-gpios", 0, &cmd_gpio,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret)
@@ -694,7 +726,7 @@ static int stm32_sdmmc2_probe_level_translator(struct udevice *dev)
/* Level translator is present if CK signal is propagated to CKIN */
if (!clk_hi || clk_lo)
- priv->clk_reg_msk &= ~SDMMC_CLKCR_SELCLKRX_CKIN;
+ plat->clk_reg_msk &= ~SDMMC_CLKCR_SELCLKRX_CKIN;
dm_gpio_free(dev, &ckin_gpio);
@@ -712,58 +744,26 @@ static int stm32_sdmmc2_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
- struct stm32_sdmmc2_priv *priv = dev_get_priv(dev);
- struct mmc_config *cfg = &plat->cfg;
int ret;
- priv->base = dev_read_addr(dev);
- if (priv->base == FDT_ADDR_T_NONE)
- return -EINVAL;
-
- if (dev_read_bool(dev, "st,neg-edge"))
- priv->clk_reg_msk |= SDMMC_CLKCR_NEGEDGE;
- if (dev_read_bool(dev, "st,sig-dir"))
- priv->pwr_reg_msk |= SDMMC_POWER_DIRPOL;
- if (dev_read_bool(dev, "st,use-ckin"))
- stm32_sdmmc2_probe_level_translator(dev);
-
- ret = clk_get_by_index(dev, 0, &priv->clk);
- if (ret)
+ ret = clk_enable(&plat->clk);
+ if (ret) {
+ clk_free(&plat->clk);
return ret;
-
- ret = clk_enable(&priv->clk);
- if (ret)
- goto clk_free;
-
- ret = reset_get_by_index(dev, 0, &priv->reset_ctl);
- if (ret)
- dev_dbg(dev, "No reset provided\n");
-
- gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio,
- GPIOD_IS_IN);
-
- cfg->f_min = 400000;
- cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
- cfg->name = "STM32 SD/MMC";
-
- cfg->host_caps = 0;
- cfg->f_max = 52000000;
- mmc_of_parse(dev, cfg);
+ }
upriv->mmc = &plat->mmc;
- /* SDMMC init */
- stm32_sdmmc2_reset(priv);
- return 0;
+ if (plat->clk_reg_msk & SDMMC_CLKCR_SELCLKRX_CKIN)
+ stm32_sdmmc2_probe_level_translator(dev);
-clk_free:
- clk_free(&priv->clk);
+ /* SDMMC init */
+ stm32_sdmmc2_reset(plat);
- return ret;
+ return 0;
}
-static int stm32_sdmmc_bind(struct udevice *dev)
+static int stm32_sdmmc2_bind(struct udevice *dev)
{
struct stm32_sdmmc2_plat *plat = dev_get_plat(dev);
@@ -781,7 +781,7 @@ U_BOOT_DRIVER(stm32_sdmmc2) = {
.of_match = stm32_sdmmc2_ids,
.ops = &stm32_sdmmc2_ops,
.probe = stm32_sdmmc2_probe,
- .bind = stm32_sdmmc_bind,
- .priv_auto = sizeof(struct stm32_sdmmc2_priv),
+ .bind = stm32_sdmmc2_bind,
+ .of_to_plat = stm32_sdmmc2_of_to_plat,
.plat_auto = sizeof(struct stm32_sdmmc2_plat),
};
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/rng/stm32mp1_rng.c b/drivers/rng/stm32mp1_rng.c
index 8ea00e3e89..89da78c6c8 100644
--- a/drivers/rng/stm32mp1_rng.c
+++ b/drivers/rng/stm32mp1_rng.c
@@ -18,22 +18,28 @@
#include <linux/iopoll.h>
#include <linux/kernel.h>
-#define RNG_CR 0x00
-#define RNG_CR_RNGEN BIT(2)
-#define RNG_CR_CED BIT(5)
+#define RNG_CR 0x00
+#define RNG_CR_RNGEN BIT(2)
+#define RNG_CR_CED BIT(5)
+#define RNG_CR_CONDRST BIT(30)
-#define RNG_SR 0x04
-#define RNG_SR_SEIS BIT(6)
-#define RNG_SR_CEIS BIT(5)
-#define RNG_SR_SECS BIT(2)
-#define RNG_SR_DRDY BIT(0)
+#define RNG_SR 0x04
+#define RNG_SR_SEIS BIT(6)
+#define RNG_SR_CEIS BIT(5)
+#define RNG_SR_SECS BIT(2)
+#define RNG_SR_DRDY BIT(0)
-#define RNG_DR 0x08
+#define RNG_DR 0x08
+
+struct stm32_rng_data {
+ bool has_cond_reset;
+};
struct stm32_rng_plat {
fdt_addr_t base;
struct clk clk;
struct reset_ctl rst;
+ const struct stm32_rng_data *data;
};
static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
@@ -83,18 +89,36 @@ static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
static int stm32_rng_init(struct stm32_rng_plat *pdata)
{
int err;
+ u32 cr, sr;
err = clk_enable(&pdata->clk);
if (err)
return err;
+ cr = readl(pdata->base + RNG_CR);
+
/* Disable CED */
- writel(RNG_CR_RNGEN | RNG_CR_CED, pdata->base + RNG_CR);
+ cr |= RNG_CR_CED;
+ if (pdata->data->has_cond_reset) {
+ cr |= RNG_CR_CONDRST;
+ writel(cr, pdata->base + RNG_CR);
+ cr &= ~RNG_CR_CONDRST;
+ writel(cr, pdata->base + RNG_CR);
+ err = readl_poll_timeout(pdata->base + RNG_CR, cr,
+ (!(cr & RNG_CR_CONDRST)), 10000);
+ if (err)
+ return err;
+ }
/* clear error indicators */
writel(0, pdata->base + RNG_SR);
- return 0;
+ cr |= RNG_CR_RNGEN;
+ writel(cr, pdata->base + RNG_CR);
+
+ err = readl_poll_timeout(pdata->base + RNG_SR, sr,
+ sr & RNG_SR_DRDY, 10000);
+ return err;
}
static int stm32_rng_cleanup(struct stm32_rng_plat *pdata)
@@ -108,6 +132,8 @@ static int stm32_rng_probe(struct udevice *dev)
{
struct stm32_rng_plat *pdata = dev_get_plat(dev);
+ pdata->data = (struct stm32_rng_data *)dev_get_driver_data(dev);
+
reset_assert(&pdata->rst);
udelay(20);
reset_deassert(&pdata->rst);
@@ -146,10 +172,17 @@ static const struct dm_rng_ops stm32_rng_ops = {
.read = stm32_rng_read,
};
+static const struct stm32_rng_data stm32mp13_rng_data = {
+ .has_cond_reset = true,
+};
+
+static const struct stm32_rng_data stm32_rng_data = {
+ .has_cond_reset = false,
+};
+
static const struct udevice_id stm32_rng_match[] = {
- {
- .compatible = "st,stm32-rng",
- },
+ {.compatible = "st,stm32mp13-rng", .data = (ulong)&stm32mp13_rng_data},
+ {.compatible = "st,stm32-rng", .data = (ulong)&stm32_rng_data},
{},
};
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.