From e75cc00982f6fa035a476ab90bd5ddd66ff23622 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:22:36 +0800 Subject: clk: mtmips: add clock driver for MediaTek MT7621 SoC This patch adds a clock driver for MediaTek MT7621 SoC. This driver provides clock gate control as well as getting clock frequency for CPU/SYS/XTAL and some peripherals. Reviewed-by: Sean Anderson Signed-off-by: Weijie Gao --- drivers/clk/mtmips/Makefile | 1 + drivers/clk/mtmips/clk-mt7621.c | 288 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 drivers/clk/mtmips/clk-mt7621.c (limited to 'drivers') diff --git a/drivers/clk/mtmips/Makefile b/drivers/clk/mtmips/Makefile index 732e7f25453..ee8b5afe879 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 00000000000..03363b70d74 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, +}; -- cgit v1.3.1 From 4d30111cea5e1f57911dce4af5555fef86edbbad Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:22:49 +0800 Subject: pinctrl: mtmips: add support for MediaTek MT7621 SoC This patch adds pinctrl support for MediaTek MT7621 SoC. The MT7621 SoC supports pinconf, but it is not the same as mt7628. Reviewed-by: Daniel Schwierzeck Signed-off-by: Weijie Gao --- drivers/pinctrl/mtmips/Kconfig | 9 + drivers/pinctrl/mtmips/Makefile | 1 + drivers/pinctrl/mtmips/pinctrl-mt7621.c | 306 +++++++++++++++++++++++++ drivers/pinctrl/mtmips/pinctrl-mtmips-common.c | 4 +- drivers/pinctrl/mtmips/pinctrl-mtmips-common.h | 12 + 5 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 drivers/pinctrl/mtmips/pinctrl-mt7621.c (limited to 'drivers') diff --git a/drivers/pinctrl/mtmips/Kconfig b/drivers/pinctrl/mtmips/Kconfig index 844d5b743fe..456f3ea25d2 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 ba945a89a76..8fece4f5fa9 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 00000000000..3e98a01bad4 --- /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 + */ + +#include +#include +#include +#include +#include + +#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 e361916eb28..869b7810685 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 b51d8f009c0..1f1023ef421 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); -- cgit v1.3.1 From 5ac88d1b0122303a9f6e250d3711f144bc61e72f Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:22:56 +0800 Subject: usb: xhci-mtk: add support for MediaTek MT7621 SoC This patch makes xhci-mtk driver available for MediaTek MT7621 SoC Signed-off-by: Weijie Gao --- drivers/usb/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 31ae9f74e7a..c3b97f48f0f 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. -- cgit v1.3.1 From be3dc920988df180182f263524d536772cc16986 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:01 +0800 Subject: phy: mtk-tphy: add support for MediaTek MT7621 SoC This patch makes mtk-tphy driver available for MediaTek MT7621 SoC Signed-off-by: Weijie Gao --- drivers/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 4a3856d3c2f..c25b42c68f5 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 -- cgit v1.3.1 From d2002fa7e20314f8806c0ac46fa01240cc7cb9aa Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:08 +0800 Subject: spi: add support for MediaTek MT7621 SoC This patch makes mt7621_spi driver available for MediaTek MT7621 SoC Signed-off-by: Weijie Gao --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 766d5636c09..75b794548b2 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 -- cgit v1.3.1 From 40862e49f213b8fa9aef62d53c067d02aacceccd Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:14 +0800 Subject: gpio: add support for MediaTek MT7621 SoC This patch makes mt7621_gpio driver available for MediaTek MT7621 SoC Reviewed-by: Stefan Roese Signed-off-by: Weijie Gao --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3c73a7f6183..aaa152fae73 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. -- cgit v1.3.1 From fe3d57a443b0a51446b85dd452c2e9830c0ac384 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:19 +0800 Subject: watchdog: add support for MediaTek MT7621 SoC This patch makes mt7621_wdt driver available for MediaTek MT7621 SoC Reviewed-by: Stefan Roese Signed-off-by: Weijie Gao --- drivers/watchdog/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 532ada89c1b..6043fe717fb 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. -- cgit v1.3.1 From 163db41d2f0995c8697705c12fdfad872eaac13e Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:26 +0800 Subject: mmc: mediatek: add support for MediaTek MT7621 SoC This patch adds SDXC support for MediaTek MT7621 SoC Reviewed-by: Jaehoon Chung Signed-off-by: Weijie Gao --- drivers/mmc/mtk-sd.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index 97182ffd7f5..e61e8cf4b94 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 }, -- cgit v1.3.1 From 528e483a09db22798aec022fc6ca2f4ab40670d4 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:31 +0800 Subject: net: mediatek: remap iobase address The iobase address from dts node is actually physical address. It's identical to the virtual address in ARM platform. This is ok because this driver was used only by ARM platforms (mt7622/mt7623 ...). But now this driver will be used by mt7621 which is a MIPS SoC. For MIPS platform the physical address space is mapped to KSEG0 and KSEG1 and this makes the virtual address apparently not idential to its physical address. To solve this issue, this patch replaces dev_read_addr with dev_remap_addr to get the remapped iobase address. Reviewed-by: Ramon Fried Signed-off-by: Weijie Gao --- drivers/net/mtk_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c index 666ddeb10d1..caa83b7cece 100644 --- a/drivers/net/mtk_eth.c +++ b/drivers/net/mtk_eth.c @@ -1419,7 +1419,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, -- cgit v1.3.1 From 86062e7a5deb8b32112f6c05403b6a2e5d9f1853 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:37 +0800 Subject: net: mediatek: use regmap api to modify ethsys registers The address returned by regmap_get_range() is not remapped. Directly r/w to this address is ok for ARM platforms since it's idential to the virtual address. But for MIPS platform only virtual address should be used for access. To solve this issue, the regmap api regmap_read/regmap_write should be used since they will remap address before accessing. Reviewed-by: Ramon Fried Signed-off-by: Weijie Gao --- drivers/net/mtk_eth.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c index caa83b7cece..ac1e8abd719 100644 --- a/drivers/net/mtk_eth.c +++ b/drivers/net/mtk_eth.c @@ -159,9 +159,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 +234,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 */ @@ -1427,15 +1433,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); -- cgit v1.3.1 From ad80d48979d8a58f71ea780bdbdedd83f3c77467 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:42 +0800 Subject: net: mediatek: add support for MediaTek MT7621 SoC This patch adds GMAC support for MediaTek MT7621 SoC. MT7621 has the same GMAC/Switch configuration as MT7623. Reviewed-by: Ramon Fried Signed-off-by: Weijie Gao --- drivers/net/mtk_eth.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mtk_eth.c b/drivers/net/mtk_eth.c index ac1e8abd719..4fe7ee0d36a 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 { @@ -675,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); @@ -738,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); @@ -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 }, {} }; -- cgit v1.3.1 From 3ab8beaadca43be24893072619ab14a213788860 Mon Sep 17 00:00:00 2001 From: Weijie Gao Date: Fri, 20 May 2022 11:23:47 +0800 Subject: nand: raw: add support for MediaTek MT7621 SoC This patch adds NAND flash controller driver for MediaTek MT7621 SoC. The NAND flash controller of MT7621 supports only SLC NAND flashes. It supports 4~12 bits correction with maximum 4KB page size. Signed-off-by: Weijie Gao --- drivers/mtd/nand/raw/Kconfig | 17 +- drivers/mtd/nand/raw/Makefile | 2 + drivers/mtd/nand/raw/mt7621_nand.c | 1205 ++++++++++++++++++++++++++++++++ drivers/mtd/nand/raw/mt7621_nand.h | 29 + drivers/mtd/nand/raw/mt7621_nand_spl.c | 237 +++++++ 5 files changed, 1488 insertions(+), 2 deletions(-) create mode 100644 drivers/mtd/nand/raw/mt7621_nand.c create mode 100644 drivers/mtd/nand/raw/mt7621_nand.h create mode 100644 drivers/mtd/nand/raw/mt7621_nand_spl.c (limited to 'drivers') diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 190300fc179..ce67d1abde2 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 e3f6b903f75..f278f31f5cd 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 00000000000..2fd89349392 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 00000000000..af4bc55961a --- /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 + */ + +#ifndef _MT7621_NAND_H_ +#define _MT7621_NAND_H_ + +#include +#include +#include +#include + +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 00000000000..114fc8b7cea --- /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 + */ + +#include +#include +#include +#include +#include +#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; +} -- cgit v1.3.1