diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/mtmips/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/mtmips/clk-mt7621.c | 288 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 2 | ||||
-rw-r--r-- | drivers/i2c/stm32f7_i2c.c | 5 | ||||
-rw-r--r-- | drivers/misc/stm32_rcc.c | 1 | ||||
-rw-r--r-- | drivers/mmc/mtk-sd.c | 13 | ||||
-rw-r--r-- | drivers/mmc/stm32_sdmmc2.c | 224 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mt7621_nand.c | 1205 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mt7621_nand.h | 29 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mt7621_nand_spl.c | 237 | ||||
-rw-r--r-- | drivers/net/mtk_eth.c | 45 | ||||
-rw-r--r-- | drivers/phy/Kconfig | 2 | ||||
-rw-r--r-- | drivers/pinctrl/mtmips/Kconfig | 9 | ||||
-rw-r--r-- | drivers/pinctrl/mtmips/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/mtmips/pinctrl-mt7621.c | 306 | ||||
-rw-r--r-- | drivers/pinctrl/mtmips/pinctrl-mtmips-common.c | 4 | ||||
-rw-r--r-- | drivers/pinctrl/mtmips/pinctrl-mtmips-common.h | 12 | ||||
-rw-r--r-- | drivers/rng/stm32mp1_rng.c | 61 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 2 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 2 |
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. |